/*
* Copyright (C) 2017-2018 Red Hat, Inc.
*
* 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 3 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Author: Gris Ge
*/
/* The code below is modified from usr/idbm.c which licensed like below:
*
* 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.
*
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* For strerror_r() */
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "libopeniscsiusr/libopeniscsiusr_common.h"
#include "context.h"
#include "idbm.h"
#include "misc.h"
#include "idbm_fields.h"
#include "iface.h"
#include "version.h"
#include "node.h"
#include "default.h"
#define TYPE_INT_O 1
#define TYPE_STR 2
#define TYPE_UINT8 3
#define TYPE_UINT16 4
#define TYPE_UINT32 5
#define TYPE_INT32 6
#define TYPE_INT64 7
#define TYPE_BOOL 8
#define MAX_KEYS 256 /* number of keys total(including CNX_MAX) */
#define NAME_MAXVAL 128 /* the maximum length of key name */
#define VALUE_MAXVAL 256 /* the maximum length of 223 bytes in the RFC. */
/* ^ MAX_KEYS, NAME_MAXVAL and VALUE_MAXVAL are copied from usr/idbm.h
* The RFC 3720 only said:
* If not otherwise specified, the maximum length of a simple-value (not
* its encoded representation) is 255 bytes, not including the delimiter
* (comma or zero byte).
*/
#define OPTS_MAXVAL 8
#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 */
#define ISCSI_BEGIN_REC "# BEGIN RECORD "ISCSI_VERSION_STR
#define ISCSI_END_REC "# END RECORD"
#ifndef LOCK_DIR
#define LOCK_DIR "/run/lock/iscsi"
#endif
#define LOCK_FILE LOCK_DIR"/lock"
#define LOCK_WRITE_FILE LOCK_DIR"/lock.write"
#define _rec_str(_key, _recs, _org, _name, _show, _n, _mod) \
do { \
_recs[_n].type = TYPE_STR; \
_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
if (strlen((char*)_org->_name)) \
_strncpy((char*)_recs[_n].value, (char*)_org->_name, \
VALUE_MAXVAL); \
_recs[_n].data = &_org->_name; \
_recs[_n].data_len = sizeof(_org->_name); \
_recs[_n].visible = _show; \
_recs[_n].can_modify = _mod; \
_n++; \
} while(0)
#define _rec_uint8(_key, _recs, _org, _name, _show, _n, _mod) \
do { \
_recs[_n].type = TYPE_UINT8; \
_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu8, _org->_name); \
_recs[_n].data = &_org->_name; \
_recs[_n].data_len = sizeof(_org->_name); \
_recs[_n].visible = _show; \
_recs[_n].can_modify = _mod; \
_n++; \
} while (0)
#define _rec_uint16(_key, _recs, _org, _name, _show, _n, _mod) \
do { \
_recs[_n].type = TYPE_UINT16; \
_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu16, _org->_name); \
_recs[_n].data = &_org->_name; \
_recs[_n].data_len = sizeof(_org->_name); \
_recs[_n].visible = _show; \
_recs[_n].can_modify = _mod; \
_n++; \
} while (0)
#define _rec_uint32(_key, _recs, _org, _name, _show, _n, _mod) \
do { \
_recs[_n].type = TYPE_UINT32; \
_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu32, _org->_name); \
_recs[_n].data = &_org->_name; \
_recs[_n].data_len = sizeof(_org->_name); \
_recs[_n].visible = _show; \
_recs[_n].can_modify = _mod; \
_n++; \
} while (0)
#define _rec_int32(_key, _recs, _org, _name, _show, _n, _mod) \
do { \
_recs[_n].type = TYPE_INT32; \
_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIi32, _org->_name); \
_recs[_n].data = &_org->_name; \
_recs[_n].data_len = sizeof(_org->_name); \
_recs[_n].visible = _show; \
_recs[_n].can_modify = _mod; \
_n++; \
} while (0)
#define _rec_int64(_key, _recs, _org, _name, _show, _n, _mod) \
do { \
_recs[_n].type = TYPE_INT64; \
_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIi64, _org->_name); \
_recs[_n].data = &_org->_name; \
_recs[_n].data_len = sizeof(_org->_name); \
_recs[_n].visible = _show; \
_recs[_n].can_modify = _mod; \
_n++; \
} while (0)
#define _rec_bool(_key, _recs, _org, _name, _show, _n, _mod) \
do { \
_recs[_n].type = TYPE_BOOL; \
_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
snprintf(_recs[_n].value, VALUE_MAXVAL, "%s", \
_org->_name ? "Yes" : "No"); \
_recs[_n].data = &_org->_name; \
_recs[_n].data_len = sizeof(_org->_name); \
_recs[_n].visible = _show; \
_recs[_n].can_modify = _mod; \
_n++; \
} while(0)
#define _rec_int_o2(_key, _recs, _org, _name, _show, _op0, _op1, _n, _mod) \
do { \
_recs[_n].type = TYPE_INT_O; \
_strncpy(_recs[_n].name, _key, NAME_MAXVAL); \
if (_org->_name == 0) _strncpy(_recs[_n].value, _op0, VALUE_MAXVAL); \
if (_org->_name == 1) _strncpy(_recs[_n].value, _op1, VALUE_MAXVAL); \
_recs[_n].data = &_org->_name; \
_recs[_n].data_len = sizeof(_org->_name); \
_recs[_n].visible = _show; \
_recs[_n].opts[0] = _op0; \
_recs[_n].opts[1] = _op1; \
_recs[_n].numopts = 2; \
_recs[_n].can_modify = _mod; \
_n++; \
} while(0)
#define _rec_int_o3(_key, _recs, _org, _name, _show, _op0, _op1, _op2, _n, \
_mod) \
do { \
_rec_int_o2(_key, _recs, _org, _name, _show, _op0, _op1, _n, _mod); \
_n--; \
if (_org->_name == 2) _strncpy(_recs[_n].value, _op2, VALUE_MAXVAL);\
_recs[_n].opts[2] = _op2; \
_recs[_n].numopts = 3; \
_n++; \
} while(0)
#define _rec_int_o4(_key, _recs, _org, _name, _show, _op0, _op1, _op2, _op3, \
_n, _mod) \
do { \
_rec_int_o3(_key, _recs, _org, _name, _show, _op0, _op1, _op2, _n, \
_mod); \
_n--; \
if (_org->_name == 3) _strncpy(_recs[_n].value, _op3, VALUE_MAXVAL);\
_recs[_n].opts[3] = _op3; \
_recs[_n].numopts = 4; \
_n++; \
} while(0)
#define _rec_int_o5(_key, _recs, _org, _name, _show, _op0, _op1, _op2, _op3, \
_op4, _n, _mod) \
do { \
_rec_int_o4(_key, _recs, _org, _name, _show, _op0, _op1, _op2, _op3, \
_n, _mod); \
_n--; \
if (_org->_name == 4) _strncpy(_recs[_n].value, _op4, VALUE_MAXVAL);\
_recs[_n].opts[4] = _op4; \
_recs[_n].numopts = 5; \
_n++; \
} while(0)
#define _rec_int_o6(_key, _recs, _org, _name, _show, _op0, _op1, _op2, _op3, \
_op4, _op5, _n, _mod) \
do { \
_rec_int_o5(_key, _recs, _org, _name, _show, _op0, _op1, _op2, _op3, \
_op4, _n, _mod); \
_n--; \
if (_org->_name == 5) _strncpy(_recs[_n].value, _op5, VALUE_MAXVAL);\
_recs[_n].opts[5] = _op5; \
_recs[_n].numopts = 6; \
_n++; \
} while(0)
enum modify_mode {
_CANNOT_MODIFY,
_CAN_MODIFY,
};
struct idbm_rec {
int type;
char name[NAME_MAXVAL];
char value[VALUE_MAXVAL];
void *data;
int data_len;
int visible;
char* opts[OPTS_MAXVAL];
int numopts;
/*
* TODO: make it a enum that can indicate whether it also requires
* a relogin to pick up if a session is running.
*/
enum modify_mode can_modify;
};
static void _idbm_node_rec_link(struct iscsi_node *node, struct idbm_rec *recs);
int _idbm_lock(struct iscsi_context *ctx)
{
int fd, i, ret;
struct idbm *db = NULL;
char strerr_buff[_STRERR_BUFF_LEN];
int errno_save = 0;
assert(ctx != NULL);
db = ctx->db;
if (db->refs > 0) {
db->refs++;
return 0;
}
if (access(LOCK_DIR, F_OK) != 0) {
if (mkdir(LOCK_DIR, 0660) != 0) {
_error(ctx, "Could not open %s: %d %s", LOCK_DIR, errno,
_strerror(errno, strerr_buff));
return LIBISCSI_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;
errno_save = errno;
if (errno != EEXIST) {
_error(ctx, "Maybe you are not root? "
"Could not lock discovery DB: %s: %d %s",
LOCK_WRITE_FILE, errno_save,
_strerror(errno_save, strerr_buff));
return LIBISCSI_ERR_IDBM;
} else if (i == 0)
_debug(ctx, "Waiting for discovery DB lock on %s",
LOCK_WRITE_FILE);
usleep(10000);
}
if (ret != 0) {
_error(ctx, "Timeout on acquiring lock on DB: %s, errno: %d %s",
LOCK_WRITE_FILE, errno_save,
_strerror(errno_save, strerr_buff));
return LIBISCSI_ERR_IDBM;
}
db->refs = 1;
return 0;
}
void _idbm_unlock(struct iscsi_context *ctx)
{
struct idbm *db = NULL;
assert(ctx != NULL);
db = ctx->db;
if (db->refs > 1) {
db->refs--;
return;
}
db->refs = 0;
unlink(LOCK_WRITE_FILE);
}
static struct idbm_rec* _idbm_recs_alloc(void)
{
return calloc(MAX_KEYS, sizeof(struct idbm_rec));
}
static void _idbm_recs_free(struct idbm_rec* recs)
{
free(recs);
}
static int _idbm_iface_rec_link(struct iscsi_iface *iface,
struct idbm_rec *recs, int num)
{
int init_num = num;
if (init_num == 0)
_rec_str(IFACE_ISCSINAME, recs, iface, name, IDBM_SHOW, num,
_CANNOT_MODIFY);
else
_rec_str(IFACE_ISCSINAME, recs, iface, name, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_NETNAME, recs, iface, netdev, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_IPADDR, recs, iface, ipaddress, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint8(IFACE_PREFIX_LEN, recs, iface, prefix_len, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_HWADDR, recs, iface, hwaddress, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_TRANSPORTNAME, recs, iface, transport_name, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_INAME, recs, iface, iname, IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_STATE, recs, iface, state, IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint16(IFACE_VLAN_ID, recs, iface, vlan_id, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint8(IFACE_VLAN_PRIORITY, recs, iface, vlan_priority, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_VLAN_STATE, recs, iface, vlan_state, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint32(IFACE_NUM, recs, iface, iface_num, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint16(IFACE_MTU, recs, iface, mtu, IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint16(IFACE_PORT, recs, iface, port, IDBM_SHOW, num, _CAN_MODIFY);
if (! iface->is_ipv6) {
_rec_str(IFACE_BOOT_PROTO, recs, iface, bootproto, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_SUBNET_MASK, recs, iface, subnet_mask, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_GATEWAY, recs, iface, gateway, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_DHCP_ALT_CID, recs, iface,
dhcp_alt_client_id_state, IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_DHCP_ALT_CID_STR, recs, iface,
dhcp_alt_client_id, IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_DHCP_DNS, recs, iface, dhcp_dns, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_DHCP_LEARN_IQN, recs, iface, dhcp_learn_iqn,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_DHCP_REQ_VID, recs, iface,
dhcp_req_vendor_id_state, IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_DHCP_VID, recs, iface, dhcp_vendor_id_state,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_DHCP_VID_STR, recs, iface, dhcp_vendor_id,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_DHCP_SLP_DA, recs, iface, dhcp_slp_da, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_FRAGMENTATION, recs, iface, fragmentation,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_GRAT_ARP, recs, iface, gratuitous_arp, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_IN_FORWARD, recs, iface, incoming_forwarding,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_TOS_STATE, recs, iface, tos_state, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_uint8(IFACE_TOS, recs, iface, tos, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint8(IFACE_TTL, recs, iface, ttl, IDBM_SHOW, num,
_CAN_MODIFY);
} else {
_rec_str(IFACE_IPV6_AUTOCFG, recs, iface, ipv6_autocfg,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_LINKLOCAL_AUTOCFG, recs, iface,
linklocal_autocfg, IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_ROUTER_AUTOCFG, recs, iface, router_autocfg,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_LINKLOCAL, recs, iface, ipv6_linklocal,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_ROUTER, recs, iface, ipv6_router, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint8(IFACE_DUP_ADDR_DETECT_CNT, recs, iface,
dup_addr_detect_cnt, IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint32(IFACE_FLOW_LABEL, recs, iface, flow_label,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_GRAT_NEIGHBOR_ADV, recs, iface,
gratuitous_neighbor_adv, IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint8(IFACE_HOP_LIMIT, recs, iface, hop_limit, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_MLD, recs, iface, mld, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint32(IFACE_ND_REACHABLE_TMO, recs, iface,
nd_reachable_tmo, IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint32(IFACE_ND_REXMIT_TIME, recs, iface, nd_rexmit_time,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint32(IFACE_ND_STALE_TMO, recs, iface, nd_stale_tmo,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint32(IFACE_RTR_ADV_LINK_MTU, recs, iface,
router_adv_link_mtu, IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint8(IFACE_TRAFFIC_CLASS, recs, iface, traffic_class,
IDBM_SHOW, num, _CAN_MODIFY);
}
_rec_str(IFACE_DELAYED_ACK, recs, iface, delayed_ack, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_TCP_NAGLE, recs, iface, nagle, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_TCP_WSF_STATE, recs, iface, tcp_wsf_state, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_uint8(IFACE_TCP_WSF, recs, iface, tcp_wsf, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint8(IFACE_TCP_TIMER_SCALE, recs, iface, tcp_timer_scale,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_TCP_TIMESTAMP, recs, iface, tcp_timestamp, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_REDIRECT, recs, iface, redirect, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint16(IFACE_DEF_TMF_TMO, recs, iface, def_task_mgmt_tmo,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_HDRDGST, recs, iface, header_digest, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_DATADGST, recs, iface, data_digest, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_IMM_DATA, recs, iface, immediate_data, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_INITIAL_R2T, recs, iface, initial_r2t, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_DSEQ_INORDER, recs, iface, data_seq_inorder, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_DPDU_INORDER, recs, iface, data_pdu_inorder, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_uint8(IFACE_ERL, recs, iface, erl, IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint32(IFACE_MAX_RECV_DLEN, recs, iface, max_recv_dlength,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint32(IFACE_FIRST_BURST, recs, iface, first_burst_len, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_uint16(IFACE_MAX_R2T, recs, iface, max_out_r2t, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_uint32(IFACE_MAX_BURST, recs, iface, max_burst_len, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_CHAP_AUTH, recs, iface, chap_auth, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_BIDI_CHAP, recs, iface, bidi_chap, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(IFACE_STRICT_LOGIN_COMP, recs, iface, strict_login_comp,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(IFACE_DISCOVERY_AUTH, recs, iface, discovery_auth, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(IFACE_DISCOVERY_LOGOUT, recs, iface, discovery_logout,
IDBM_SHOW, num, _CAN_MODIFY);
return num;
}
static void _idbm_recs_print(struct idbm_rec *recs, FILE *f, int show)
{
int i;
fprintf(f, "%s\n", ISCSI_BEGIN_REC);
for (i = 0; i < MAX_KEYS; i++) {
if (recs[i].visible == IDBM_HIDE)
continue;
if (show == IDBM_MASKED && recs[i].visible == IDBM_MASKED) {
if (*(char*)recs[i].data) {
fprintf(f, "%s = ********\n", recs[i].name);
continue;
}
/* fall through */
}
if (strlen(recs[i].value))
fprintf(f, "%s = %s\n", recs[i].name, recs[i].value);
else if (f == stdout)
fprintf(f, "%s = \n", recs[i].name);
}
fprintf(f, "%s\n", ISCSI_END_REC);
}
void _idbm_iface_print(struct iscsi_iface *iface, FILE *f)
{
struct idbm_rec *recs = NULL;
recs = _idbm_recs_alloc();
if (recs == NULL)
return;
_idbm_iface_rec_link(iface, recs, 0);
_idbm_recs_print(recs, f, IDBM_SHOW);
_idbm_recs_free(recs);
}
void _idbm_node_print(struct iscsi_node *node, FILE *f, bool show_secret)
{
struct idbm_rec *recs = NULL;
recs = _idbm_recs_alloc();
if (recs == NULL)
return;
_idbm_node_rec_link(node, recs);
_idbm_recs_print(recs, f, show_secret ? IDBM_SHOW : IDBM_MASKED);
_idbm_recs_free(recs);
}
static int _idbm_rec_update_param(struct iscsi_context *ctx,
struct idbm_rec *recs, char *name,
char *value, int line_number)
{
int rc = LIBISCSI_OK;
int i = 0;
int j = 0;
int passwd_done = 0;
char passwd_len[8];
assert(ctx != NULL);
assert(recs != NULL);
assert(name != NULL);
assert(value != NULL);
setup_passwd_len:
for (i = 0; i < MAX_KEYS; ++i) {
if (!strcmp(name, recs[i].name)) {
_debug(ctx, "updated '%s', '%s' => '%s'", name,
recs[i].value, value);
/* parse recinfo by type */
switch (recs[i].type) {
case TYPE_UINT8:
if (!recs[i].data)
continue;
*(uint8_t *)recs[i].data =
strtoul(value, NULL, 10);
goto updated;
case TYPE_UINT16:
if (!recs[i].data)
continue;
*(uint16_t *)recs[i].data =
strtoul(value, NULL, 10);
goto updated;
case TYPE_UINT32:
if (!recs[i].data)
continue;
*(uint32_t *)recs[i].data =
strtoul(value, NULL, 10);
goto updated;
case TYPE_STR:
if (!recs[i].data)
continue;
_strncpy((char*)recs[i].data,
value, recs[i].data_len);
goto updated;
case TYPE_INT32:
if (!recs[i].data)
continue;
*(int32_t *)recs[i].data =
strtoul(value, NULL, 10);
goto updated;
case TYPE_INT64:
if (!recs[i].data)
continue;
*(int64_t *)recs[i].data =
strtoull(value, NULL, 10);
goto updated;
case TYPE_INT_O:
for (j = 0; j < recs[i].numopts; ++j) {
if (!strcmp(value, recs[i].opts[j])) {
if (!recs[i].data)
continue;
*(int*)recs[i].data = j;
goto updated;
}
}
goto unknown_value;
case TYPE_BOOL:
if (!recs[i].data)
continue;
if (strcmp(value, "Yes") == 0)
*(bool *)recs[i].data = true;
else if (strcmp(value, "No") == 0)
*(bool *)recs[i].data = false;
else
goto unknown_value;
goto updated;
default:
unknown_value:
_error(ctx, "Got unknown data type %d "
"for name '%s', value '%s'",
recs[i].data, recs[i].name,
recs[i].value);
rc = LIBISCSI_ERR_BUG;
goto out;
}
if (line_number) {
_warn(ctx, "config file line %d contains "
"unknown value format '%s' for "
"parameter name '%s'",
line_number, value, name);
} else {
_error(ctx, "unknown value format '%s' for "
"parameter name '%s'", value, name);
rc = LIBISCSI_ERR_INVAL;
}
goto out;
}
}
_error(ctx, "Unknown parameter name %s", name);
rc = LIBISCSI_ERR_INVAL;
goto out;
updated:
_strncpy((char*)recs[i].value, value, VALUE_MAXVAL);
#define check_password_param(_param) \
if (!passwd_done && !strcmp(#_param, name)) { \
passwd_done = 1; \
name = #_param "_length"; \
snprintf(passwd_len, 8, "%.7d", (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(discovery.slp.auth.password);
check_password_param(discovery.slp.auth.password_in);
check_password_param(host.auth.password);
check_password_param(host.auth.password_in);
out:
return rc;
}
/*
* from linux kernel
*/
static char *strstrip(char *s)
{
size_t size;
char *end;
size = strlen(s);
if (!size)
return s;
end = s + size - 1;
while (end >= s && isspace(*end))
end--;
*(end + 1) = '\0';
while (*s && isspace(*s))
s++;
return s;
}
static int _idbm_recs_read(struct iscsi_context *ctx, struct idbm_rec *recs,
const char *conf_path)
{
int rc = LIBISCSI_OK;
char name[NAME_MAXVAL];
char value[VALUE_MAXVAL];
char *line = NULL;
char *nl = NULL;
char buffer[2048];
int line_number = 0;
int c = 0;
int i = 0;
FILE *f = NULL;
int errno_save = 0;
char strerr_buff[_STRERR_BUFF_LEN];
assert(ctx != NULL);
assert(recs != NULL);
assert(conf_path != NULL);
f = fopen(conf_path, "r");
errno_save = errno;
if (!f) {
_error(ctx, "Failed to open %s using read mode: %d %s",
conf_path, errno_save,
_strerror(errno_save, strerr_buff));
rc = LIBISCSI_ERR_IDBM;
goto out;
}
_info(ctx, "Parsing iSCSI interface configuration %s", conf_path);
/* process the config file */
do {
line = fgets(buffer, sizeof (buffer), f);
line_number++;
if (!line)
continue;
if (strlen(line) == 0)
continue;
nl = line + strlen(line) - 1;
if (*nl != '\n') {
_warn(ctx, "Config file %s line %d too long.",
conf_path, 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) {
_warn(ctx, "config file %s line %d do not has value",
conf_path, line_number);
continue;
}
*(name+i)=0; nl++;
/* skip after-name traling spaces */
while (*nl && isspace(c = *nl)) nl++;
if (*nl && *nl != '=') {
_warn(ctx, "config file %s line %d has not '=' "
"separator", conf_path, line_number);
continue;
}
/* skip '=' sepa */
nl++;
/* skip after-sepa traling spaces */
while (*nl && isspace(c = *nl)) nl++;
if (!*nl) {
_warn(ctx, "config file %s line %d do not has value",
conf_path, line_number);
continue;
}
/* parse value */
i=0; *value = 0;
while (*nl) {
*(value+i) = *nl; i++; nl++;
}
*(value+i) = 0;
rc = _idbm_rec_update_param(ctx, recs, name, value,
line_number);
if (rc == LIBISCSI_ERR_INVAL) {
_error(ctx, "config file %s invalid.", conf_path);
goto out;
} else if (rc != LIBISCSI_OK)
goto out;
} while (line);
out:
if (f != NULL)
fclose(f);
return rc;
}
int _idbm_iface_get(struct iscsi_context *ctx, const char *iface_name, struct
iscsi_iface **iface)
{
int rc = LIBISCSI_OK;
char *conf_path = NULL;
struct idbm_rec *recs = NULL;
assert(iface != NULL);
assert(ctx != NULL);
*iface = NULL;
if (iface_name == NULL)
goto out;
if (strcmp(iface_name, "iface.example") == 0)
goto out;
_good(_asprintf(&conf_path, "%s/%s", IFACE_CONFIG_DIR, iface_name),
rc, out);
*iface = calloc(1, sizeof(struct iscsi_iface));
_alloc_null_check(ctx, *iface, rc, out);
snprintf((*iface)->name, sizeof((*iface)->name)/sizeof(char),
"%s", iface_name);
if (strstr(iface_name, "ipv6"))
(*iface)->is_ipv6 = true;
recs = _idbm_recs_alloc();
_alloc_null_check(ctx, recs, rc, out);
_idbm_iface_rec_link(*iface, recs, 0);
_good(_idbm_recs_read(ctx, recs, conf_path), rc, out);
if (! _iface_is_valid(*iface)) {
_warn(ctx, "'%s' is not a valid iSCSI interface configuration "
"file", conf_path);
iscsi_iface_free(*iface);
*iface = NULL;
/* We still treat this as pass(no error) */
}
out:
if (rc != LIBISCSI_OK) {
iscsi_iface_free(*iface);
*iface = NULL;
}
free(conf_path);
_idbm_recs_free(recs);
return rc;
}
struct idbm *_idbm_new(void)
{
return calloc(1, sizeof(struct idbm));
}
void _idbm_free(struct idbm *db)
{
free(db);
}
static void _idbm_node_rec_link(struct iscsi_node *node, struct idbm_rec *recs)
{
int num = 0;
_rec_str(NODE_NAME, recs, node, target_name, IDBM_SHOW, num,
_CANNOT_MODIFY);
_rec_int32(NODE_TPGT, recs, node, tpgt, IDBM_SHOW, num,
_CANNOT_MODIFY);
_rec_int_o3(NODE_STARTUP, recs, node, startup, IDBM_SHOW, "manual",
"automatic", "onboot", num, _CAN_MODIFY);
_rec_bool(NODE_LEADING_LOGIN, recs, node, leading_login, IDBM_SHOW,
num, _CAN_MODIFY);
/*
* 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 normally 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).
*/
num = _idbm_iface_rec_link(&((*node).iface), recs, num);
_rec_str(NODE_DISC_ADDR, recs, node, disc_address, IDBM_SHOW, num,
_CANNOT_MODIFY);
_rec_int32(NODE_DISC_PORT, recs, node, disc_port, IDBM_SHOW, num,
_CANNOT_MODIFY);
_rec_int_o6(NODE_DISC_TYPE, recs, node, disc_type, IDBM_SHOW,
"send_targets", "isns", "offload_send_targets", "slp",
"static", "fw", num, _CANNOT_MODIFY);
_rec_uint32(SESSION_INIT_CMDSN, recs, node, session.initial_cmdsn,
IDBM_SHOW, num,_CAN_MODIFY);
_rec_int64(SESSION_INIT_LOGIN_RETRY, recs, node,
session.initial_login_retry_max, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int64(SESSION_XMIT_THREAD_PRIORITY, recs, node,
session.xmit_thread_priority, IDBM_SHOW, num, _CAN_MODIFY);
_rec_uint16(SESSION_CMDS_MAX, recs, node, session.cmds_max, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_uint16(SESSION_QDEPTH, recs, node, session.queue_depth, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_int64(SESSION_NR_SESSIONS, recs, node, session.nr_sessions,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int_o2(SESSION_AUTH_METHOD, recs, node, session.auth.authmethod,
IDBM_SHOW, "None", "CHAP", num, _CAN_MODIFY);
_rec_str(SESSION_USERNAME, recs, node, session.auth.username, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_str(SESSION_PASSWORD, recs, node, session.auth.password,
IDBM_MASKED, num, _CAN_MODIFY);
_rec_uint32(SESSION_PASSWORD_LEN, recs, node,
session.auth.password_length, IDBM_HIDE, num, _CAN_MODIFY);
_rec_str(SESSION_USERNAME_IN, recs, node, session.auth.username_in,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_str(SESSION_PASSWORD_IN, recs, node, session.auth.password_in,
IDBM_MASKED, num, _CAN_MODIFY);
_rec_uint32(SESSION_PASSWORD_IN_LEN, recs, node,
session.auth.password_in_length, IDBM_HIDE, num,
_CAN_MODIFY);
_rec_int64(SESSION_REPLACEMENT_TMO, recs, node,
session.tmo.replacement_timeout, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int64(SESSION_ABORT_TMO, recs, node, session.err_tmo.abort_timeout,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(SESSION_LU_RESET_TMO, recs, node,
session.err_tmo.lu_reset_timeout, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int64(SESSION_TGT_RESET_TMO, recs, node,
session.err_tmo.tgt_reset_timeout, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int64(SESSION_HOST_RESET_TMO, recs, node,
session.err_tmo.host_reset_timeout, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_bool(SESSION_FAST_ABORT, recs, node, session.op_cfg.FastAbort,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_bool(SESSION_INITIAL_R2T, recs, node, session.op_cfg.InitialR2T,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_bool(SESSION_IMM_DATA, recs, node, session.op_cfg.ImmediateData,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(SESSION_FIRST_BURST, recs, node,
session.op_cfg.FirstBurstLength, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int64(SESSION_MAX_BURST, recs, node, session.op_cfg.MaxBurstLength,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(SESSION_DEF_TIME2RETAIN, recs, node,
session.op_cfg.DefaultTime2Retain, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int64(SESSION_DEF_TIME2WAIT, recs, node,
session.op_cfg.DefaultTime2Wait, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int64(SESSION_MAX_CONNS, recs, node, session.op_cfg.MaxConnections,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(SESSION_MAX_R2T, recs, node,
session.op_cfg.MaxOutstandingR2T, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int64(SESSION_ERL, recs, node, session.op_cfg.ERL, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_int_o2(SESSION_SCAN, recs, node, session.scan, IDBM_SHOW, "manual",
"auto", num, _CAN_MODIFY);
_rec_int64(SESSION_REOPEN_MAX, recs, node, session.reopen_max, IDBM_SHOW, num,
_CAN_MODIFY);
_rec_str(CONN_ADDR, recs, node, conn.address, IDBM_SHOW, num,
_CANNOT_MODIFY);
_rec_int32(CONN_PORT, recs, node, conn.port, IDBM_SHOW, num,
_CANNOT_MODIFY);
_rec_int_o3(CONN_STARTUP, recs, node, conn.startup, IDBM_SHOW,
"manual", "automatic", "onboot", num, _CAN_MODIFY);
_rec_int64(CONN_WINDOW_SIZE, recs, node, conn.tcp.window_size,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(CONN_SERVICE_TYPE, recs, node, conn.tcp.type_of_service,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(CONN_LOGOUT_TMO, recs, node, conn.tmo.logout_timeout,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(CONN_LOGIN_TMO, recs, node, conn.tmo.login_timeout,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(CONN_AUTH_TMO, recs, node, conn.tmo.auth_timeout,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(CONN_NOP_INT, recs, node, conn.tmo.noop_out_interval,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(CONN_NOP_TMO, recs, node, conn.tmo.noop_out_timeout,
IDBM_SHOW, num, _CAN_MODIFY);
_rec_int64(CONN_MAX_XMIT_DLEN, recs, node,
conn.op_cfg.MaxXmitDataSegmentLength, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_int64(CONN_MAX_RECV_DLEN, recs, node,
conn.op_cfg.MaxRecvDataSegmentLength, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_int_o4(CONN_HDR_DIGEST, recs, node, conn.op_cfg.HeaderDigest,
IDBM_SHOW, "None", "CRC32C", "CRC32C,None",
"None,CRC32C", num, _CAN_MODIFY);
_rec_int_o4(CONN_DATA_DIGEST, recs, node, conn.op_cfg.DataDigest,
IDBM_SHOW, "None", "CRC32C", "CRC32C,None",
"None,CRC32C", num, _CAN_MODIFY);
_rec_bool(CONN_IFMARKER, recs, node, conn.op_cfg.IFMarker, IDBM_SHOW,
num, _CAN_MODIFY);
_rec_bool(CONN_OFMARKER, recs, node, conn.op_cfg.OFMarker, IDBM_SHOW,
num, _CAN_MODIFY);
}
int _idbm_node_get(struct iscsi_context *ctx, const char *target_name,
const char *portal, const char *iface_name,
struct iscsi_node **node)
{
int rc = LIBISCSI_OK;
char *conf_path = NULL;
struct idbm_rec *recs = NULL;
assert(node != NULL);
assert(ctx != NULL);
*node = NULL;
if ((target_name == NULL) || (portal == NULL))
goto out;
if (iface_name == NULL) // old style of config
_good(_asprintf(&conf_path, "%s/%s/%s", NODE_CONFIG_DIR,
target_name, portal), rc, out);
else
_good(_asprintf(&conf_path, "%s/%s/%s/%s", NODE_CONFIG_DIR,
target_name, portal, iface_name), rc, out);
*node = calloc(1, sizeof(struct iscsi_node));
_alloc_null_check(ctx, *node, rc, out);
_default_node(*node);
recs = _idbm_recs_alloc();
_alloc_null_check(ctx, recs, rc, out);
_idbm_node_rec_link(*node, recs);
_good(_idbm_recs_read(ctx, recs, conf_path), rc, out);
if (! _iface_is_valid(&((*node)->iface))) {
_warn(ctx, "'%s' has invalid iSCSI interface configuration",
conf_path);
iscsi_node_free(*node);
*node = NULL;
/* We still treat this as pass(no error) */
goto out;
}
// Add extra properties
if (strchr((*node)->conn.address, '.')) {
(*node)->conn.is_ipv6 = false;
snprintf((*node)->portal, sizeof((*node)->portal)/sizeof(char),
"%s:%" PRIi32, (*node)->conn.address,
(*node)->conn.port);
} else {
(*node)->conn.is_ipv6 = true;
snprintf((*node)->portal, sizeof((*node)->portal)/sizeof(char),
"[%s]:%" PRIi32, (*node)->conn.address,
(*node)->conn.port);
}
out:
if (rc != LIBISCSI_OK) {
iscsi_node_free(*node);
*node = NULL;
}
free(conf_path);
_idbm_recs_free(recs);
return rc;
}