/*
 * iSCSI Discovery Database Library
 *
 * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
 * Copyright (C) 2006 Mike Christie
 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
 * maintained by open-iscsi@@googlegroups.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * See the file COPYING included with this distribution for more details.
 */

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include <fcntl.h>
#include <glob.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <inttypes.h>

#include "idbm.h"
#include "idbm_fields.h"
#include "log.h"
#include "iscsi_util.h"
#include "iscsi_settings.h"
#include "transport.h"
#include "iscsi_sysfs.h"
#include "iface.h"
#include "sysdeps.h"
#include "fw_context.h"
#include "iscsi_err.h"

// GLOB_ONLYDIR is not defined under musl
#ifndef GLOB_ONLYDIR
#define GLOB_ONLYDIR	0x100
#endif

#define IDBM_HIDE	0    /* Hide parameter when print. */
#define IDBM_SHOW	1    /* Show parameter when print. */
#define IDBM_MASKED	2    /* Show "stars" instead of real value when print */

static struct idbm *db;

#define ARRAY_LEN(x) ( sizeof(x) / sizeof((x)[0]) )

#define __recinfo_str(_key, _info, _rec, _name, _show, _n, _mod) do { \
	_info[_n].type = TYPE_STR; \
	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
	if (strlen((char*)_rec->_name)) \
		strlcpy((char*)_info[_n].value, (char*)_rec->_name, \
			VALUE_MAXVAL); \
	_info[_n].data = &_rec->_name; \
	_info[_n].data_len = sizeof(_rec->_name); \
	_info[_n].visible = _show; \
	_info[_n].can_modify = _mod; \
	_n++; \
} while(0)

#define __recinfo_int(_key, _info, _rec, _name, _show, _n, _mod) do { \
	_info[_n].type = TYPE_INT; \
	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
	snprintf(_info[_n].value, VALUE_MAXVAL, "%" PRIi32, _rec->_name); \
	_info[_n].data = &_rec->_name; \
	_info[_n].data_len = sizeof(_rec->_name); \
	_info[_n].visible = _show; \
	_info[_n].can_modify = _mod; \
	_n++; \
} while(0)

#define __recinfo_uint8(_key, _info, _rec, _name, _show, _n, _mod) do { \
	_info[_n].type = TYPE_UINT8; \
	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
	snprintf(_info[_n].value, VALUE_MAXVAL, "%" PRIu8, _rec->_name); \
	_info[_n].data = &_rec->_name; \
	_info[_n].data_len = sizeof(_rec->_name); \
	_info[_n].visible = _show; \
	_info[_n].can_modify = _mod; \
	_n++; \
} while (0)

#define __recinfo_uint16(_key, _info, _rec, _name, _show, _n, _mod) do { \
	_info[_n].type = TYPE_UINT16; \
	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
	snprintf(_info[_n].value, VALUE_MAXVAL, "%" PRIu16, _rec->_name); \
	_info[_n].data = &_rec->_name; \
	_info[_n].data_len = sizeof(_rec->_name); \
	_info[_n].visible = _show; \
	_info[_n].can_modify = _mod; \
	_n++; \
} while (0)

#define __recinfo_uint32(_key, _info, _rec, _name, _show, _n, _mod) do { \
	_info[_n].type = TYPE_UINT32; \
	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
	snprintf(_info[_n].value, VALUE_MAXVAL, "%" PRIu32, _rec->_name); \
	_info[_n].data = &_rec->_name; \
	_info[_n].data_len = sizeof(_rec->_name); \
	_info[_n].visible = _show; \
	_info[_n].can_modify = _mod; \
	_n++; \
} while (0)

#define __recinfo_int_o2(_key,_info,_rec,_name,_show,_op0,_op1,_n, _mod) do { \
	_info[_n].type = TYPE_INT_O; \
	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
	if (_rec->_name == 0) strlcpy(_info[_n].value, _op0, VALUE_MAXVAL); \
	if (_rec->_name == 1) strlcpy(_info[_n].value, _op1, VALUE_MAXVAL); \
	_info[_n].data = &_rec->_name; \
	_info[_n].data_len = sizeof(_rec->_name); \
	_info[_n].visible = _show; \
	_info[_n].opts[0] = _op0; \
	_info[_n].opts[1] = _op1; \
	_info[_n].numopts = 2; \
	_info[_n].can_modify = _mod; \
	_n++; \
} while(0)

#define __recinfo_int_o3(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_n,	\
			 _mod) do { \
	__recinfo_int_o2(_key,_info,_rec,_name,_show,_op0,_op1,_n, _mod); \
	_n--; \
	if (_rec->_name == 2) strlcpy(_info[_n].value, _op2, VALUE_MAXVAL);\
	_info[_n].opts[2] = _op2; \
	_info[_n].numopts = 3; \
	_n++; \
} while(0)

#define __recinfo_int_o4(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3,_n, \
			 _mod) do { \
	__recinfo_int_o3(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_n, _mod); \
	_n--; \
	if (_rec->_name == 3) strlcpy(_info[_n].value, _op3, VALUE_MAXVAL); \
	_info[_n].opts[3] = _op3; \
	_info[_n].numopts = 4; \
	_n++; \
} while(0)

#define __recinfo_int_o5(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3, \
			 _op4,_n, _mod) do { \
	__recinfo_int_o4(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3, \
			  _n,_mod); \
	_n--; \
	if (_rec->_name == 4) strlcpy(_info[_n].value, _op4, VALUE_MAXVAL); \
	_info[_n].opts[4] = _op4; \
	_info[_n].numopts = 5; \
	_n++; \
} while(0)

#define __recinfo_int_o6(_key,_info,_rec,_name,_show,_op0,_op1,_op2, \
			 _op3,_op4,_op5,_n,_mod) do { \
	__recinfo_int_o5(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3, \
			 _op4,_n,_mod); \
	_n--; \
	if (_rec->_name == 5) strlcpy(_info[_n].value, _op5, VALUE_MAXVAL); \
	_info[_n].opts[5] = _op5; \
	_info[_n].numopts = 6; \
	_n++; \
} while(0)

#define __recinfo_int_list(_key,_info,_rec,_name,_show,_tbl,_n,_mod) do { \
	_info[_n].type = TYPE_INT_LIST; \
	strlcpy(_info[_n].name, _key, NAME_MAXVAL); \
	_info[_n].value[0] = '\0'; \
	for (unsigned long _i = 0; _i < ARRAY_LEN(_rec->_name); _i++) {	\
		if (_rec->_name[_i] != (unsigned)~0) {			\
			for (unsigned long _j = 0; _j < ARRAY_LEN(_tbl); _j++) {	\
				if (_tbl[_j].value == (int)_rec->_name[_i]) { \
					strcat(_info[_n].value, _tbl[_j].name); \
					strcat(_info[_n].value, ","); \
					break; \
				} \
			} \
		} \
	} \
	/* delete trailing ',' */ \
	if (strrchr(_info[_n].value, ',')) \
		*strrchr(_info[_n].value, ',') = '\0'; \
	_info[_n].data = &_rec->_name; \
	_info[_n].data_len = sizeof(_rec->_name); \
	_info[_n].visible = _show; \
	_info[_n].opts[0] = (void *)&_tbl; \
	_info[_n].numopts = ARRAY_LEN(_tbl); \
	_info[_n].can_modify = _mod; \
	_n++; \
} while (0)

static struct int_list_tbl {
	const char *name;
	int value;
} chap_algs [] = {
	{ "MD5", AUTH_CHAP_ALG_MD5 },
	{ "SHA1", AUTH_CHAP_ALG_SHA1 },
	{ "SHA256", AUTH_CHAP_ALG_SHA256 },
#ifdef SHA3_256_SUPPORTED
	{ "SHA3-256", AUTH_CHAP_ALG_SHA3_256 },
#endif
};

static int idbm_remove_disc_to_node_link(node_rec_t *rec, char *portal);

static void
idbm_recinfo_discovery(discovery_rec_t *r, recinfo_t *ri)
{
	int num = 0;

	__recinfo_int_o2(DISC_STARTUP, ri, r, startup, IDBM_SHOW,
			"manual", "automatic", num, 1);
	__recinfo_int_o5(DISC_TYPE, ri, r, type, IDBM_SHOW,
			"sendtargets", "isns", "offload_send_targets",
			"static", "fw", num, 0);
	switch (r->type) {
	case DISCOVERY_TYPE_SENDTARGETS:
		__recinfo_str(DISC_ST_ADDR, ri, r,
			address, IDBM_SHOW, num, 0);
		__recinfo_int(DISC_ST_PORT, ri, r,
			port, IDBM_SHOW, num, 0);
		__recinfo_int_o2(DISC_ST_AUTH_METHOD, ri, r,
			u.sendtargets.auth.authmethod,
			IDBM_SHOW, "None", "CHAP", num, 1);
		__recinfo_str(DISC_ST_USERNAME, ri, r,
			u.sendtargets.auth.username, IDBM_SHOW, num, 1);
		__recinfo_str(DISC_ST_PASSWORD, ri, r,
			u.sendtargets.auth.password, IDBM_MASKED, num, 1);
		__recinfo_int(DISC_ST_PASSWORD_LEN, ri, r,
			u.sendtargets.auth.password_length, IDBM_HIDE, num, 1);
		__recinfo_str(DISC_ST_USERNAME_IN, ri, r,
			u.sendtargets.auth.username_in, IDBM_SHOW, num, 1);
		__recinfo_str(DISC_ST_PASSWORD_IN, ri, r,
			u.sendtargets.auth.password_in, IDBM_MASKED, num, 1);
		__recinfo_int(DISC_ST_PASSWORD_IN_LEN, ri, r,
			u.sendtargets.auth.password_in_length, IDBM_HIDE,
			num, 1);
		/* reusing SESSION_CHAP_ALGS */
		__recinfo_int_list(SESSION_CHAP_ALGS, ri, r,
				   u.sendtargets.auth.chap_algs,
				   IDBM_SHOW, chap_algs, num, 1);
		__recinfo_int(DISC_ST_LOGIN_TMO, ri, r,
			u.sendtargets.conn_timeo.login_timeout,
			IDBM_SHOW, num, 1);
		__recinfo_int_o2(DISC_ST_USE_DISC_DAEMON, ri, r,
			u.sendtargets.use_discoveryd,
			IDBM_SHOW, "No", "Yes", num, 1);
		__recinfo_int(DISC_ST_DISC_DAEMON_POLL_INVAL, ri, r,
			u.sendtargets.discoveryd_poll_inval,
			IDBM_SHOW, num, 1);
		__recinfo_int(DISC_ST_REOPEN_MAX, ri, r,
			u.sendtargets.reopen_max,
			IDBM_SHOW, num, 1);
		__recinfo_int(DISC_ST_AUTH_TMO, ri, r,
			u.sendtargets.conn_timeo.auth_timeout,
			IDBM_SHOW, num, 1);
		__recinfo_int(DISC_ST_ACTIVE_TMO, ri, r,
			      u.sendtargets.conn_timeo.active_timeout,
			      IDBM_SHOW, num, 1);
		__recinfo_int(DISC_ST_MAX_RECV_DLEN, ri, r,
			      u.sendtargets.conn_conf.MaxRecvDataSegmentLength,
			      IDBM_SHOW, num, 1);
		break;
	case DISCOVERY_TYPE_ISNS:
		__recinfo_str(DISC_ISNS_ADDR, ri, r,
			address, IDBM_SHOW, num, 0);
		__recinfo_int(DISC_ISNS_PORT, ri, r,
			port, IDBM_SHOW, num, 0);
		__recinfo_int_o2(DISC_ISNS_USE_DISC_DAEMON, ri, r,
			u.isns.use_discoveryd,
			IDBM_SHOW, "No", "Yes", num, 1);
		__recinfo_int(DISC_ISNS_DISC_DAEMON_POLL_INVAL, ri, r,
			u.isns.discoveryd_poll_inval,
			IDBM_SHOW, num, 1);
		break;
	default:
		break;
	}
}

void
idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
{
	int num = 0, i;
	int iface_type;

	iface_type = iface_get_iptype(&r->iface);

	__recinfo_str(NODE_NAME, ri, r, name, IDBM_SHOW, num, 0);
	__recinfo_int(NODE_TPGT, ri, r, tpgt, IDBM_SHOW, num, 0);
	__recinfo_int_o3(NODE_STARTUP, ri, r, startup,
			IDBM_SHOW, "manual", "automatic", "onboot", num, 1);
	__recinfo_int_o2(NODE_LEADING_LOGIN, ri, r, leading_login, IDBM_SHOW,
			 "No", "Yes", num, 1);
	/*
	 * Note: because we do not add the iface.iscsi_ifacename to
	 * sysfs iscsiadm does some weird matching. We can change the iface
	 * values if a session is not running, but node record ifaces values
	 * have to be changed and so do the iface record ones.
	 *
	 * Users should nornmally not want to change the iface ones
	 * in the node record directly and instead do it through
	 * the iface mode which will do the right thing (although that
	 * needs some locking).
	 */
	__recinfo_str(IFACE_HWADDR, ri, r, iface.hwaddress, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_IPADDR, ri, r, iface.ipaddress, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_ISCSINAME, ri, r, iface.name, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_NETNAME, ri, r, iface.netdev, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_GATEWAY, ri, r, iface.gateway, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_SUBNET_MASK, ri, r, iface.subnet_mask, IDBM_SHOW, num, 1);
	__recinfo_uint8(IFACE_PREFIX_LEN, ri, r, iface.prefix_len,
			IDBM_SHOW, num, 1);
	/*
	 * svn 780 compat: older versions used node.transport_name and
	 * rec->transport_name
	 */
	__recinfo_str(IFACE_TRANSPORTNAME, ri, r, iface.transport_name,
		      IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_INAME, ri, r, iface.iname, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_STATE, ri, r, iface.state, IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_VLAN_ID, ri, r, iface.vlan_id, IDBM_SHOW, num,
			 1);
	__recinfo_uint8(IFACE_VLAN_PRIORITY, ri, r, iface.vlan_priority,
			IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_VLAN_STATE, ri, r, iface.vlan_state, IDBM_SHOW,
		      num, 1);
	__recinfo_int(IFACE_NUM, ri, r, iface.iface_num, IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_MTU, ri, r, iface.mtu, IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_PORT, ri, r, iface.port, IDBM_SHOW, num, 1);

	if (iface_type == ISCSI_IFACE_TYPE_IPV4) {
		__recinfo_str(IFACE_BOOT_PROTO, ri, r, iface.bootproto,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_ALT_CID, ri, r,
			      iface.dhcp_alt_client_id_state, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_DHCP_ALT_CID_STR, ri, r,
			      iface.dhcp_alt_client_id, IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_DNS, ri, r, iface.dhcp_dns, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_DHCP_LEARN_IQN, ri, r,
			      iface.dhcp_learn_iqn, IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_REQ_VID, ri, r,
			      iface.dhcp_req_vendor_id_state, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_DHCP_VID, ri, r, iface.dhcp_vendor_id_state,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_VID_STR, ri, r, iface.dhcp_vendor_id,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_SLP_DA, ri, r, iface.dhcp_slp_da,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_FRAGMENTATION, ri, r, iface.fragmentation,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_GRAT_ARP, ri, r, iface.gratuitous_arp,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_IN_FORWARD, ri, r,
			      iface.incoming_forwarding, IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_TOS_STATE, ri, r, iface.tos_state,
			      IDBM_SHOW, num, 1);
		__recinfo_uint8(IFACE_TOS, ri, r, iface.tos, IDBM_SHOW, num, 1);
		__recinfo_uint8(IFACE_TTL, ri, r, iface.ttl, IDBM_SHOW, num, 1);
	} else if (iface_type == ISCSI_IFACE_TYPE_IPV6) {
		__recinfo_str(IFACE_IPV6_AUTOCFG, ri, r, iface.ipv6_autocfg,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_LINKLOCAL_AUTOCFG, ri, r,
			      iface.linklocal_autocfg, IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_ROUTER_AUTOCFG, ri, r, iface.router_autocfg,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_LINKLOCAL, ri, r, iface.ipv6_linklocal,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_ROUTER, ri, r, iface.ipv6_router,
			      IDBM_SHOW, num, 1);
		__recinfo_uint8(IFACE_DUP_ADDR_DETECT_CNT, ri, r,
				iface.dup_addr_detect_cnt, IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_FLOW_LABEL, ri, r, iface.flow_label,
				 IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_GRAT_NEIGHBOR_ADV, ri, r,
			      iface.gratuitous_neighbor_adv, IDBM_SHOW, num, 1);
		__recinfo_uint8(IFACE_HOP_LIMIT, ri, r, iface.hop_limit,
				IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_MLD, ri, r, iface.mld, IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_ND_REACHABLE_TMO, ri, r,
				 iface.nd_reachable_tmo, IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_ND_REXMIT_TIME, ri, r,
				 iface.nd_rexmit_time, IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_ND_STALE_TMO, ri, r, iface.nd_stale_tmo,
				 IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_RTR_ADV_LINK_MTU, ri, r,
				 iface.router_adv_link_mtu, IDBM_SHOW, num, 1);
		__recinfo_uint8(IFACE_TRAFFIC_CLASS, ri, r, iface.traffic_class,
				IDBM_SHOW, num, 1);
	}

	__recinfo_str(IFACE_DELAYED_ACK, ri, r, iface.delayed_ack, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_TCP_NAGLE, ri, r, iface.nagle, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_TCP_WSF_STATE, ri, r, iface.tcp_wsf_state,
		      IDBM_SHOW, num, 1);
	__recinfo_uint8(IFACE_TCP_WSF, ri, r, iface.tcp_wsf, IDBM_SHOW, num, 1);
	__recinfo_uint8(IFACE_TCP_TIMER_SCALE, ri, r, iface.tcp_timer_scale,
			IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_TCP_TIMESTAMP, ri, r, iface.tcp_timestamp,
		      IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_REDIRECT, ri, r, iface.redirect, IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_DEF_TMF_TMO, ri, r, iface.def_task_mgmt_tmo,
			 IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_HDRDGST, ri, r, iface.header_digest, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_DATADGST, ri, r, iface.data_digest, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_IMM_DATA, ri, r, iface.immediate_data, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_INITIAL_R2T, ri, r, iface.initial_r2t, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_DSEQ_INORDER, ri, r, iface.data_seq_inorder,
		      IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_DPDU_INORDER, ri, r, iface.data_pdu_inorder,
		      IDBM_SHOW, num, 1);
	__recinfo_uint8(IFACE_ERL, ri, r, iface.erl, IDBM_SHOW, num, 1);
	__recinfo_uint32(IFACE_MAX_RECV_DLEN, ri, r, iface.max_recv_dlength,
			 IDBM_SHOW, num, 1);
	__recinfo_uint32(IFACE_FIRST_BURST, ri, r, iface.first_burst_len,
			 IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_MAX_R2T, ri, r, iface.max_out_r2t, IDBM_SHOW,
			 num, 1);
	__recinfo_uint32(IFACE_MAX_BURST, ri, r, iface.max_burst_len, IDBM_SHOW,
			 num, 1);
	__recinfo_str(IFACE_CHAP_AUTH, ri, r, iface.chap_auth, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_BIDI_CHAP, ri, r, iface.bidi_chap, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_STRICT_LOGIN_COMP, ri, r, iface.strict_login_comp,
		      IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_DISCOVERY_AUTH, ri, r, iface.discovery_auth,
		      IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_DISCOVERY_LOGOUT, ri, r, iface.discovery_logout,
		      IDBM_SHOW, num, 1);


	__recinfo_str(NODE_DISC_ADDR, ri, r, disc_address, IDBM_SHOW,
		      num, 0);
	__recinfo_int(NODE_DISC_PORT, ri, r, disc_port, IDBM_SHOW,
		      num, 0);
	__recinfo_int_o5(NODE_DISC_TYPE, ri, r, disc_type, IDBM_SHOW,
			 "send_targets", "isns", "offload_send_targets",
			 "static", "fw", num, 0);
	__recinfo_int(SESSION_INIT_CMDSN, ri, r,
		      session.initial_cmdsn, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_INIT_LOGIN_RETRY, ri, r,
		      session.initial_login_retry_max, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_XMIT_THREAD_PRIORITY, ri, r,
		      session.xmit_thread_priority, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_CMDS_MAX, ri, r,
		      session.cmds_max, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_QDEPTH, ri, r,
		       session.queue_depth, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_NR_SESSIONS, ri, r,
		       session.nr_sessions, IDBM_SHOW, num, 1);
	__recinfo_int_o2(SESSION_AUTH_METHOD, ri, r, session.auth.authmethod,
			 IDBM_SHOW, "None", "CHAP", num, 1);
	__recinfo_str(SESSION_USERNAME, ri, r,
		      session.auth.username, IDBM_SHOW, num, 1);
	__recinfo_str(SESSION_PASSWORD, ri, r,
		      session.auth.password, IDBM_MASKED, num, 1);
	__recinfo_int(SESSION_PASSWORD_LEN, ri, r,
		      session.auth.password_length, IDBM_HIDE, num, 1);
	__recinfo_str(SESSION_USERNAME_IN, ri, r,
		      session.auth.username_in, IDBM_SHOW, num, 1);
	__recinfo_str(SESSION_PASSWORD_IN, ri, r,
		      session.auth.password_in, IDBM_MASKED, num, 1);
	__recinfo_int(SESSION_PASSWORD_IN_LEN, ri, r,
		      session.auth.password_in_length, IDBM_HIDE, num, 1);
	__recinfo_int_list(SESSION_CHAP_ALGS, ri, r,
			   session.auth.chap_algs, IDBM_SHOW, chap_algs, num, 1);
	__recinfo_int(SESSION_REPLACEMENT_TMO, ri, r,
		      session.timeo.replacement_timeout,
		      IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_ABORT_TMO, ri, r,
		      session.err_timeo.abort_timeout,
		      IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_LU_RESET_TMO, ri, r,
		      session.err_timeo.lu_reset_timeout,
		      IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_TGT_RESET_TMO, ri, r,
		      session.err_timeo.tgt_reset_timeout,
		      IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_HOST_RESET_TMO, ri, r,
		      session.err_timeo.host_reset_timeout,
		      IDBM_SHOW, num, 1);
	__recinfo_int_o2(SESSION_FAST_ABORT, ri, r,
			 session.iscsi.FastAbort, IDBM_SHOW, "No", "Yes",
			 num, 1);
	__recinfo_int_o2(SESSION_INITIAL_R2T, ri, r,
			session.iscsi.InitialR2T, IDBM_SHOW,
			"No", "Yes", num, 1);
	__recinfo_int_o2(SESSION_IMM_DATA, ri, r,
			session.iscsi.ImmediateData,
			IDBM_SHOW, "No", "Yes", num, 1);
	__recinfo_int(SESSION_FIRST_BURST, ri, r,
		      session.iscsi.FirstBurstLength, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_MAX_BURST, ri, r,
		      session.iscsi.MaxBurstLength, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_DEF_TIME2RETAIN, ri, r,
		      session.iscsi.DefaultTime2Retain, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_DEF_TIME2WAIT, ri, r,
		      session.iscsi.DefaultTime2Wait, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_MAX_CONNS, ri, r,
		      session.iscsi.MaxConnections, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_MAX_R2T, ri, r,
		      session.iscsi.MaxOutstandingR2T, IDBM_SHOW, num, 1);
	__recinfo_int(SESSION_ERL, ri, r,
		      session.iscsi.ERL, IDBM_SHOW, num, 1);
	__recinfo_int_o2(SESSION_SCAN, ri, r,
			 session.scan, IDBM_SHOW, "manual", "auto",
			 num, 1);
	__recinfo_int(SESSION_REOPEN_MAX, ri, r,
			session.reopen_max, IDBM_SHOW, num, 1);

	for (i = 0; i < ISCSI_CONN_MAX; i++) {
		char key[NAME_MAXVAL];

		sprintf(key, CONN_ADDR, i);
		__recinfo_str(key, ri, r, conn[i].address, IDBM_SHOW, num, 0);
		sprintf(key, CONN_PORT, i);
		__recinfo_int(key, ri, r, conn[i].port, IDBM_SHOW, num, 0);
		sprintf(key, CONN_STARTUP, i);
		__recinfo_int_o3(key, ri, r, conn[i].startup, IDBM_SHOW,
				 "manual", "automatic", "onboot", num, 1);
		sprintf(key, CONN_WINDOW_SIZE, i);
		__recinfo_int(key, ri, r, conn[i].tcp.window_size,
			      IDBM_SHOW, num, 1);
		sprintf(key, CONN_SERVICE_TYPE, i);
		__recinfo_int(key, ri, r, conn[i].tcp.type_of_service,
				IDBM_SHOW, num, 1);
		sprintf(key, CONN_LOGOUT_TMO, i);
		__recinfo_int(key, ri, r, conn[i].timeo.logout_timeout,
				IDBM_SHOW, num, 1);
		sprintf(key, CONN_LOGIN_TMO, i);
		__recinfo_int(key, ri, r, conn[i].timeo.login_timeout,
				IDBM_SHOW, num, 1);
		sprintf(key, CONN_AUTH_TMO, i);
		__recinfo_int(key, ri, r, conn[i].timeo.auth_timeout,
				IDBM_SHOW, num, 1);

		sprintf(key, CONN_NOP_INT, i);
		__recinfo_int(key, ri, r, conn[i].timeo.noop_out_interval,
				IDBM_SHOW, num, 1);
		sprintf(key, CONN_NOP_TMO, i);
		__recinfo_int(key, ri, r, conn[i].timeo.noop_out_timeout,
				IDBM_SHOW, num, 1);

		sprintf(key, CONN_MAX_XMIT_DLEN, i);
		__recinfo_int(key, ri, r,
			conn[i].iscsi.MaxXmitDataSegmentLength, IDBM_SHOW,
			num, 1);
		sprintf(key, CONN_MAX_RECV_DLEN, i);
		__recinfo_int(key, ri, r,
			conn[i].iscsi.MaxRecvDataSegmentLength, IDBM_SHOW,
			num, 1);
		sprintf(key, CONN_HDR_DIGEST, i);
		__recinfo_int_o4(key, ri, r, conn[i].iscsi.HeaderDigest,
				 IDBM_SHOW, "None", "CRC32C", "CRC32C,None",
				 "None,CRC32C", num, 1);
		sprintf(key, CONN_DATA_DIGEST, i);
		__recinfo_int_o4(key, ri, r, conn[i].iscsi.DataDigest, IDBM_SHOW,
				 "None", "CRC32C", "CRC32C,None",
				 "None,CRC32C", num, 1);
		sprintf(key, CONN_IFMARKER, i);
		__recinfo_int_o2(key, ri, r, conn[i].iscsi.IFMarker, IDBM_SHOW,
				"No", "Yes", num, 1);
		sprintf(key, CONN_OFMARKER, i);
		__recinfo_int_o2(key, ri, r, conn[i].iscsi.OFMarker, IDBM_SHOW,
				"No", "Yes", num, 1);
	}
}

void idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri)
{
	int num = 0;
	int iface_type;

	iface_type = iface_get_iptype(r);

	__recinfo_str(IFACE_ISCSINAME, ri, r, name, IDBM_SHOW, num, 0);
	__recinfo_str(IFACE_NETNAME, ri, r, netdev, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_IPADDR, ri, r, ipaddress, IDBM_SHOW, num, 1);
	__recinfo_uint8(IFACE_PREFIX_LEN, ri, r, prefix_len, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_HWADDR, ri, r, hwaddress, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_TRANSPORTNAME, ri, r, transport_name,
		      IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_INAME, ri, r, iname, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_STATE, ri, r, state, IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_VLAN_ID, ri, r, vlan_id, IDBM_SHOW, num, 1);
	__recinfo_uint8(IFACE_VLAN_PRIORITY, ri, r, vlan_priority,
		      IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_VLAN_STATE, ri, r, vlan_state, IDBM_SHOW, num, 1);
	__recinfo_int(IFACE_NUM, ri, r, iface_num, IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_MTU, ri, r, mtu, IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_PORT, ri, r, port, IDBM_SHOW, num, 1);

	if (iface_type == ISCSI_IFACE_TYPE_IPV4) {
		__recinfo_str(IFACE_BOOT_PROTO, ri, r, bootproto, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_SUBNET_MASK, ri, r, subnet_mask, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_GATEWAY, ri, r, gateway, IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_ALT_CID, ri, r,
			      dhcp_alt_client_id_state, IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_ALT_CID_STR, ri, r, dhcp_alt_client_id,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_DNS, ri, r, dhcp_dns, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_DHCP_LEARN_IQN, ri, r, dhcp_learn_iqn,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_REQ_VID, ri, r,
			      dhcp_req_vendor_id_state, IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_VID, ri, r, dhcp_vendor_id_state,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_VID_STR, ri, r, dhcp_vendor_id,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_DHCP_SLP_DA, ri, r, dhcp_slp_da, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_FRAGMENTATION, ri, r, fragmentation,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_GRAT_ARP, ri, r, gratuitous_arp, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_IN_FORWARD, ri, r, incoming_forwarding,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_TOS_STATE, ri, r, tos_state, IDBM_SHOW,
			      num, 1);
		__recinfo_uint8(IFACE_TOS, ri, r, tos, IDBM_SHOW, num, 1);
		__recinfo_uint8(IFACE_TTL, ri, r, ttl, IDBM_SHOW, num, 1);
	} else if (iface_type == ISCSI_IFACE_TYPE_IPV6) {
		__recinfo_str(IFACE_IPV6_AUTOCFG, ri, r, ipv6_autocfg,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_LINKLOCAL_AUTOCFG, ri, r, linklocal_autocfg,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_ROUTER_AUTOCFG, ri, r, router_autocfg,
			      IDBM_SHOW, num, 1);
		__recinfo_str(IFACE_LINKLOCAL, ri, r, ipv6_linklocal, IDBM_SHOW,
			      num, 1);
		__recinfo_str(IFACE_ROUTER, ri, r, ipv6_router, IDBM_SHOW,
			      num, 1);
		__recinfo_uint8(IFACE_DUP_ADDR_DETECT_CNT, ri, r,
				dup_addr_detect_cnt, IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_FLOW_LABEL, ri, r, flow_label, IDBM_SHOW,
				 num, 1);
		__recinfo_str(IFACE_GRAT_NEIGHBOR_ADV, ri, r,
			      gratuitous_neighbor_adv, IDBM_SHOW, num, 1);
		__recinfo_uint8(IFACE_HOP_LIMIT, ri, r, hop_limit, IDBM_SHOW,
				num, 1);
		__recinfo_str(IFACE_MLD, ri, r, mld, IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_ND_REACHABLE_TMO, ri, r,
				 nd_reachable_tmo, IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_ND_REXMIT_TIME, ri, r, nd_rexmit_time,
				 IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_ND_STALE_TMO, ri, r, nd_stale_tmo,
				 IDBM_SHOW, num, 1);
		__recinfo_uint32(IFACE_RTR_ADV_LINK_MTU, ri, r,
				 router_adv_link_mtu, IDBM_SHOW, num, 1);
		__recinfo_uint8(IFACE_TRAFFIC_CLASS, ri, r, traffic_class,
				IDBM_SHOW, num, 1);
	}

	__recinfo_str(IFACE_DELAYED_ACK, ri, r, delayed_ack, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_TCP_NAGLE, ri, r, nagle, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_TCP_WSF_STATE, ri, r, tcp_wsf_state, IDBM_SHOW,
		      num, 1);
	__recinfo_uint8(IFACE_TCP_WSF, ri, r, tcp_wsf, IDBM_SHOW, num, 1);
	__recinfo_uint8(IFACE_TCP_TIMER_SCALE, ri, r, tcp_timer_scale,
			IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_TCP_TIMESTAMP, ri, r, tcp_timestamp, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_REDIRECT, ri, r, redirect, IDBM_SHOW, num, 1);
	__recinfo_uint16(IFACE_DEF_TMF_TMO, ri, r, def_task_mgmt_tmo, IDBM_SHOW,
			 num, 1);
	__recinfo_str(IFACE_HDRDGST, ri, r, header_digest, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_DATADGST, ri, r, data_digest, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_IMM_DATA, ri, r, immediate_data, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_INITIAL_R2T, ri, r, initial_r2t, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_DSEQ_INORDER, ri, r, data_seq_inorder, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_DPDU_INORDER, ri, r, data_pdu_inorder, IDBM_SHOW,
		      num, 1);
	__recinfo_uint8(IFACE_ERL, ri, r, erl, IDBM_SHOW, num, 1);
	__recinfo_uint32(IFACE_MAX_RECV_DLEN, ri, r, max_recv_dlength,
			 IDBM_SHOW, num, 1);
	__recinfo_uint32(IFACE_FIRST_BURST, ri, r, first_burst_len, IDBM_SHOW,
			 num, 1);
	__recinfo_uint16(IFACE_MAX_R2T, ri, r, max_out_r2t, IDBM_SHOW, num, 1);
	__recinfo_uint32(IFACE_MAX_BURST, ri, r, max_burst_len, IDBM_SHOW,
			 num, 1);
	__recinfo_str(IFACE_CHAP_AUTH, ri, r, chap_auth, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_BIDI_CHAP, ri, r, bidi_chap, IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_STRICT_LOGIN_COMP, ri, r, strict_login_comp,
		      IDBM_SHOW, num, 1);
	__recinfo_str(IFACE_DISCOVERY_AUTH, ri, r, discovery_auth, IDBM_SHOW,
		      num, 1);
	__recinfo_str(IFACE_DISCOVERY_LOGOUT, ri, r, discovery_logout,
		      IDBM_SHOW, num, 1);
}

void idbm_recinfo_host_chap(struct iscsi_chap_rec *r, recinfo_t *ri)
{
	int num = 0;

	__recinfo_uint16(HOST_AUTH_INDEX, ri, r, chap_tbl_idx, IDBM_SHOW,
			 num, 1);

	if (r->chap_type == CHAP_TYPE_OUT) {
		__recinfo_str(HOST_AUTH_USERNAME, ri, r, username, IDBM_SHOW,
			      num, 1);
		__recinfo_str(HOST_AUTH_PASSWORD, ri, r, password, IDBM_MASKED,
			      num, 1);
		__recinfo_int(HOST_AUTH_PASSWORD_LEN, ri, r, password_length,
			      IDBM_HIDE, num, 1);
	} else {
		__recinfo_str(HOST_AUTH_USERNAME_IN, ri, r, username, IDBM_SHOW,
			      num, 1);
		__recinfo_str(HOST_AUTH_PASSWORD_IN, ri, r, password,
			      IDBM_MASKED, num, 1);
		__recinfo_int(HOST_AUTH_PASSWORD_IN_LEN, ri, r, password_length,
			      IDBM_HIDE, num, 1);
	}
}

void idbm_recinfo_flashnode(struct flashnode_rec *r, recinfo_t *ri)
{
	int num = 0;
	int i;

	__recinfo_uint8(FLASHNODE_SESS_AUTO_SND_TGT_DISABLE, ri, r,
			sess.auto_snd_tgt_disable, IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_DISCOVERY_SESS, ri, r,
			sess.discovery_session, IDBM_SHOW, num, 1);
	__recinfo_str(FLASHNODE_SESS_PORTAL_TYPE, ri, r, sess.portal_type,
		      IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_ENTRY_EN, ri, r,
			sess.entry_enable, IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_IMM_DATA_EN, ri, r, sess.immediate_data,
			IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_INITIAL_R2T_EN, ri, r, sess.initial_r2t,
			IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_DATASEQ_INORDER, ri, r,
			sess.data_seq_in_order, IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_PDU_INORDER, ri, r,
			sess.data_pdu_in_order, IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_CHAP_AUTH_EN, ri, r, sess.chap_auth_en,
			IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_DISCOVERY_LOGOUT_EN, ri, r,
			sess.discovery_logout_en, IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_BIDI_CHAP_EN, ri, r, sess.bidi_chap_en,
			IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_DISCOVERY_AUTH_OPTIONAL, ri, r,
			sess.discovery_auth_optional, IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_ERL, ri, r, sess.erl, IDBM_SHOW, num, 1);
	__recinfo_uint32(FLASHNODE_SESS_FIRST_BURST, ri, r,
			 sess.first_burst_len, IDBM_SHOW, num, 1);
	__recinfo_uint16(FLASHNODE_SESS_DEF_TIME2WAIT, ri, r,
			 sess.def_time2wait, IDBM_SHOW, num, 1);
	__recinfo_uint16(FLASHNODE_SESS_DEF_TIME2RETAIN, ri, r,
			 sess.def_time2retain, IDBM_SHOW, num, 1);
	__recinfo_uint16(FLASHNODE_SESS_MAX_R2T, ri, r,
			 sess.max_outstanding_r2t, IDBM_SHOW, num, 1);
	__recinfo_str(FLASHNODE_SESS_ISID, ri, r, sess.isid, IDBM_SHOW, num, 1);
	__recinfo_uint16(FLASHNODE_SESS_TSID, ri, r, sess.tsid, IDBM_SHOW,
			 num, 1);
	__recinfo_uint32(FLASHNODE_SESS_MAX_BURST, ri, r, sess.max_burst_len,
			 IDBM_SHOW, num, 1);
	__recinfo_uint16(FLASHNODE_SESS_DEF_TASKMGMT_TMO, ri, r,
			 sess.def_taskmgmt_tmo, IDBM_SHOW, num, 1);
	__recinfo_str(FLASHNODE_SESS_ALIAS, ri, r, sess.targetalias, IDBM_SHOW,
		      num, 1);
	__recinfo_str(FLASHNODE_SESS_NAME, ri, r, sess.targetname, IDBM_SHOW,
		      num, 1);
	__recinfo_uint16(FLASHNODE_SESS_DISCOVERY_PARENT_IDX, ri, r,
			 sess.discovery_parent_idx, IDBM_SHOW, num, 1);
	__recinfo_str(FLASHNODE_SESS_DISCOVERY_PARENT_TYPE, ri, r,
		      sess.discovery_parent_type, IDBM_SHOW, num, 1);
	__recinfo_uint16(FLASHNODE_SESS_TPGT, ri, r, sess.tpgt, IDBM_SHOW,
			 num, 1);
	__recinfo_uint16(FLASHNODE_SESS_CHAP_OUT_IDX, ri, r, sess.chap_out_idx,
			 IDBM_SHOW, num, 1);
	__recinfo_uint16(FLASHNODE_SESS_CHAP_IN_IDX, ri, r, sess.chap_in_idx,
			 IDBM_SHOW, num, 1);
	__recinfo_str(FLASHNODE_SESS_USERNAME, ri, r, sess.username, IDBM_SHOW,
		      num, 1);
	__recinfo_str(FLASHNODE_SESS_USERNAME_IN, ri, r, sess.username_in,
		      IDBM_SHOW, num, 1);
	__recinfo_str(FLASHNODE_SESS_PASSWORD, ri, r, sess.password, IDBM_SHOW,
		      num, 1);
	__recinfo_str(FLASHNODE_SESS_PASSWORD_IN, ri, r, sess.password_in,
		      IDBM_SHOW, num, 1);
	__recinfo_uint8(FLASHNODE_SESS_IS_BOOT_TGT, ri, r, sess.is_boot_target,
			IDBM_SHOW, num, 1);

	for (i = 0; i < ISCSI_CONN_MAX; i++) {
		char key[NAME_MAXVAL];

		sprintf(key, FLASHNODE_CONN_IS_FW_ASSIGNED_IPV6, i);
		__recinfo_uint8(key, ri, r, conn[i].is_fw_assigned_ipv6,
				IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_HDR_DGST_EN, i);
		__recinfo_uint8(key, ri, r, conn[i].header_digest_en, IDBM_SHOW,
				num, 1);
		sprintf(key, FLASHNODE_CONN_DATA_DGST_EN, i);
		__recinfo_uint8(key, ri, r, conn[i].data_digest_en, IDBM_SHOW,
				num, 1);
		sprintf(key, FLASHNODE_CONN_SNACK_REQ_EN, i);
		__recinfo_uint8(key, ri, r, conn[i].snack_req_en, IDBM_SHOW,
				num, 1);
		sprintf(key, FLASHNODE_CONN_TCP_TIMESTAMP_STAT, i);
		__recinfo_uint8(key, ri, r, conn[i].tcp_timestamp_stat,
				IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_TCP_NAGLE_DISABLE, i);
		__recinfo_uint8(key, ri, r, conn[i].tcp_nagle_disable,
				IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_TCP_WSF_DISABLE, i);
		__recinfo_uint8(key, ri, r, conn[i].tcp_wsf_disable, IDBM_SHOW,
				num, 1);
		sprintf(key, FLASHNODE_CONN_TCP_TIMER_SCALE, i);
		__recinfo_uint8(key, ri, r, conn[i].tcp_timer_scale, IDBM_SHOW,
				num, 1);
		sprintf(key, FLASHNODE_CONN_TCP_TIMESTAMP_EN, i);
		__recinfo_uint8(key, ri, r, conn[i].tcp_timestamp_en,
				IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_IP_FRAG_DISABLE, i);
		__recinfo_uint8(key, ri, r, conn[i].fragment_disable, IDBM_SHOW,
				num, 1);
		sprintf(key, FLASHNODE_CONN_MAX_XMIT_DLENGTH, i);
		__recinfo_uint32(key, ri, r, conn[i].max_xmit_dlength,
				 IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_MAX_RECV_DLENGTH, i);
		__recinfo_uint32(key, ri, r, conn[i].max_recv_dlength,
				 IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_KEEPALIVE_TMO, i);
		__recinfo_uint16(key, ri, r, conn[i].keepalive_tmo, IDBM_SHOW,
				 num, 1);
		sprintf(key, FLASHNODE_CONN_PORT, i);
		__recinfo_uint16(key, ri, r, conn[i].port, IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_IPADDR, i);
		__recinfo_str(key, ri, r, conn[i].ipaddress, IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_REDIRECT_IPADDR, i);
		__recinfo_str(key, ri, r, conn[i].redirect_ipaddr, IDBM_SHOW,
			      num, 1);
		sprintf(key, FLASHNODE_CONN_MAX_SEGMENT_SIZE, i);
		__recinfo_uint32(key, ri, r, conn[i].max_segment_size,
				 IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_LOCAL_PORT, i);
		__recinfo_uint16(key, ri, r, conn[i].local_port, IDBM_SHOW,
				 num, 1);
		sprintf(key, FLASHNODE_CONN_IPV4_TOS, i);
		__recinfo_uint8(key, ri, r, conn[i].ipv4_tos, IDBM_SHOW,
				num, 1);
		sprintf(key, FLASHNODE_CONN_IPV6_TC, i);
		__recinfo_uint8(key, ri, r, conn[i].ipv6_traffic_class,
				IDBM_SHOW, num, 1);
		sprintf(key, FLASHNODE_CONN_IPV6_FLOW_LABEL, i);
		__recinfo_uint16(key, ri, r, conn[i].ipv6_flow_lbl, IDBM_SHOW,
				 num, 1);
		sprintf(key, FLASHNODE_CONN_LINK_LOCAL_IPV6, i);
		__recinfo_str(key, ri, r, conn[i].link_local_ipv6, IDBM_SHOW,
			      num, 1);
		sprintf(key, FLASHNODE_CONN_TCP_XMIT_WSF, i);
		__recinfo_uint32(key, ri, r, conn[i].tcp_xmit_wsf, IDBM_SHOW,
				 num, 1);
		sprintf(key, FLASHNODE_CONN_TCP_RECV_WSF, i);
		__recinfo_uint32(key, ri, r, conn[i].tcp_recv_wsf, IDBM_SHOW,
				 num, 1);
		sprintf(key, FLASHNODE_CONN_STATSN, i);
		__recinfo_uint32(key, ri, r, conn[i].stat_sn, IDBM_SHOW,
				 num, 1);
		sprintf(key, FLASHNODE_CONN_EXP_STATSN, i);
		__recinfo_uint32(key, ri, r, conn[i].exp_stat_sn, IDBM_SHOW,
				 num, 1);
	}
}

recinfo_t *idbm_recinfo_alloc(int max_keys)
{
	recinfo_t *info;

	info = malloc(sizeof(recinfo_t)*max_keys);
	if (!info)
		return NULL;
	memset(info, 0, sizeof(recinfo_t)*max_keys);
	return info;
}

void idbm_print(int type, void *rec, int show, FILE *f)
{
	int i;
	recinfo_t *info;

	info = idbm_recinfo_alloc(MAX_KEYS);
	if (!info)
		return;

	switch (type) {
	case IDBM_PRINT_TYPE_DISCOVERY:
		idbm_recinfo_discovery((discovery_rec_t*)rec, info);
		break;
	case IDBM_PRINT_TYPE_NODE:
		idbm_recinfo_node((node_rec_t*)rec, info);
		break;
	case IDBM_PRINT_TYPE_IFACE:
		idbm_recinfo_iface((struct iface_rec *)rec, info);
		break;
	case IDBM_PRINT_TYPE_HOST_CHAP:
		idbm_recinfo_host_chap((struct iscsi_chap_rec *)rec, info);
		break;
	case IDBM_PRINT_TYPE_FLASHNODE:
		idbm_recinfo_flashnode((struct flashnode_rec *)rec, info);
		break;
	}

	fprintf(f, "%s\n", ISCSI_BEGIN_REC);
	for (i = 0; i < MAX_KEYS; i++) {
		if (!info[i].visible)
			continue;
		if (!show && info[i].visible == IDBM_MASKED) {
			if (*(char*)info[i].data) {
				fprintf(f, "%s = ********\n", info[i].name);
				continue;
			}
			/* fall through */
		}

		if (strlen(info[i].value))
			fprintf(f, "%s = %s\n", info[i].name, info[i].value);
		else if (f == stdout)
			fprintf(f, "%s = <empty>\n", info[i].name);
	}
	fprintf(f, "%s\n", ISCSI_END_REC);

	free(info);
}

static void
idbm_setup_session_defaults(struct iscsi_session_operational_config *conf)
{
	conf->InitialR2T = 0;
	conf->ImmediateData = 1;
	conf->FirstBurstLength = DEF_INI_FIRST_BURST_LEN;
	conf->MaxBurstLength = DEF_INI_MAX_BURST_LEN;
	conf->DefaultTime2Wait = ISCSI_DEF_TIME2WAIT;
	conf->DefaultTime2Retain = 0;
	conf->MaxConnections = 1;
	conf->MaxOutstandingR2T = 1;
	conf->ERL = 0;
	conf->FastAbort = 1;
}

static void idbm_setup_conn_defaults(struct iscsi_conn_operational_config *conf)
{
	conf->MaxXmitDataSegmentLength = 0;
	conf->MaxRecvDataSegmentLength = DEF_INI_MAX_RECV_SEG_LEN;
	conf->HeaderDigest = CONFIG_DIGEST_NEVER;
	conf->DataDigest = CONFIG_DIGEST_NEVER;
	conf->IFMarker = 0;
	conf->OFMarker = 0;
}

static void
idbm_discovery_setup_defaults(discovery_rec_t *rec, discovery_type_e type)
{
	memset(rec, 0, sizeof(discovery_rec_t));

	rec->startup = ISCSI_STARTUP_MANUAL;
	rec->type = type;
	rec->iscsid_req_tmo = -1;
	switch (type) {
	case DISCOVERY_TYPE_SENDTARGETS:
		rec->u.sendtargets.discoveryd_poll_inval = 30;
		rec->u.sendtargets.use_discoveryd = 0;
		rec->u.sendtargets.reopen_max = 5;
		rec->u.sendtargets.auth.authmethod = 0;
		rec->u.sendtargets.auth.password_length = 0;
		rec->u.sendtargets.auth.password_in_length = 0;
		/* TYPE_INT_LIST fields should be initialized to ~0 to indicate unset values */
		memset(rec->u.sendtargets.auth.chap_algs, ~0, sizeof(rec->u.sendtargets.auth.chap_algs));
		rec->u.sendtargets.auth.chap_algs[0] = AUTH_CHAP_ALG_MD5;
		rec->u.sendtargets.conn_timeo.login_timeout=15;
		rec->u.sendtargets.conn_timeo.auth_timeout = 45;
		rec->u.sendtargets.conn_timeo.active_timeout=30;
		idbm_setup_session_defaults(&rec->u.sendtargets.session_conf);
		idbm_setup_conn_defaults(&rec->u.sendtargets.conn_conf);
		/* override def setting */
		rec->u.sendtargets.conn_conf.MaxRecvDataSegmentLength =
						DEF_INI_DISC_MAX_RECV_SEG_LEN;
		break;
	case DISCOVERY_TYPE_ISNS:
		rec->u.isns.use_discoveryd = 0;
		rec->u.isns.discoveryd_poll_inval = -1;
		break;
	default:
		break;
	}
}

int idbm_rec_update_param(recinfo_t *info, char *name, char *value,
			  int line_number)
{
	int i;
	int passwd_done = 0;
	char passwd_len[8];
	char *tmp_value, *token, *tmp;
	bool *found = NULL;
	int *tmp_data;

setup_passwd_len:
	for (i=0; i<MAX_KEYS; i++) {
		if (!strcmp(name, info[i].name)) {
			int j,k;
			struct int_list_tbl *tbl;

			log_debug(7, "updated '%s', '%s' => '%s'", name,
				  info[i].value, value);
			/* parse recinfo by type */
			switch (info[i].type) {
			case TYPE_INT:
				if (!info[i].data)
					continue;

				*(int*)info[i].data =
					strtoul(value, NULL, 10);
				goto updated;
			case TYPE_UINT8:
				if (!info[i].data)
					continue;

				*(uint8_t *)info[i].data =
					strtoul(value, NULL, 10);
				goto updated;
			case TYPE_UINT16:
				if (!info[i].data)
					continue;

				*(uint16_t *)info[i].data =
					strtoul(value, NULL, 10);
				goto updated;
			case TYPE_UINT32:
				if (!info[i].data)
					continue;

				*(uint32_t *)info[i].data =
					strtoul(value, NULL, 10);
				goto updated;
			case TYPE_STR:
				if (!info[i].data)
					continue;

				strlcpy((char*)info[i].data,
					value, info[i].data_len);
				goto updated;
			case TYPE_INT_O:
				for (j=0; j<info[i].numopts; j++) {
					if (!strcmp(value, info[i].opts[j])) {
						if (!info[i].data)
							continue;

						*(int*)info[i].data = j;
						goto updated;
					}
				}
				/* internal error if reached ?? */
				break;
			case TYPE_INT_LIST:
				if (!info[i].data)
					continue;
				tbl = (void *)info[i].opts[0];
				/*
				 * strsep is destructive, make a copy to work with
				 * tmp_value would be modified in strsep() too, so
				 * here make a copy of tmp_value to tmp
				 */
				tmp_value = strdup(value);
				if (!tmp_value)
					return ISCSI_ERR_NOMEM;
				tmp = tmp_value;

				k = 0;
				tmp_data = malloc(info[i].data_len);
				if (!tmp_data)
					goto free_tmp;
				memset(tmp_data, ~0, info[i].data_len);

				found = calloc(info[i].numopts, sizeof(bool));
				if (!found)
					goto free_tmp_data;

next_token:			while ((token = strsep(&tmp_value, ", \n"))) {
					if (!strlen(token))
						continue;
					if ((k * (int)sizeof(int)) >= (info[i].data_len)) {
						log_warning("Too many values set for '%s'"
						            ", continuing without processing them all",
						            info[i].name);
						break;
					}
					for (j = 0; j < info[i].numopts; j++) {
						if (!strcmp(token, tbl[j].name)) {
							if ((found[j])) {
								log_warning("Ignoring repeated "
								            "value '%s' "
								            "for '%s'", token,
								            info[i].name);
								goto next_token;
							}
							((int*)tmp_data)[k++] = tbl[j].value;
							found[j] = true;
							goto next_token;
						}
					}
					log_warning("Ignoring unknown value '%s'"
					            " for '%s'", token, info[i].name);
				}
				memcpy(info[i].data, tmp_data, info[i].data_len);
				free(tmp);
				free(tmp_data);
				tmp_value = NULL;
				tmp_data = NULL;
				token = NULL;
				goto updated;
			}
			if (line_number) {
				log_warning("config file line %d contains "
					    "unknown value format '%s' for "
					    "parameter name '%s'",
					    line_number, value, name);
			} else {
				log_error("unknown value format '%s' for "
					  "parameter name '%s'", value, name);
			}
			break;
		}
	}

	return ISCSI_ERR_INVAL;

free_tmp_data:
	free(tmp_data);

free_tmp:
	free(tmp);
	return ISCSI_ERR_NOMEM;

updated:
	strlcpy((char*)info[i].value, value, VALUE_MAXVAL);
	if (found)
		free(found);

#define check_password_param(_param) \
	if (!passwd_done && !strcmp(#_param, name)) { \
		passwd_done = 1; \
		name = #_param "_length"; \
		snprintf(passwd_len, 8, "%.7" PRIi32, (int)strlen(value) & 0xffff); \
		value = passwd_len; \
		goto setup_passwd_len; \
	}

	check_password_param(node.session.auth.password);
	check_password_param(node.session.auth.password_in);
	check_password_param(discovery.sendtargets.auth.password);
	check_password_param(discovery.sendtargets.auth.password_in);
	check_password_param(host.auth.password);
	check_password_param(host.auth.password_in);

	return 0;
}

/*
 * TODO: we can also check for valid values here.
 */
int idbm_verify_param(recinfo_t *info, char *name)
{
	int i;

	for (i = 0; i < MAX_KEYS; i++) {
		if (strcmp(name, info[i].name))
			continue;

		log_debug(7, "verify %s %d", name, info[i].can_modify);
		if (info[i].can_modify)
			return 0;
		else {
			log_error("Cannot modify %s. It is used to look up "
				  "the record and cannot be changed.", name);
			return ISCSI_ERR_INVAL;
		}
	}

	log_error("Cannot modify %s. Invalid param name.", name);
	return ISCSI_ERR_INVAL;
}

void idbm_recinfo_config(recinfo_t *info, FILE *f)
{
	char name[NAME_MAXVAL];
	char value[VALUE_MAXVAL];
	char *line, *nl, buffer[2048];
	int line_number = 0;
	int c = 0, i;

	fseek(f, 0, SEEK_SET);

	/* process the config file */
	do {
		line = fgets(buffer, sizeof (buffer), f);
		line_number++;
		if (!line)
			continue;

		nl = line + strlen(line) - 1;
		if (*nl != '\n') {
			log_warning("Config file line %d too long.",
			       line_number);
			continue;
		}

		line = strstrip(line);
		/* process any non-empty, non-comment lines */
		if (!*line || *line == '\0' || *line ==  '\n' || *line == '#')
			continue;

		/* parse name */
		i=0; nl = line; *name = 0;
		while (*nl && !isspace(c = *nl) && *nl != '=') {
			*(name+i) = *nl; i++; nl++;
		}
		if (!*nl) {
			log_warning("Config file line %d does not have value",
			       line_number);
			continue;
		}
		*(name+i)=0; nl++;
		/* skip after-name traling spaces */
		while (*nl && isspace(c = *nl)) nl++;
		if (*nl && *nl != '=') {
			log_warning("Config file line %d does not have '=' separator",
			       line_number);
			continue;
		}
		/* skip '=' sepa */
		nl++;
		/* skip after-sepa traling spaces */
		while (*nl && isspace(c = *nl)) nl++;
		if (!*nl) {
			log_warning("Config file line %d does not have value",
			       line_number);
			continue;
		}
		/* parse value */
		i=0; *value = 0;
		while (*nl) {
			*(value+i) = *nl; i++; nl++;
		}
		*(value+i) = 0;

		idbm_rec_update_param(info, name, value, line_number);
	} while (line);
}

/*
 * TODO: remove db's copy of nrec and infos
 */
static void idbm_sync_config(void)
{
	char *config_file;
	FILE *f;

	/* in case of no configuration file found we just
	 * initialize default node and default discovery records
	 * from hard-coded default values */
	idbm_node_setup_defaults(&db->nrec);
	idbm_discovery_setup_defaults(&db->drec_st, DISCOVERY_TYPE_SENDTARGETS);
	idbm_discovery_setup_defaults(&db->drec_isns, DISCOVERY_TYPE_ISNS);

	idbm_recinfo_discovery(&db->drec_st, db->dinfo_st);
	idbm_recinfo_discovery(&db->drec_isns, db->dinfo_isns);
	idbm_recinfo_node(&db->nrec, db->ninfo);

	if (!db->get_config_file) {
		log_debug(1, "Could not get config file. No config file fn");
		return;
	}

	config_file = db->get_config_file();
	if (!config_file) {
		log_debug(1, "Could not get config file for sync config");
		return;
	}

	f = fopen(config_file, "r");
	if (!f) {
		log_debug(1, "cannot open configuration file %s. "
			  "Default location is %s.",
			  config_file, CONFIG_FILE);
		return;
	}
	log_debug(5, "updating defaults from '%s'", config_file);

	idbm_recinfo_config(db->dinfo_st, f);
	idbm_recinfo_config(db->dinfo_isns, f);
	idbm_recinfo_config(db->ninfo, f);
	fclose(f);

	/* update password lengths */
	if (*db->drec_st.u.sendtargets.auth.password)
		db->drec_st.u.sendtargets.auth.password_length =
			strlen((char*)db->drec_st.u.sendtargets.auth.password);
	if (*db->drec_st.u.sendtargets.auth.password_in)
		db->drec_st.u.sendtargets.auth.password_in_length =
		     strlen((char*)db->drec_st.u.sendtargets.auth.password_in);
	if (*db->nrec.session.auth.password)
		db->nrec.session.auth.password_length =
			strlen((char*)db->nrec.session.auth.password);
	if (*db->nrec.session.auth.password_in)
		db->nrec.session.auth.password_in_length =
			strlen((char*)db->nrec.session.auth.password_in);
}

void idbm_node_setup_from_conf(node_rec_t *rec)
{
	memset(rec, 0, sizeof(*rec));
	idbm_node_setup_defaults(rec);
	idbm_sync_config();
	memcpy(rec, &db->nrec, sizeof(*rec));
}

int idbm_print_discovery_info(discovery_rec_t *rec, int show)
{
	idbm_print(IDBM_PRINT_TYPE_DISCOVERY, rec, show, stdout);
	return 1;
}

int idbm_print_node_info(void *data, node_rec_t *rec)
{
	int show = *((int *)data);

	idbm_print(IDBM_PRINT_TYPE_NODE, rec, show, stdout);
	return 0;
}

int idbm_print_host_chap_info(struct iscsi_chap_rec *chap)
{
	/* User only calls this to print chap so always print */
	idbm_print(IDBM_PRINT_TYPE_HOST_CHAP, chap, 1, stdout);
	return 0;
}

int idbm_print_flashnode_info(struct flashnode_rec *fnode)
{
	idbm_print(IDBM_PRINT_TYPE_FLASHNODE, fnode, 1, stdout);
	return 0;
}

int idbm_print_node_flat(__attribute__((unused))void *data, node_rec_t *rec)
{
	if (strchr(rec->conn[0].address, '.'))
		printf("%s:%d,%d %s\n", rec->conn[0].address, rec->conn[0].port,
			rec->tpgt, rec->name);
	else
		printf("[%s]:%d,%d %s\n", rec->conn[0].address,
		       rec->conn[0].port, rec->tpgt, rec->name);
	return 0;
}

int idbm_print_node_tree(struct node_rec *last_rec, struct node_rec *rec,
			 char *prefix)
{
	if (!last_rec || strcmp(last_rec->name, rec->name)) {
		printf("%sTarget: %s\n", prefix, rec->name);
		if (last_rec)
			memset(last_rec, 0, sizeof(node_rec_t));
	}

	if (!last_rec ||
	     ((strcmp(last_rec->conn[0].address, rec->conn[0].address) ||
	     last_rec->conn[0].port != rec->conn[0].port))) {
		if (strchr(rec->conn[0].address, '.'))
			printf("%s\tPortal: %s:%d,%d\n", prefix,
			       rec->conn[0].address,
			       rec->conn[0].port, rec->tpgt);
		else
			printf("%s\tPortal: [%s]:%d,%d\n", prefix,
			       rec->conn[0].address,
			       rec->conn[0].port, rec->tpgt);
	}

	if (last_rec)
		memcpy(last_rec, rec, sizeof(node_rec_t));
	return 0;
}

int idbm_print_node_and_iface_tree(void *data, node_rec_t *rec)
{
	idbm_print_node_tree(data, rec, "");
	printf("\t\tIface Name: %s\n", rec->iface.name);
	return 0;
}

static int
get_params_from_disc_link(char *link, char **target, char **tpgt,
			  char **address, char **port, char **ifaceid)
{
	(*target) = link;
	*address = strchr(*target, ',');
	if (!(*address))
		return ISCSI_ERR_INVAL;
	*(*address)++ = '\0';
	*port = strchr(*address, ',');
	if (!(*port))
		return ISCSI_ERR_INVAL;
	*(*port)++ = '\0';
	*tpgt = strchr(*port, ',');
	if (!(*tpgt))
		return ISCSI_ERR_INVAL;
	*(*tpgt)++ = '\0';
	*ifaceid = strchr(*tpgt, ',');
	if (!(*ifaceid))
		return ISCSI_ERR_INVAL;
	*(*ifaceid)++ = '\0';
	return 0;
}

int idbm_lock(void)
{
	int fd, i, ret;

	if (db->refs > 0) {
		db->refs++;
		return 0;
	}

	if (access(LOCK_DIR, F_OK) != 0) {
		if (mkdir(LOCK_DIR, 0770) != 0) {
			log_error("Could not open %s: %s", LOCK_DIR,
				  strerror(errno));
			return ISCSI_ERR_IDBM;
		}
	}

	fd = open(LOCK_FILE, O_RDWR | O_CREAT, 0666);
	if (fd >= 0)
		close(fd);

	for (i = 0; i < 3000; i++) {
		ret = link(LOCK_FILE, LOCK_WRITE_FILE);
		if (ret == 0)
			break;

		if (errno != EEXIST) {
			log_error("Maybe you are not root?");
			log_error("Could not lock discovery DB: %s: %s",
					LOCK_WRITE_FILE, strerror(errno));
			return ISCSI_ERR_IDBM;
		} else if (i == 0)
			log_debug(2, "Waiting for discovery DB lock");

		usleep(10000);
	}

	db->refs = 1;
	return 0;
}

void idbm_unlock(void)
{
	if (db->refs > 1) {
		db->refs--;
		return;
	}

	db->refs = 0;
	unlink(LOCK_WRITE_FILE);
}

/*
 * Backwards Compat:
 * If the portal is a file then we are doing the old style default
 * session behavior (svn pre 780).
 */
static FILE *idbm_open_rec_r(char *portal, char *config)
{
	struct stat statb;

	log_debug(5, "Looking for config file %s config %s.", portal, config);

	if (stat(portal, &statb)) {
		log_debug(5, "Could not stat %s err %d.", portal, errno);
		return NULL;
	}

	if (S_ISDIR(statb.st_mode)) {
		strlcat(portal, "/", PATH_MAX);
		strlcat(portal, config, PATH_MAX);
	}
	return fopen(portal, "r");
}

/*
 * When the disable_lock param is true, the idbm_lock/idbm_unlock needs
 * to be held by the caller, this will avoid overwriting each other in
 * case of updating(read-modify-write) the recs in parallel.
 */
static int __idbm_rec_read(node_rec_t *out_rec, char *conf, bool disable_lock)
{
	recinfo_t *info;
	FILE *f;
	int rc = 0;

	info = idbm_recinfo_alloc(MAX_KEYS);
	if (!info)
		return ISCSI_ERR_NOMEM;

	if (!disable_lock) {
		rc = idbm_lock();
		if (rc)
			goto free_info;
	}

	f = fopen(conf, "r");
	if (!f) {
		log_debug(5, "Could not open %s err %s", conf,
			  strerror(errno));
		rc = ISCSI_ERR_IDBM;
		goto unlock;
	}

	memset(out_rec, 0, sizeof(*out_rec));
	idbm_node_setup_defaults(out_rec);
	idbm_recinfo_node(out_rec, info);
	idbm_recinfo_config(info, f);
	fclose(f);

unlock:
	if (!disable_lock)
		idbm_unlock();
free_info:
	free(info);
	return rc;
}

/*
 * When the disable_lock param is true, the idbm_lock/idbm_unlock needs
 * to be held by the caller, this will avoid overwriting each other in
 * case of updating(read-modify-write) the recs in parallel.
 */
int
idbm_rec_read(node_rec_t *out_rec, char *targetname, int tpgt,
	      char *ip, int port, struct iface_rec *iface, bool disable_lock)
{
	struct stat statb;
	char *portal;
	int rc;

	portal = calloc(1, PATH_MAX);
	if (!portal)
		return ISCSI_ERR_IDBM;

	/* try old style portal as config */
	snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR,
		 targetname, ip, port);
	log_debug(5, "rec read looking for config file %s.", portal);
	if (!stat(portal, &statb))
		goto read;

	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d/%s", NODE_CONFIG_DIR,
		 targetname, ip, port, tpgt, iface->name);
	log_debug(5, "rec read looking for config file %s.", portal);
	if (!strlen(iface->name)) {
		rc = ISCSI_ERR_INVAL;
		goto free_portal;
	}

	if (stat(portal, &statb)) {
		log_debug(5, "Could not stat %s: %s.", portal, strerror(errno));
		free(portal);
		return ISCSI_ERR_IDBM;
	}

read:
	rc = __idbm_rec_read(out_rec, portal, disable_lock);
free_portal:
	free(portal);
	return rc;
}

static int print_discovered_flat(void *data, node_rec_t *rec)
{
	struct discovery_rec *drec = data;

	if (rec->disc_type != drec->type)
		goto no_match;

	if (drec->type == DISCOVERY_TYPE_SENDTARGETS ||
	    drec->type == DISCOVERY_TYPE_ISNS) {
		if (rec->disc_port != drec->port ||
		    strcmp(rec->disc_address, drec->address))
			goto no_match;
	}

	idbm_print_node_flat(NULL, rec);
	return 0;
no_match:
	return -1;
}

struct discovered_tree_info {
	struct discovery_rec *drec;
	struct node_rec *last_rec;
};

static int print_discovered_tree(void *data, node_rec_t *rec)
{
	struct discovered_tree_info *tree_info = data;
	struct discovery_rec *drec = tree_info->drec;

	if (rec->disc_type != drec->type)
		goto no_match;

	if (strlen(drec->address)) {
		if (rec->disc_port != drec->port ||
		    strcmp(rec->disc_address, drec->address))
			goto no_match;
	}

	idbm_print_node_and_iface_tree(tree_info->last_rec, rec);
	return 0;
no_match:
	return -1;
}

static int idbm_print_discovered(discovery_rec_t *drec, int info_level)
{
	int num_found = 0;

	if (info_level < 1)
		idbm_for_each_rec(&num_found, drec, print_discovered_flat, false);
	else {
		struct discovered_tree_info tree_info;
		struct node_rec last_rec;

		memset(&last_rec, 0, sizeof(struct node_rec));

		tree_info.drec = drec;
		tree_info.last_rec = &last_rec;

		idbm_for_each_rec(&num_found, &tree_info, print_discovered_tree, false);
	}
	return num_found;
}

static int idbm_for_each_drec(int type, char *config_root, void *data,
			      idbm_drec_op_fn *fn)
{
	DIR *entity_dirfd;
	struct dirent *entity_dent;
	int found = 0;
	discovery_rec_t drec;
	char *tmp_port;

	entity_dirfd = opendir(config_root);
	if (!entity_dirfd)
		return found;

	while ((entity_dent = readdir(entity_dirfd))) {
		if (!strcmp(entity_dent->d_name, ".") ||
		    !strcmp(entity_dent->d_name, ".."))
			continue;

		log_debug(5, "found %s", entity_dent->d_name);

		tmp_port = strchr(entity_dent->d_name, ',');
		if (!tmp_port)
			continue;
		/*
		 * pre 872 tools dumped the target portal symlinks in the isns
		 * dir instead of the server. If we find one of those links
		 * (by checking if there is a valid port) we skip it.
		 */
		if (strchr(tmp_port, ':') || strchr(tmp_port, '.'))
			continue;
		*tmp_port++ = '\0';

		memset(&drec, 0, sizeof(drec));
		if (idbm_discovery_read(&drec, type, entity_dent->d_name,
					atoi(tmp_port))) {
			log_error("Could not read discovery record for "
				  "%s:%s.", entity_dent->d_name, tmp_port);
			continue;
		}

		if (!fn(data, &drec))
			found++;
	}
	closedir(entity_dirfd);
	return found;
}

int idbm_for_each_st_drec(void *data, idbm_drec_op_fn *fn)
{
	return idbm_for_each_drec(DISCOVERY_TYPE_SENDTARGETS, ST_CONFIG_DIR,
				  data, fn);
}

int idbm_for_each_isns_drec(void *data, idbm_drec_op_fn *fn)
{
	return idbm_for_each_drec(DISCOVERY_TYPE_ISNS, ISNS_CONFIG_DIR,
				  data, fn);
}

static int __idbm_print_all_by_drec(void *data, struct discovery_rec *drec)
{
	int info_level = *(int *)data;

	if (info_level >= 1) {
		printf("DiscoveryAddress: %s,%d\n",
		       drec->address, drec->port);
		idbm_print_discovered(drec, info_level);
	} else
		printf("%s:%d via %s\n", drec->address, drec->port,
		       drec->type == DISCOVERY_TYPE_ISNS ?
		       "isns" : "sendtargets");
	return 0;
}

static int idbm_print_all_st(int info_level)
{
	int rc;

	rc = idbm_for_each_st_drec(&info_level, __idbm_print_all_by_drec);
	if (rc < 0)
		return 0;
	return rc;
}

static int idbm_print_all_isns(int info_level)
{
	int rc;

	rc = idbm_for_each_isns_drec(&info_level, __idbm_print_all_by_drec);
	if (rc < 0)
		return 0;
	return rc;
}

int idbm_print_all_discovery(int info_level)
{
	discovery_rec_t *drec;
	int found = 0, tmp;

	if (info_level < 1) {
		found = idbm_print_all_st(info_level);
		found += idbm_print_all_isns(info_level);
		return found;
	}

	drec = calloc(1, sizeof(*drec));
	if (!drec)
		return 0;

	tmp = 0;
	printf("SENDTARGETS:\n");
	tmp = idbm_print_all_st(info_level);
	if (!tmp)
		printf("No targets found.\n");
	found += tmp;
	tmp = 0;

	printf("iSNS:\n");
	tmp = idbm_print_all_isns(info_level);
	if (!tmp) {
		/*
		 * pre 872 tools did not store the server ip,port so
		 * we drop down here, to just look for target portals.
		 */
		drec->type = DISCOVERY_TYPE_ISNS;
		tmp = idbm_print_discovered(drec, info_level);
		if (!tmp)
			printf("No targets found.\n");
	}
	found += tmp;
	tmp = 0;

	printf("STATIC:\n");
	drec->type = DISCOVERY_TYPE_STATIC;
	tmp = idbm_print_discovered(drec, info_level);
	if (!tmp)
		printf("No targets found.\n");
	found += tmp;
	tmp = 0;

	printf("FIRMWARE:\n");
	drec->type = DISCOVERY_TYPE_FW;
	tmp = idbm_print_discovered(drec, info_level);
	if (!tmp)
		printf("No targets found.\n");
	found += tmp;

	free(drec);
	return found;
}

/**
 * idbm_for_each_iface - iterate over bound iface recs
 * @found: nr of recs found so far
 * @data: data pointer passed to fn
 * @fn: iterator function ran over each bound iface rec
 * @targetname: rec's target name
 * @tpgt: rec's portal group tag
 * @ip: rec's ip address
 * @port: rec's port
 *
 * This will run fn over all recs with the {targetname,tpgt,ip,port}
 * id. It does not iterate over the ifaces setup in the iface DB directory.
 *
 * fn should return -1 if it skipped the rec, an ISCSI_ERR error code if
 * the operation failed or 0 if fn was run successfully.
 */
static int idbm_for_each_iface(int *found, void *data, idbm_iface_op_fn *fn,
			       char *targetname, int tpgt, char *ip, int port,
			       bool ruw_lock)
{
	DIR *iface_dirfd;
	struct dirent *iface_dent;
	struct stat statb;
	node_rec_t rec;
	int rc = 0;
	char *portal;

	portal = calloc(1, PATH_MAX);
	if (!portal)
		return ISCSI_ERR_NOMEM;

	if (tpgt >= 0)
		goto read_iface;

	/* old style portal as a config */
	snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR, targetname,
		 ip, port);
	if (stat(portal, &statb)) {
		log_error("iface iter could not stat %s.", portal);
		rc = ISCSI_ERR_IDBM;
		goto free_portal;
	}

	rc = __idbm_rec_read(&rec, portal, ruw_lock);
	if (rc)
		goto free_portal;

	rc = fn(data, &rec);
	if (!rc)
		(*found)++;
	else if (rc == -1)
		rc = 0;
	goto free_portal;

read_iface:
	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d", NODE_CONFIG_DIR,
		 targetname, ip, port, tpgt);

	iface_dirfd = opendir(portal);
	if (!iface_dirfd) {
		log_error("iface iter could not read dir %s.", portal);
		rc = ISCSI_ERR_IDBM;
		goto free_portal;
	}

	while ((iface_dent = readdir(iface_dirfd))) {
		int curr_rc;

		if (!strcmp(iface_dent->d_name, ".") ||
		    !strcmp(iface_dent->d_name, ".."))
			continue;

		log_debug(5, "iface iter found %s.", iface_dent->d_name);
		memset(portal, 0, PATH_MAX);
		snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d/%s", NODE_CONFIG_DIR,
			 targetname, ip, port, tpgt, iface_dent->d_name);
		if (__idbm_rec_read(&rec, portal, ruw_lock))
			continue;

		curr_rc = fn(data, &rec);
		/* less than zero means it was not a match */
		if (curr_rc > 0 && !rc)
			rc = curr_rc;
		else if (curr_rc == 0)
			(*found)++;
	}

	closedir(iface_dirfd);
free_portal:
	free(portal);
	return rc;
}

/*
 * backwards compat
 * The portal could be a file or dir with interfaces
 */
int idbm_for_each_portal(int *found, void *data, idbm_portal_op_fn *fn,
			 char *targetname, bool ruw_lock)
{
	DIR *portal_dirfd;
	struct dirent *portal_dent;
	int rc = 0;
	char *portal;

	portal = calloc(1, PATH_MAX);
	if (!portal)
		return ISCSI_ERR_NOMEM;

	snprintf(portal, PATH_MAX, "%s/%s", NODE_CONFIG_DIR, targetname);
	portal_dirfd = opendir(portal);
	if (!portal_dirfd) {
		rc = ISCSI_ERR_IDBM;
		goto done;
	}

	while ((portal_dent = readdir(portal_dirfd))) {
		char *tmp_port, *tmp_tpgt;
		int curr_rc;

		if (!strcmp(portal_dent->d_name, ".") ||
		    !strcmp(portal_dent->d_name, ".."))
			continue;

		log_debug(5, "found %s", portal_dent->d_name);
		tmp_port = strchr(portal_dent->d_name, ',');
		if (!tmp_port)
			continue;
		*tmp_port++ = '\0';
		tmp_tpgt = strchr(tmp_port, ',');
		if (tmp_tpgt)
			*tmp_tpgt++ = '\0';

		curr_rc = fn(found, data, targetname,
			tmp_tpgt ? atoi(tmp_tpgt) : -1,
			portal_dent->d_name, atoi(tmp_port),
			ruw_lock);
		/* less than zero means it was not a match */
		if (curr_rc > 0 && !rc)
			rc = curr_rc;
	}
	closedir(portal_dirfd);
done:
	free(portal);
	return rc;
}

int idbm_for_each_node(int *found, void *data, idbm_node_op_fn *fn, bool ruw_lock)
{
	DIR *node_dirfd;
	struct dirent *node_dent;
	int rc = 0;

	*found = 0;

	node_dirfd = opendir(NODE_CONFIG_DIR);
	if (!node_dirfd)
		/* on start up node dir may not be created */
		return 0;

	while ((node_dent = readdir(node_dirfd))) {
		int curr_rc;

		if (!strcmp(node_dent->d_name, ".") ||
		    !strcmp(node_dent->d_name, ".."))
			continue;

		log_debug(5, "searching %s", node_dent->d_name);
		curr_rc = fn(found, data, node_dent->d_name, ruw_lock);
		/* less than zero means it was not a match */
		if (curr_rc > 0 && !rc)
			rc = curr_rc;
	}

	closedir(node_dirfd);
	return rc;
}

static int iface_fn(void *data, node_rec_t *rec)
{
	struct rec_op_data *op_data = data;

	return op_data->fn(op_data->data, rec);
}

static int portal_fn(int *found, void *data, char *targetname,
		     int tpgt, char *ip, int port, bool ruw_lock)
{
	int rc;

	if (ruw_lock) {
		rc = idbm_lock();
		if (rc)
			return rc;
	}

	rc = idbm_for_each_iface(found, data, iface_fn, targetname,
			         tpgt, ip, port, ruw_lock);
	if (ruw_lock)
		idbm_unlock();

	return rc;
}

static int node_fn(int *found, void *data, char *targetname, bool ruw_lock)
{
	return idbm_for_each_portal(found, data, portal_fn, targetname, ruw_lock);
}

int idbm_for_each_rec(int *found, void *data, idbm_iface_op_fn *fn, bool ruw_lock)
{
	struct rec_op_data op_data;

	memset(&op_data, 0, sizeof(struct rec_op_data));
	op_data.data = data;
	op_data.fn = fn;

	return idbm_for_each_node(found, &op_data, node_fn, ruw_lock);
}

static struct {
	char *config_root;
	char *config_name;
} disc_type_to_config_vals[] = {
	{ ST_CONFIG_DIR, ST_CONFIG_NAME },
	{ ISNS_CONFIG_DIR, ISNS_CONFIG_NAME },
};

int
idbm_discovery_read(discovery_rec_t *out_rec, int drec_type,
		    char *addr, int port)
{
	recinfo_t *info;
	char *portal;
	int rc = 0;
	FILE *f;

	if (drec_type > 1)
		return ISCSI_ERR_INVAL;

	memset(out_rec, 0, sizeof(discovery_rec_t));
	out_rec->iscsid_req_tmo = -1;

	info = idbm_recinfo_alloc(MAX_KEYS);
	if (!info)
		return ISCSI_ERR_NOMEM;

	portal = malloc(PATH_MAX);
	if (!portal) {
		rc = ISCSI_ERR_NOMEM;
		goto free_info;
	}

	snprintf(portal, PATH_MAX, "%s/%s,%d",
		 disc_type_to_config_vals[drec_type].config_root,
		 addr, port);
	log_debug(5, "Looking for config file %s", portal);

	rc = idbm_lock();
	if (rc)
		goto free_info;

	f = idbm_open_rec_r(portal,
			    disc_type_to_config_vals[drec_type].config_name);
	if (!f) {
		log_debug(1, "Could not open %s: %s", portal,
			  strerror(errno));
		rc = ISCSI_ERR_IDBM;
		goto unlock;
	}

	idbm_discovery_setup_defaults(out_rec, drec_type);
	idbm_recinfo_discovery(out_rec, info);
	idbm_recinfo_config(info, f);
	fclose(f);

unlock:
	idbm_unlock();
free_info:
	free(portal);
	free(info);
	return rc;
}

/*
 * Backwards Compat:
 * If the portal is a file then we are doing the old style default
 * session behavior (svn pre 780).
 */
static FILE *idbm_open_rec_w(char *portal, char *config)
{
	struct stat statb;
	FILE *f;
	int err;

	log_debug(5, "Looking for config file %s", portal);

	err = stat(portal, &statb);
	if (err)
		goto mkdir_portal;

	if (!S_ISDIR(statb.st_mode)) {
		/*
		 * Old style portal as a file. Let's update it.
		 */
		if (unlink(portal)) {
			log_error("Could not convert %s to %s/%s. "
				 "err %d", portal, portal,
				  config, errno);
			return NULL;
		}

mkdir_portal:
		if (mkdir(portal, 0770) != 0) {
			log_error("Could not make dir %s err %d",
				  portal, errno);
			return NULL;
		}
	}

	strlcat(portal, "/", PATH_MAX);
	strlcat(portal, config, PATH_MAX);
	f = fopen(portal, "w");
	if (!f)
		log_error("Could not open %s err %d", portal, errno);
	return f;
}

static int idbm_rec_write_new(node_rec_t *rec)
{
	struct stat statb;
	FILE *f;
	char *portal;
	int rc = 0;

	portal = malloc(PATH_MAX);
	if (!portal) {
		log_error("Could not alloc portal");
		return ISCSI_ERR_NOMEM;
	}

	snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port);

	rc = stat(portal, &statb);
	if (rc) {
		rc = 0;
		/*
		 * older iscsiadm versions had you create the config then set
		 * set the tgpt. In new versions you must pass all the info in
		 * from the start
		 */
		goto mkdir_portal;
	}

	if (!S_ISDIR(statb.st_mode)) {
		/*
		 * Old style portal as a file, but with tpgt. Let's update it.
		 */
		if (unlink(portal)) {
			log_error("Could not convert %s: %s", portal,
				  strerror(errno));
			rc = ISCSI_ERR_IDBM;
			goto free_portal;
		}
	} else {
		rc = ISCSI_ERR_INVAL;
		goto free_portal;
	}

mkdir_portal:
	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port, rec->tpgt);
	if (stat(portal, &statb)) {
		if (mkdir(portal, 0770) != 0) {
			log_error("Could not make dir %s: %s",
				  portal, strerror(errno));
			rc = ISCSI_ERR_IDBM;
			goto free_portal;
		}
	}

	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d/%s", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port, rec->tpgt,
		 rec->iface.name);

	f = fopen(portal, "w");
	if (!f) {
		log_error("Could not open %s: %s", portal, strerror(errno));
		rc = ISCSI_ERR_IDBM;
		goto free_portal;
	}

	idbm_print(IDBM_PRINT_TYPE_NODE, rec, 1, f);
	fclose(f);
free_portal:
	free(portal);
	return rc;
}

static int idbm_rec_write_old(node_rec_t *rec)
{
	FILE *f;
	char *portal;
	int rc = 0;
	glob_t globbuf;
	size_t i;
	int tpgt = PORTAL_GROUP_TAG_UNKNOWN;

	portal = malloc(PATH_MAX);
	if (!portal) {
		log_error("Could not alloc portal");
		return ISCSI_ERR_NOMEM;
	}

	/* check for newer portal dir with tpgt */
	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,*", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port);
	rc = glob(portal, GLOB_ONLYDIR, NULL, &globbuf);
	if (!rc) {
		if (globbuf.gl_pathc > 1)
			log_warning("multiple tpg records for portal "
				    "%s/%s:%d found", rec->name,
				    rec->conn[0].address, rec->conn[0].port);
		/* set pattern for sscanf matching of tpgt */
		snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%%u", NODE_CONFIG_DIR,
			 rec->name, rec->conn[0].address, rec->conn[0].port);
		for (i = 0; i < globbuf.gl_pathc; i++) {
			rc = sscanf(globbuf.gl_pathv[i], portal, &tpgt);
			if (rc == 1)
				break;
		}
		if (tpgt == PORTAL_GROUP_TAG_UNKNOWN)
			log_warning("glob match on existing records, "
				    "but no valid tpgt found");
	}
	globfree(&globbuf);
	rc = 0;

	/* if a tpgt was selected from an old record, write entry in new format */
	if (tpgt != PORTAL_GROUP_TAG_UNKNOWN) {
		log_warning("using tpgt %u from existing record", tpgt);
		rec->tpgt = tpgt;
		rc = idbm_remove_disc_to_node_link(rec, portal);
		free(portal);
		return idbm_rec_write_new(rec);
	}

	snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port);

	f = fopen(portal, "w");
	if (!f) {
		log_error("Could not open %s: %sd", portal, strerror(errno));
		rc = ISCSI_ERR_IDBM;
		goto free_portal;
	}
	idbm_print(IDBM_PRINT_TYPE_NODE, rec, 1, f);
	fclose(f);
free_portal:
	free(portal);
	return rc;
}

/*
 * When the disable_lock param is true, the idbm_lock/idbm_unlock needs
 * to be held by the caller, this will avoid overwriting each other in
 * case of updating(read-modify-write) the recs in parallel.
 */
static int idbm_rec_write(node_rec_t *rec, bool disable_lock)
{
	char *portal;
	int rc = 0;

	portal = malloc(PATH_MAX);
	if (!portal) {
		log_error("Could not alloc portal");
		return ISCSI_ERR_NOMEM;
	}

	snprintf(portal, PATH_MAX, "%s", NODE_CONFIG_DIR);
	if (access(portal, F_OK) != 0) {
		if (mkdir(portal, 0770) != 0) {
			log_error("Could not make %s: %s", portal,
				  strerror(errno));
			rc = ISCSI_ERR_IDBM;
			goto free_portal;
		}
	}

	snprintf(portal, PATH_MAX, "%s/%s", NODE_CONFIG_DIR, rec->name);
	if (access(portal, F_OK) != 0) {
		if (mkdir(portal, 0770) != 0) {
			log_error("Could not make %s: %s", portal,
				  strerror(errno));
			rc = ISCSI_ERR_IDBM;
			goto free_portal;
		}
	}

	if (!disable_lock) {
		rc = idbm_lock();
		if (rc)
			goto free_portal;
	}

	if (rec->tpgt == PORTAL_GROUP_TAG_UNKNOWN)
		/* old style portal as config */
		rc = idbm_rec_write_old(rec);
	else
		rc = idbm_rec_write_new(rec);

	if (!disable_lock)
		idbm_unlock();
free_portal:
	free(portal);
	return rc;
}

static int
idbm_discovery_write(discovery_rec_t *rec)
{
	FILE *f;
	char *portal;
	int rc = 0;

	if (rec->type > 1)
		return ISCSI_ERR_INVAL;

	portal = malloc(PATH_MAX);
	if (!portal) {
		log_error("Could not alloc portal");
		return ISCSI_ERR_NOMEM;
	}

	rc = idbm_lock();
	if (rc)
		goto free_portal;

	snprintf(portal, PATH_MAX, "%s",
		 disc_type_to_config_vals[rec->type].config_root);
	if (access(portal, F_OK) != 0) {
		if (mkdir(portal, 0770) != 0) {
			log_error("Could not make %s: %s", portal,
				  strerror(errno));
			rc = ISCSI_ERR_IDBM;
			goto unlock;
		}
	}

	snprintf(portal, PATH_MAX, "%s/%s,%d",
		 disc_type_to_config_vals[rec->type].config_root,
		 rec->address, rec->port);

	f = idbm_open_rec_w(portal,
			    disc_type_to_config_vals[rec->type].config_name);
	if (!f) {
		log_error("Could not open %s: %s", portal, strerror(errno));
		rc = ISCSI_ERR_IDBM;
		goto unlock;
	}

	idbm_print(IDBM_PRINT_TYPE_DISCOVERY, rec, 1, f);
	fclose(f);
unlock:
	idbm_unlock();
free_portal:
	free(portal);
	return rc;
}

int idbm_add_discovery(discovery_rec_t *newrec)
{
	discovery_rec_t rec;

	if (!idbm_discovery_read(&rec, newrec->type, newrec->address,
				newrec->port)) {
		log_debug(7, "disc rec already exists");
		/* fall through */
	} else
		log_debug(7, "adding new DB record");

	return idbm_discovery_write(newrec);
}

static int setup_disc_to_node_link(char *disc_portal, node_rec_t *rec)
{
	struct stat statb;
	int rc = 0;

	switch (rec->disc_type) {
	case DISCOVERY_TYPE_SENDTARGETS:
		/* st dir setup when we create its discovery node */
		snprintf(disc_portal, PATH_MAX, "%s/%s,%d/%s,%s,%d,%d,%s",
			 ST_CONFIG_DIR,
			 rec->disc_address, rec->disc_port, rec->name,
			 rec->conn[0].address, rec->conn[0].port, rec->tpgt,
			 rec->iface.name);
		break;
	case DISCOVERY_TYPE_FW:
		if (access(FW_CONFIG_DIR, F_OK) != 0) {
			if (mkdir(FW_CONFIG_DIR, 0770) != 0) {
				log_error("Could not make %s: %s",
					  FW_CONFIG_DIR, strerror(errno));
				rc = ISCSI_ERR_IDBM;
			}
		}

		snprintf(disc_portal, PATH_MAX, "%s/%s,%s,%d,%d,%s",
			 FW_CONFIG_DIR, rec->name,
			 rec->conn[0].address, rec->conn[0].port, rec->tpgt,
			 rec->iface.name);
		break;
	case DISCOVERY_TYPE_STATIC:
		if (access(STATIC_CONFIG_DIR, F_OK) != 0) {
			if (mkdir(STATIC_CONFIG_DIR, 0770) != 0) {
				log_error("Could not make %s; %s",
					  STATIC_CONFIG_DIR, strerror(errno));
				rc = ISCSI_ERR_IDBM;
			}
		}

		snprintf(disc_portal, PATH_MAX, "%s/%s,%s,%d,%d,%s",
			 STATIC_CONFIG_DIR, rec->name,
			 rec->conn[0].address, rec->conn[0].port, rec->tpgt,
			 rec->iface.name);
		break;
	case DISCOVERY_TYPE_ISNS:
		if (access(ISNS_CONFIG_DIR, F_OK) != 0) {
			if (mkdir(ISNS_CONFIG_DIR, 0770) != 0) {
				log_error("Could not make %s: %s",
					  ISNS_CONFIG_DIR, strerror(errno));
				rc = ISCSI_ERR_IDBM;
			}
		}

		/*
		 * Older tools lumped all portals together in the
		 * isns config dir. In 2.0-872, the isns dir added
		 * a isns server (ddress and port) dir like sendtargets.
		 *
		 * If we found a older style link we return that so it
		 * can be removed. If this function is called for
		 * addition of a rec then the older link should have been
		 * removed and we break down below.
		 */
		snprintf(disc_portal, PATH_MAX, "%s/%s,%s,%d,%d,%s",
			 ISNS_CONFIG_DIR,
			 rec->name, rec->conn[0].address,
			 rec->conn[0].port, rec->tpgt, rec->iface.name);
		if (!stat(disc_portal, &statb)) {
			log_debug(7, "using old style isns dir %s.",
				  disc_portal);
			break;
		}

		snprintf(disc_portal, PATH_MAX, "%s/%s,%d",
			 ISNS_CONFIG_DIR, rec->disc_address, rec->disc_port);
		if (!stat(disc_portal, &statb) && S_ISDIR(statb.st_mode)) {
			/*
			 * if there is a dir for this isns server then
			 * assume we are using the new style links
			 */
			snprintf(disc_portal, PATH_MAX,
				 "%s/%s,%d/%s,%s,%d,%d,%s",
				 ISNS_CONFIG_DIR, rec->disc_address,
				 rec->disc_port, rec->name,
				 rec->conn[0].address, rec->conn[0].port,
				 rec->tpgt, rec->iface.name);
			break;
		}

		/* adding a older link */
		snprintf(disc_portal, PATH_MAX, "%s/%s,%s,%d,%d,%s",
			 ISNS_CONFIG_DIR, rec->name, rec->conn[0].address,
			 rec->conn[0].port, rec->tpgt, rec->iface.name);
		break;
	default:
		rc = ISCSI_ERR_INVAL;
	}

	return rc;
}

int idbm_add_node(node_rec_t *newrec, discovery_rec_t *drec, int overwrite)
{
	node_rec_t rec;
	char *node_portal = NULL, *disc_portal;
	int rc;

	rc = idbm_lock();
	if (rc)
		return rc;

	if (!idbm_rec_read(&rec, newrec->name, newrec->tpgt,
			   newrec->conn[0].address, newrec->conn[0].port,
			   &newrec->iface, true)) {
		if (!overwrite) {
			rc = 0;
			goto unlock;
		}

		rc = idbm_delete_node(&rec);
		if (rc)
			goto unlock;

		if (drec->type == DISCOVERY_TYPE_FW) {
			log_debug(8, "setting firmware node 'startup' to 'onboot'");
			newrec->startup = ISCSI_STARTUP_ONBOOT;
			newrec->conn[0].startup = ISCSI_STARTUP_ONBOOT;
		}
		log_debug(7, "overwriting existing record");
	} else
		log_debug(7, "adding new DB record");

	if (drec) {
		newrec->disc_type = drec->type;
		newrec->disc_port = drec->port;
		strcpy(newrec->disc_address, drec->address);
	}

	rc = idbm_rec_write(newrec, true);
	/*
	 * if a old app passed in a bogus tpgt then we do not create links
	 * since it will set a different tpgt in another iscsiadm call
	 */
	if (rc || !drec || newrec->tpgt == PORTAL_GROUP_TAG_UNKNOWN)
		goto unlock;

	node_portal = calloc(2, PATH_MAX);
	if (!node_portal) {
		rc = ISCSI_ERR_NOMEM;
		goto unlock;
	}

	disc_portal = node_portal + PATH_MAX;
	snprintf(node_portal, PATH_MAX, "%s/%s/%s,%d,%d", NODE_CONFIG_DIR,
		 newrec->name, newrec->conn[0].address, newrec->conn[0].port,
		 newrec->tpgt);
	rc = setup_disc_to_node_link(disc_portal, newrec);
	if (rc)
		goto unlock;

	log_debug(7, "node addition making link from %s to %s", node_portal,
		 disc_portal);

	if (symlink(node_portal, disc_portal)) {
		if (errno == EEXIST)
			log_debug(7, "link from %s to %s exists", node_portal,
				  disc_portal);
		else {
			rc = ISCSI_ERR_IDBM;
			log_error("Could not make link from disc source %s to "
				 "node %s: %s", disc_portal, node_portal,
				 strerror(errno));
		}
	}
unlock:
	idbm_unlock();
	free(node_portal);
	return rc;
}

static int idbm_bind_iface_to_nodes(idbm_disc_nodes_fn *disc_node_fn,
				    void *data, struct iface_rec *iface,
				    struct list_head *bound_recs)
{
	struct node_rec *rec, *tmp;
	struct list_head new_recs;
	int rc;

	INIT_LIST_HEAD(&new_recs);
	rc = disc_node_fn(data, iface, &new_recs);
	if (rc)
		return rc;

	list_for_each_entry_safe(rec, tmp, &new_recs, list) {
		list_del_init(&rec->list);
		list_add_tail(&rec->list, bound_recs);
		iface_copy(&rec->iface, iface);
	}
	return 0;
}

static int
discovery_error_fatal(int err)
{
	switch (err) {
	/* No error */
	case ISCSI_SUCCESS:
	/* Transport errors or timeouts are not fatal */
	case ISCSI_ERR_TRANS:
	case ISCSI_ERR_TRANS_TIMEOUT:
		return 0;
	}
	return 1;
}

int idbm_bind_ifaces_to_nodes(idbm_disc_nodes_fn *disc_node_fn,
			      void *data, struct list_head *ifaces,
			      struct list_head *bound_recs)
{
	struct list_head def_ifaces;
	struct node_rec *rec, *tmp_rec;
	struct iface_rec *iface, *tmp_iface;
	struct iscsi_transport *t;
	int rc = 0, found = 0;

	INIT_LIST_HEAD(&def_ifaces);

	if (!ifaces || list_empty(ifaces)) {
		iface_link_ifaces(&def_ifaces);

		list_for_each_entry_safe(iface, tmp_iface, &def_ifaces, list) {
			list_del(&iface->list);
			t = iscsi_sysfs_get_transport_by_name(iface->transport_name);
			/*
			 * only auto bind to software iscsi if it is
			 * not the default iface (that is handled below)
			 */
			if (!t || strcmp(t->name, DEFAULT_TRANSPORT) ||
			    !strcmp(iface->name, DEFAULT_IFACENAME)) {
				free(iface);
				continue;
			}

			rc = idbm_bind_iface_to_nodes(disc_node_fn, data, iface,
						      bound_recs);
			free(iface);
			if (discovery_error_fatal(rc))
				goto fail;
			found = 1;
		}

		/* create default iface with old/default behavior */
		if (!found) {
			struct iface_rec def_iface;

			memset(&def_iface, 0, sizeof(struct iface_rec));
			iface_setup_defaults(&def_iface);
			return idbm_bind_iface_to_nodes(disc_node_fn, data,
							&def_iface, bound_recs);
		}
	} else {
		list_for_each_entry(iface, ifaces, list) {
			if (strcmp(iface->name, DEFAULT_IFACENAME) &&
			    !iface_is_valid(iface)) {
				log_error("iface %s is not valid. Will not "
					  "bind node to it. Iface settings "
					  iface_fmt, iface->name,
					  iface_str(iface));
				continue;
			}

			rc = idbm_bind_iface_to_nodes(disc_node_fn, data, iface,
						      bound_recs);
			if (discovery_error_fatal(rc))
				goto fail;
		}
	}
	return 0;

fail:
	list_for_each_entry_safe(iface, tmp_iface, &def_ifaces, list) {
		list_del(&iface->list);
		free(iface);
	}

	list_for_each_entry_safe(rec, tmp_rec, bound_recs, list) {
		list_del(&rec->list);
		free(rec);
	}
	return rc;
}

static void idbm_rm_disc_node_links(char *disc_dir)
{
	char *target = NULL, *tpgt = NULL, *port = NULL;
	char *address = NULL, *iface_id = NULL;
	DIR *disc_dirfd;
	struct dirent *disc_dent;
	node_rec_t *rec;

	rec = calloc(1, sizeof(*rec));
	if (!rec)
		return;

	disc_dirfd = opendir(disc_dir);
	if (!disc_dirfd)
		goto free_rec;

	/* rm links to nodes */
	while ((disc_dent = readdir(disc_dirfd))) {
		if (!strcmp(disc_dent->d_name, ".") ||
		    !strcmp(disc_dent->d_name, ".."))
			continue;


		if (get_params_from_disc_link(disc_dent->d_name, &target, &tpgt,
					      &address, &port, &iface_id)) {
			log_error("Improperly formed disc to node link");
			continue;
		}

		log_debug(5, "disc removal removing link %s %s %s %s",
			  target, address, port, iface_id);

		memset(rec, 0, sizeof(*rec));
		strlcpy(rec->name, target, TARGET_NAME_MAXLEN);
		rec->tpgt = atoi(tpgt);
		rec->conn[0].port = atoi(port);
		strlcpy(rec->conn[0].address, address, NI_MAXHOST);
		strlcpy(rec->iface.name, iface_id, ISCSI_MAX_IFACE_LEN);

		if (idbm_delete_node(rec))
			log_error("Could not delete node %s/%s/%s,%s/%s",
				  NODE_CONFIG_DIR, target, address, port,
				  iface_id);
 	}

	closedir(disc_dirfd);
free_rec:
	free(rec);
}

int idbm_delete_discovery(discovery_rec_t *drec)
{
	char *portal;
	struct stat statb;
	int rc = 0;

	portal = calloc(1, PATH_MAX);
	if (!portal)
		return ISCSI_ERR_NOMEM;

	snprintf(portal, PATH_MAX, "%s/%s,%d",
		 disc_type_to_config_vals[drec->type].config_root,
		 drec->address, drec->port);
	log_debug(5, "Removing config file %s", portal);

	if (stat(portal, &statb)) {
		log_debug(5, "Could not stat %s to delete disc err %d",
			  portal, errno);
		goto free_portal;
	}

	if (S_ISDIR(statb.st_mode)) {
		strlcat(portal, "/", PATH_MAX);
		strlcat(portal,
			disc_type_to_config_vals[drec->type].config_name,
			PATH_MAX);
	}

	if (unlink(portal))
		log_debug(5, "Could not remove %s err %d", portal, errno);

	memset(portal, 0, PATH_MAX);
	snprintf(portal, PATH_MAX, "%s/%s,%d",
		 disc_type_to_config_vals[drec->type].config_root,
		 drec->address, drec->port);
	idbm_rm_disc_node_links(portal);

	/* rm portal dir */
	if (S_ISDIR(statb.st_mode)) {
		memset(portal, 0, PATH_MAX);
		snprintf(portal, PATH_MAX, "%s/%s,%d",
			 disc_type_to_config_vals[drec->type].config_root,
			 drec->address, drec->port);
		rmdir(portal);
	}

free_portal:
	free(portal);
	return rc;
}

/*
 * Backwards Compat:
 * if there is no link then this is pre svn 780 version where
 * we did not link the disc source and node
 */
static int idbm_remove_disc_to_node_link(node_rec_t *rec,
					 char *portal)
{
	int rc = 0;
	struct stat statb;
	node_rec_t *tmprec;

	tmprec = malloc(sizeof(*tmprec));
	if (!tmprec)
		return ISCSI_ERR_NOMEM;

	memset(portal, 0, PATH_MAX);
	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d/%s", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port, rec->tpgt,
		 rec->iface.name);

	rc = __idbm_rec_read(tmprec, portal, false);
	if (rc) {
		/* old style recs will not have tpgt or a link so skip */
		rc = 0;
		goto done;
	}

	log_debug(7, "found drec %s %d",
		  tmprec->disc_address, tmprec->disc_port);
	/* rm link from discovery source to node */
	memset(portal, 0, PATH_MAX);
	rc = setup_disc_to_node_link(portal, tmprec);
	if (rc)
		goto done;

	rc = idbm_lock();
	if (rc)
		goto done;

	if (!stat(portal, &statb)) {
		if (unlink(portal)) {
			log_error("Could not remove link %s: %s",
				  portal, strerror(errno));
			rc = ISCSI_ERR_IDBM;
		} else
			log_debug(7, "rmd %s", portal);
	} else
		log_debug(7, "Could not stat %s", portal);
	idbm_unlock();

done:
	free(tmprec);
	return rc;
}

static int st_disc_filter(const struct dirent *dir)
{
	return strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..") &&
		strcmp(dir->d_name, ST_CONFIG_NAME);
}

int idbm_delete_node(node_rec_t *rec)
{
	struct stat statb;
	char *portal;
	int rc = 0, dir_rm_rc = 0;

	portal = calloc(1, PATH_MAX);
	if (!portal)
		return ISCSI_ERR_NOMEM;

	rc = idbm_remove_disc_to_node_link(rec, portal);
	if (rc)
		goto free_portal;

	memset(portal, 0, PATH_MAX);
	snprintf(portal, PATH_MAX, "%s/%s/%s,%d", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port);
	log_debug(5, "Removing config file %s iface id %s",
		  portal, rec->iface.name);

	rc = idbm_lock();
	if (rc)
		goto free_portal;

	if (!stat(portal, &statb))
		goto rm_conf;

	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d/%s", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port,
		 rec->tpgt, rec->iface.name);
	log_debug(5, "Removing config file %s", portal);

	if (!stat(portal, &statb))
		goto rm_conf;

	log_error("Could not stat %s to delete node: %s",
		  portal, strerror(errno));
	rc = ISCSI_ERR_IDBM;
	goto unlock;

rm_conf:
	if (unlink(portal)) {
		log_error("Could not remove %s: %s", portal, strerror(errno));
		rc = ISCSI_ERR_IDBM;
		goto unlock;
	}

	memset(portal, 0, PATH_MAX);
	snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d", NODE_CONFIG_DIR,
		 rec->name, rec->conn[0].address, rec->conn[0].port,
		 rec->tpgt);
	if (!stat(portal, &statb)) {
		struct dirent **namelist;
		int n, i;

		memset(portal, 0, PATH_MAX);
		snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d", NODE_CONFIG_DIR,
			 rec->name, rec->conn[0].address, rec->conn[0].port,
			 rec->tpgt);
		n = scandir(portal, &namelist, st_disc_filter, alphasort);
		if (n < 0)
			goto free_portal;
		if (n == 0)
			dir_rm_rc = rmdir(portal);

		for (i = 0; i < n; i++)
			free(namelist[i]);
		free(namelist);
	}
	/* rm target dir */
	if (!dir_rm_rc) {
		memset(portal, 0, PATH_MAX);
		snprintf(portal, PATH_MAX, "%s/%s", NODE_CONFIG_DIR, rec->name);
		rmdir(portal);
	}
unlock:
	idbm_unlock();
free_portal:
	free(portal);
	return rc;
}

void
idbm_sendtargets_defaults(struct iscsi_sendtargets_config *cfg)
{
	idbm_sync_config();
	memcpy(cfg, &db->drec_st.u.sendtargets,
	       sizeof(struct iscsi_sendtargets_config));
}

void
idbm_isns_defaults(struct iscsi_isns_config *cfg)
{
	idbm_sync_config();
	memcpy(cfg, &db->drec_isns.u.isns,
	       sizeof(struct iscsi_isns_config));
}

int
idbm_session_autoscan(struct iscsi_session *session)
{
	if (session)
		return session->nrec.session.scan;
	return db->nrec.session.scan;
}

struct user_param *idbm_alloc_user_param(char *name, char *value)
{
	struct user_param *param;

	param = calloc(1, sizeof(*param));
	if (!param)
		return NULL;

	INIT_LIST_HEAD(&param->list);

	param->name = strdup(name);
	if (!param->name)
		goto free_param;

	param->value = strdup(value);
	if (!param->value)
		goto free_name;

	return param;

free_name:
	free(param->name);
free_param:
	free(param);
	return NULL;
}

void idbm_free_user_param(struct user_param *param)
{
	if (!param)
		return;

	if (param->name)
		free(param->name);

	if (param->value)
		free(param->value);

	free(param);
}

int idbm_node_set_rec_from_param(struct list_head *params, node_rec_t *rec,
				 int verify)
{
	struct user_param *param;
	recinfo_t *info;
	int rc = 0;

	if (list_empty(params))
		return 0;

	info = idbm_recinfo_alloc(MAX_KEYS);
	if (!info)
		return ISCSI_ERR_NOMEM;

	idbm_recinfo_node(rec, info);

	if (verify) {
		list_for_each_entry(param, params, list) {
			rc = idbm_verify_param(info, param->name);
			if (rc)
				goto free_info;
		}
	}

	list_for_each_entry(param, params, list) {
		rc = idbm_rec_update_param(info, param->name, param->value, 0);
		if (rc) {
			if (rc == ISCSI_ERR_INVAL)
				log_error("Unknown parameter %s.", param->name);
			goto free_info;
		}
	}

free_info:
	free(info);
	return rc;
}

int idbm_node_set_param(void *data, node_rec_t *rec)
{
	int rc;

	rc = idbm_node_set_rec_from_param(data, rec, 1);
	if (rc)
		return rc;

	return idbm_rec_write(rec, true);
}

int idbm_discovery_set_param(void *data, discovery_rec_t *rec)
{
	struct list_head *params = data;
	struct user_param *param;
	recinfo_t *info;
	int rc = 0;

	info = idbm_recinfo_alloc(MAX_KEYS);
	if (!info)
		return ISCSI_ERR_NOMEM;

	idbm_recinfo_discovery((discovery_rec_t *)rec, info);

	list_for_each_entry(param, params, list) {
		rc = idbm_verify_param(info, param->name);
		if (rc)
			goto free_info;
	}

	list_for_each_entry(param, params, list) {
		rc = idbm_rec_update_param(info, param->name, param->value, 0);
		if (rc)
			goto free_info;
	}

	rc = idbm_discovery_write((discovery_rec_t *)rec);
	if (rc)
		goto free_info;

free_info:
	free(info);
	return rc;
}

int idbm_init(idbm_get_config_file_fn *fn)
{
	/* make sure root db dir is there */
	if (access(ISCSI_DB_ROOT, F_OK) != 0) {
		if (mkdir(ISCSI_DB_ROOT, 0770) != 0) {
			log_error("Could not make %s %d", ISCSI_DB_ROOT,
				   errno);
			return errno;
		}
	}

	db = malloc(sizeof(idbm_t));
	if (!db) {
		log_error("out of memory on idbm allocation");
		return ISCSI_ERR_NOMEM;
	}
	memset(db, 0, sizeof(idbm_t));
	db->get_config_file = fn;
	return 0;
}

void idbm_terminate(void)
{
	if (db)
		free(db);
}

static bool idbm_populate_rec(struct node_rec *rec,
			      char *targetname, int tpgt, char *ip,
			      int port, struct iface_rec *iface,
			      int verbose)
{
	if (targetname)
		strlcpy(rec->name, targetname, TARGET_NAME_MAXLEN);
	rec->tpgt = tpgt;
	rec->conn[0].port = port;
	if (ip)
		strlcpy(rec->conn[0].address, ip, NI_MAXHOST);
	memset(&rec->iface, 0, sizeof(struct iface_rec));
	if (iface) {
		iface_copy(&rec->iface, iface);
		if (strlen(iface->name)) {
			if (iface_conf_read(&rec->iface)) {
				if (verbose)
					log_error("Could not read iface info "
						  "for %s.", iface->name);
				return false;
			}
		}
	}
	return true;
}

/**
 * idbm_create_rec - allocate and setup a node record
 * @targetname: target name
 * @tgpt: target portal group
 * @ip: ip address of portal
 * @port: port of portal
 * @iface: iscsi iface info
 * @verbose: flag indicating whether to log ifaces setup errors
 *
 * The iface only needs to have the name set. This function will
 * read in the other values.
 */
struct node_rec *idbm_create_rec(char *targetname, int tpgt, char *ip,
				 int port, struct iface_rec *iface,
				 int verbose)
{
	struct node_rec *rec;

	rec = calloc(1, sizeof(*rec));
	if (!rec) {
		log_error("Could not not allocate memory to create node "
			  "record.");
		return NULL;
	}

	idbm_node_setup_defaults(rec);

	if (!idbm_populate_rec(rec, targetname, tpgt, ip, port, iface, verbose)) {
		goto free_rec;
	}
	return rec;
free_rec:
	free(rec);
	return NULL;
}

struct node_rec *idbm_create_rec_from_boot_context(struct boot_context *context)
{
	node_rec_t *rec;

	rec = malloc(sizeof(*rec));
	if (!rec) {
		log_error("Could not not allocate memory to create node "
			  "record.");
		return NULL;
	}

	idbm_node_setup_from_conf(rec);

	/* tpgt hard coded to 1 ??? */
	if (!idbm_populate_rec(rec, context->targetname, 1,
			       context->target_ipaddr, context->target_port,
			       NULL, 1)) {
		log_error("Could not setup rec for fw discovery login.");
		free(rec);
		return NULL;
	}

	iface_setup_defaults(&rec->iface);
	strlcpy(rec->session.auth.username, context->chap_name,
		sizeof(context->chap_name));
	strlcpy((char *)rec->session.auth.password, context->chap_password,
		sizeof(context->chap_password));
	strlcpy(rec->session.auth.username_in, context->chap_name_in,
		sizeof(context->chap_name_in));
	strlcpy((char *)rec->session.auth.password_in,
		context->chap_password_in,
		sizeof(context->chap_password_in));
	rec->session.auth.password_length =
				strlen((char *)context->chap_password);
	rec->session.auth.password_in_length =
				strlen((char *)context->chap_password_in);
	strlcpy(rec->session.boot_root, context->boot_root,
		sizeof(context->boot_root));
	strlcpy(rec->session.boot_nic, context->boot_nic,
		sizeof(context->boot_nic));
	strlcpy(rec->session.boot_target, context->boot_target,
		sizeof(context->boot_target));

	iface_setup_from_boot_context(&rec->iface, context);

	return rec;
}

void idbm_node_setup_defaults(node_rec_t *rec)
{
	int i;

	memset(rec, 0, sizeof(node_rec_t));

	INIT_LIST_HEAD(&rec->list);

	rec->tpgt = PORTAL_GROUP_TAG_UNKNOWN;
	rec->disc_type = DISCOVERY_TYPE_STATIC;
	rec->leading_login = 0;
	rec->session.cmds_max = CMDS_MAX;
	rec->session.xmit_thread_priority = XMIT_THREAD_PRIORITY;
	rec->session.initial_cmdsn = 0;
	rec->session.queue_depth = QUEUE_DEPTH;
	rec->session.nr_sessions = 1;
	rec->session.initial_login_retry_max = DEF_INITIAL_LOGIN_RETRIES_MAX;
	rec->session.reopen_max = DEF_SESSION_REOPEN_MAX;
	rec->session.auth.authmethod = 0;
	/* TYPE_INT_LIST fields should be initialized to ~0 to indicate unset values */
	memset(rec->session.auth.chap_algs, ~0, sizeof(rec->session.auth.chap_algs));
	rec->session.auth.chap_algs[0] = AUTH_CHAP_ALG_MD5;
	rec->session.auth.password_length = 0;
	rec->session.auth.password_in_length = 0;
	rec->session.err_timeo.abort_timeout = DEF_ABORT_TIMEO;
	rec->session.err_timeo.lu_reset_timeout = DEF_LU_RESET_TIMEO;
	rec->session.err_timeo.tgt_reset_timeout = DEF_TGT_RESET_TIMEO;
	rec->session.err_timeo.host_reset_timeout = DEF_HOST_RESET_TIMEO;
	rec->session.timeo.replacement_timeout = DEF_REPLACEMENT_TIMEO;
	rec->session.info = NULL;
	rec->session.sid = 0;
	rec->session.multiple = 0;
	rec->session.scan = DEF_INITIAL_SCAN;
	idbm_setup_session_defaults(&rec->session.iscsi);

	for (i=0; i<ISCSI_CONN_MAX; i++) {
		rec->conn[i].startup = ISCSI_STARTUP_MANUAL;
		rec->conn[i].port = ISCSI_LISTEN_PORT;
		rec->conn[i].tcp.window_size = TCP_WINDOW_SIZE;
		rec->conn[i].tcp.type_of_service = 0;
		rec->conn[i].timeo.login_timeout= DEF_LOGIN_TIMEO;
		rec->conn[i].timeo.logout_timeout= DEF_LOGOUT_TIMEO;
		rec->conn[i].timeo.auth_timeout = 45;

		rec->conn[i].timeo.noop_out_interval = DEF_NOOP_OUT_INTERVAL;
		rec->conn[i].timeo.noop_out_timeout = DEF_NOOP_OUT_TIMEO;

		idbm_setup_conn_defaults(&rec->conn[i].iscsi);
	}

	iface_setup_defaults(&rec->iface);
}

struct node_rec *
idbm_find_rec_in_list(struct list_head *rec_list, char *targetname, char *addr,
		      int port, struct iface_rec *iface)
{
	struct node_rec *rec;

	list_for_each_entry(rec, rec_list, list) {
		if (__iscsi_match_session(rec, targetname, addr, port, iface,
					  MATCH_ANY_SID))
			return rec;
	}

	return NULL;
}
