summaryrefslogtreecommitdiff
path: root/libopeniscsiusr
diff options
context:
space:
mode:
authorGris Ge <fge@redhat.com>2018-04-13 19:34:31 +0800
committerGris Ge <fge@redhat.com>2018-04-13 20:14:40 +0800
commit87ea50a1c3a97abd5d223bd7f8530109062f1239 (patch)
treef95aeceecaea9a6a76d33f1465a5df13894d7392 /libopeniscsiusr
parent1b0aa296a5b0d4729ca864cab8fb9a7981a06c6d (diff)
downloadopen-iscsi-87ea50a1c3a97abd5d223bd7f8530109062f1239.tar.gz
libopeniscsiusr: Add node query support
* New API functions: * iscsi_nodes_get() * iscsi_nodes_free() * And other node property query functions. * Replaced the node query functions used by `iscsiadm -m node` command which then generate identical output like old one. * Tested for back compatibility on old style node file: /etc/iscsi/nodes/<target_name>/<address>,<port> But only by renaming existing node file as I failed to find any version of iscsiadm are generating that style of node file. * API version increased to 0.2.0 due to API addition. Signed-off-by: Gris Ge <fge@redhat.com>
Diffstat (limited to 'libopeniscsiusr')
-rw-r--r--libopeniscsiusr/Makefile6
-rw-r--r--libopeniscsiusr/default.c108
-rw-r--r--libopeniscsiusr/default.h73
-rw-r--r--libopeniscsiusr/idbm.c393
-rw-r--r--libopeniscsiusr/idbm.h174
-rw-r--r--libopeniscsiusr/idbm_fields.h59
-rw-r--r--libopeniscsiusr/iface.c10
-rw-r--r--libopeniscsiusr/iface.h1
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h62
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h2
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h3
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_node.h186
-rw-r--r--libopeniscsiusr/node.c281
-rw-r--r--libopeniscsiusr/node.h52
-rw-r--r--libopeniscsiusr/rfc.h27
-rw-r--r--libopeniscsiusr/tests/test_node.c53
16 files changed, 1471 insertions, 19 deletions
diff --git a/libopeniscsiusr/Makefile b/libopeniscsiusr/Makefile
index 754ffb7..deddf3f 100644
--- a/libopeniscsiusr/Makefile
+++ b/libopeniscsiusr/Makefile
@@ -26,7 +26,7 @@ PKGCONF_DIR ?= $(LIB_DIR)/pkgconfig
LIBISCSI_USR_DIR=$(TOPDIR)/libopeniscsiusr
LIBISCSI_USR_VERSION_MAJOR=0
-LIBISCSI_USR_VERSION=0.1.1
+LIBISCSI_USR_VERSION=0.2.0
SONAME=$(LIBISCSI_USR_VERSION)
DEVLIB = libopeniscsiusr.so
LIBS = $(DEVLIB).$(SONAME)
@@ -36,10 +36,10 @@ HEADERS = libopeniscsiusr/libopeniscsiusr.h \
libopeniscsiusr/libopeniscsiusr_common.h \
libopeniscsiusr/libopeniscsiusr_session.h \
libopeniscsiusr/libopeniscsiusr_iface.h
-TESTS = tests/test_context tests/test_session tests/test_iface
+TESTS = tests/test_context tests/test_session tests/test_iface tests/test_node
EXTRA_MAN_FILES = libopeniscsiusr.h.3
-OBJS = context.o misc.o session.o sysfs.o iface.o idbm.o
+OBJS = context.o misc.o session.o sysfs.o iface.o idbm.o node.o default.o
CFLAGS ?= -O2 -g
CFLAGS += -Wall -Werror -Wextra -fvisibility=hidden -fPIC
diff --git a/libopeniscsiusr/default.c b/libopeniscsiusr/default.c
new file mode 100644
index 0000000..cd80e80
--- /dev/null
+++ b/libopeniscsiusr/default.c
@@ -0,0 +1,108 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#include <string.h>
+
+#include "libopeniscsiusr/libopeniscsiusr.h"
+#include "default.h"
+#include "iface.h"
+#include "node.h"
+
+#define CONFIG_DIGEST_NEVER 0
+
+static void default_session_op_cfg(struct iscsi_session_op_cfg *op_cfg)
+{
+ op_cfg->InitialR2T = 0;
+ op_cfg->ImmediateData = 1;
+ op_cfg->FirstBurstLength = DEF_INI_FIRST_BURST_LEN;
+ op_cfg->MaxBurstLength = DEF_INI_MAX_BURST_LEN;
+ op_cfg->DefaultTime2Wait = ISCSI_DEF_TIME2WAIT;
+ op_cfg->DefaultTime2Retain = 0;
+ op_cfg->MaxConnections = 1;
+ op_cfg->MaxOutstandingR2T = 1;
+ op_cfg->ERL = 0;
+ op_cfg->FastAbort = 1;
+}
+
+static void default_conn_op_cfg(struct iscsi_conn_op_cfg *op_cfg)
+{
+ op_cfg->MaxXmitDataSegmentLength = 0;
+ op_cfg->MaxRecvDataSegmentLength = DEF_INI_MAX_RECV_SEG_LEN;
+ op_cfg->HeaderDigest = DIGEST_NEVER;
+ op_cfg->DataDigest = DIGEST_NEVER;
+ op_cfg->IFMarker = 0;
+ op_cfg->OFMarker = 0;
+}
+
+/*
+ * default is to use tcp through whatever the network layer
+ * selects for us with the /etc/iscsi/initiatorname.iscsi iname.
+ */
+static void default_iface(struct iscsi_iface *iface)
+{
+ snprintf(iface->transport_name,
+ sizeof(iface->transport_name)/sizeof(char),
+ DEFAULT_TRANSPORT);
+
+ if (!strlen(iface->name))
+ snprintf(iface->name, sizeof(iface->name)/sizeof(char),
+ DEFAULT_IFACENAME);
+}
+
+void _default_node(struct iscsi_node *node)
+{
+ node->tpgt = PORTAL_GROUP_TAG_UNKNOWN;
+ node->disc_type = DISCOVERY_TYPE_STATIC;
+ node->leading_login = 0;
+ node->session.cmds_max = CMDS_MAX;
+ node->session.xmit_thread_priority = XMIT_THREAD_PRIORITY;
+ node->session.initial_cmdsn = 0;
+ node->session.queue_depth = QUEUE_DEPTH;
+ node->session.nr_sessions = 1;
+ node->session.initial_login_retry_max = DEF_INITIAL_LOGIN_RETRIES_MAX;
+ node->session.reopen_max = 32;
+ node->session.auth.authmethod = 0;
+ node->session.auth.password_length = 0;
+ node->session.auth.password_in_length = 0;
+ node->session.err_tmo.abort_timeout = DEF_ABORT_TIMEO;
+ node->session.err_tmo.lu_reset_timeout = DEF_LU_RESET_TIMEO;
+ node->session.err_tmo.tgt_reset_timeout = DEF_TGT_RESET_TIMEO;
+ node->session.err_tmo.host_reset_timeout = DEF_HOST_RESET_TIMEO;
+ node->session.tmo.replacement_timeout = DEF_REPLACEMENT_TIMEO;
+ node->session.se = NULL;
+ node->session.sid = 0;
+ node->session.multiple = 0;
+ node->session.scan = DEF_INITIAL_SCAN;
+
+ default_session_op_cfg(&node->session.op_cfg);
+
+ node->conn.startup = ISCSI_STARTUP_MANUAL;
+ node->conn.port = ISCSI_DEFAULT_PORT;
+ node->conn.tcp.window_size = TCP_WINDOW_SIZE;
+ node->conn.tcp.type_of_service = 0;
+ node->conn.tmo.login_timeout= DEF_LOGIN_TIMEO;
+ node->conn.tmo.logout_timeout= DEF_LOGOUT_TIMEO;
+ node->conn.tmo.auth_timeout = 45;
+ node->conn.tmo.noop_out_interval = DEF_NOOP_OUT_INTERVAL;
+ node->conn.tmo.noop_out_timeout = DEF_NOOP_OUT_TIMEO;
+
+ default_conn_op_cfg(&node->conn.op_cfg);
+
+ default_iface(&node->iface);
+}
diff --git a/libopeniscsiusr/default.h b/libopeniscsiusr/default.h
new file mode 100644
index 0000000..bb9e5fa
--- /dev/null
+++ b/libopeniscsiusr/default.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#ifndef __ISCSI_USR_DEFAULT_H__
+#define __ISCSI_USR_DEFAULT_H__
+
+#include "libopeniscsiusr/libopeniscsiusr_common.h"
+#include "rfc.h"
+#include "idbm.h"
+
+#define PORTAL_GROUP_TAG_UNKNOWN -1
+/* q depths */
+#define CMDS_MAX 128
+#define QUEUE_DEPTH 32
+
+/* system */
+#define XMIT_THREAD_PRIORITY -20
+
+/* login retries */
+#define DEF_INITIAL_LOGIN_RETRIES_MAX 4
+
+/* autoscan enabled */
+#define DEF_INITIAL_SCAN INIT_SCAN_AUTO
+
+/*
+ * Default initiator settings. These may not be the same as
+ * in the RFC. See libopeniscsiusr/libopeniscsiusr_rfc.h for those.
+ */
+/* timeouts in seconds */
+#define DEF_LOGIN_TIMEO 30
+#define DEF_LOGOUT_TIMEO 15
+#define DEF_NOOP_OUT_INTERVAL 5
+#define DEF_NOOP_OUT_TIMEO 5
+#define DEF_REPLACEMENT_TIMEO 120
+
+#define DEF_ABORT_TIMEO 15
+#define DEF_LU_RESET_TIMEO 30
+#define DEF_TGT_RESET_TIMEO 30
+#define DEF_HOST_RESET_TIMEO 60
+
+/* default window size */
+#define TCP_WINDOW_SIZE (512 * 1024)
+
+/* data and segment lengths in bytes */
+#define DEF_INI_FIRST_BURST_LEN 262144
+#define DEF_INI_MAX_BURST_LEN 16776192
+#define DEF_INI_MAX_RECV_SEG_LEN 262144
+
+#define DEFAULT_TRANSPORT "tcp"
+#define DEFAULT_IFACENAME "default"
+#define DEFAULT_NETDEV "default"
+#define DEFAULT_IPADDRESS "default"
+#define DEFAULT_HWADDRESS "default"
+
+void __DLL_LOCAL _default_node(struct iscsi_node *node);
+
+#endif /* End of __ISCSI_USR_DEFAULT_H__ */
diff --git a/libopeniscsiusr/idbm.c b/libopeniscsiusr/idbm.c
index a273fa8..b881eab 100644
--- a/libopeniscsiusr/idbm.c
+++ b/libopeniscsiusr/idbm.c
@@ -52,6 +52,7 @@
#include <limits.h>
#include <stdlib.h>
#include <ctype.h>
+#include <stdbool.h>
#include "libopeniscsiusr/libopeniscsiusr_common.h"
@@ -61,11 +62,17 @@
#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. */
@@ -141,6 +148,106 @@ do { \
_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,
@@ -153,6 +260,8 @@ struct idbm_rec {
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.
@@ -160,6 +269,8 @@ struct idbm_rec {
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;
@@ -245,13 +356,16 @@ static void _idbm_recs_free(struct idbm_rec* recs)
free(recs);
}
-static void _idbm_iface_rec_link(struct iscsi_iface *iface,
- struct idbm_rec *recs)
+static int _idbm_iface_rec_link(struct iscsi_iface *iface,
+ struct idbm_rec *recs, int num)
{
- int num = 0;
-
- _rec_str(IFACE_ISCSINAME, recs, iface, name, IDBM_SHOW, num,
- _CANNOT_MODIFY);
+ 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,
@@ -388,6 +502,7 @@ static void _idbm_iface_rec_link(struct iscsi_iface *iface,
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)
@@ -397,7 +512,7 @@ static void _idbm_recs_print(struct idbm_rec *recs, FILE *f, int show)
for (i = 0; i < MAX_KEYS; i++) {
if (recs[i].visible == IDBM_HIDE)
continue;
- if (!show && recs[i].visible == IDBM_MASKED) {
+ if (show == IDBM_MASKED && recs[i].visible == IDBM_MASKED) {
if (*(char*)recs[i].data) {
fprintf(f, "%s = ********\n", recs[i].name);
continue;
@@ -421,19 +536,33 @@ void _idbm_iface_print(struct iscsi_iface *iface, FILE *f)
if (recs == NULL)
return;
- _idbm_iface_rec_link(iface, recs);
+ _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];
@@ -477,7 +606,43 @@ setup_passwd_len:
_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,
@@ -677,7 +842,7 @@ int _idbm_iface_get(struct iscsi_context *ctx, const char *iface_name, struct
recs = _idbm_recs_alloc();
_alloc_null_check(ctx, recs, rc, out);
- _idbm_iface_rec_link(*iface, recs);
+ _idbm_iface_rec_link(*iface, recs, 0);
_good(_idbm_recs_read(ctx, recs, conf_path), rc, out);
@@ -706,3 +871,213 @@ 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_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[PATH_MAX];
+ 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
+ snprintf(conf_path, PATH_MAX, "%s/%s/%s", NODE_CONFIG_DIR,
+ target_name, portal);
+ else
+ snprintf(conf_path, PATH_MAX, "%s/%s/%s/%s", NODE_CONFIG_DIR,
+ target_name, portal, iface_name);
+
+ *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;
+ }
+ _idbm_recs_free(recs);
+ return rc;
+}
diff --git a/libopeniscsiusr/idbm.h b/libopeniscsiusr/idbm.h
index 31cbe6b..3fd0864 100644
--- a/libopeniscsiusr/idbm.h
+++ b/libopeniscsiusr/idbm.h
@@ -17,16 +17,25 @@
* Author: Gris Ge <fge@redhat.com>
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* For NI_MAXHOST */
+#endif
+
#ifndef __ISCSI_OPEN_USR_IDBM_H__
#define __ISCSI_OPEN_USR_IDBM_H__
#include <stdio.h>
#include <stdbool.h>
+#include <netdb.h>
#include "libopeniscsiusr/libopeniscsiusr_common.h"
#define ISCSI_CONFIG_ROOT "/etc/iscsi/"
#define IFACE_CONFIG_DIR ISCSI_CONFIG_ROOT"ifaces"
+#define AUTH_STR_MAX_LEN 256
+#define BOOT_NAME_MAXLEN 256
+#define IDBM_DUMP_SIZE 8192
+
struct __DLL_LOCAL idbm;
@@ -34,6 +43,164 @@ struct idbm {
int refs;
};
+enum iscsi_auth_method {
+ ISCSI_AUTH_METHOD_NONE,
+ ISCSI_AUTH_METHOD_CHAP,
+};
+
+enum iscsi_startup_type {
+ ISCSI_STARTUP_MANUAL,
+ ISCSI_STARTUP_AUTOMATIC,
+ ISCSI_STARTUP_ONBOOT,
+};
+
+enum discovery_type {
+ DISCOVERY_TYPE_SENDTARGETS,
+ DISCOVERY_TYPE_ISNS,
+ DISCOVERY_TYPE_OFFLOAD_SENDTARGETS,
+ DISCOVERY_TYPE_SLP,
+ DISCOVERY_TYPE_STATIC,
+ DISCOVERY_TYPE_FW,
+};
+
+enum leading_login_type {
+ LEADING_LOGIN_NO,
+ LEADING_LOGIN_YES,
+};
+
+enum init_scan_type {
+ INIT_SCAN_MANUAL,
+ INIT_SCAN_AUTO,
+};
+
+enum digest_type {
+ DIGEST_NEVER,
+ DIGEST_ALWAYS,
+ DIGEST_PREFER_ON,
+ DIGEST_PREFER_OFF,
+};
+
+/* all authentication-related options should be added to this structure.
+ * this structure is per-session, and can be configured
+ * by TargetName but not Subnet.
+ */
+struct iscsi_auth_config {
+ enum iscsi_auth_method authmethod;
+ char username[AUTH_STR_MAX_LEN];
+ unsigned char password[AUTH_STR_MAX_LEN];
+ uint32_t password_length;
+ char username_in[AUTH_STR_MAX_LEN];
+ unsigned char password_in[AUTH_STR_MAX_LEN];
+ uint32_t password_in_length;
+};
+
+/* all TCP options go in this structure.
+ * this structure is per-portal, and can be configured
+ * both by TargetName and Subnet.
+ */
+struct iscsi_tcp_config {
+ int64_t window_size;
+ int64_t type_of_service;
+ /* ^ try to set IP TOS bits */
+};
+
+/* all per-session timeouts go in this structure.
+ * this structure is per-session, and can be configured
+ * by TargetName but not by Subnet.
+ */
+struct iscsi_session_tmo_cfg {
+ int64_t replacement_timeout;
+};
+
+/* all error handling timeouts go in this structure.
+ * this structure is per-portal, and can be configured
+ * both by TargetName and Subnet.
+ */
+struct iscsi_error_tmo_cfg {
+ int64_t abort_timeout;
+ int64_t host_reset_timeout;
+ int64_t lu_reset_timeout;
+ int64_t tgt_reset_timeout;
+};
+
+/* all per-connection timeouts go in this structure.
+ * this structure is per-portal, and can be configured
+ * both by TargetName and Subnet.
+ */
+struct iscsi_conn_tmo_cfg {
+ int64_t login_timeout;
+ int64_t logout_timeout;
+ int64_t auth_timeout;
+ int64_t active_timeout;
+ int64_t noop_out_interval;
+ int64_t noop_out_timeout;
+};
+
+struct iscsi_conn_op_cfg {
+ int64_t MaxRecvDataSegmentLength;
+ int64_t MaxXmitDataSegmentLength;
+ enum digest_type HeaderDigest;
+ enum digest_type DataDigest;
+ bool IFMarker;
+ bool OFMarker;
+};
+
+struct iscsi_conn {
+ enum iscsi_startup_type startup;
+ char address[NI_MAXHOST];
+ int32_t port;
+ struct iscsi_tcp_config tcp;
+ struct iscsi_conn_tmo_cfg tmo;
+ struct iscsi_conn_op_cfg op_cfg;
+ bool is_ipv6;
+};
+
+/* all iSCSI operational params go in this structure.
+ * this structure is per-portal, and can be configured
+ * both by TargetName and Subnet.
+ */
+struct iscsi_session_op_cfg {
+ int64_t DataPDUInOrder;
+ int64_t DataSequenceInOrder;
+ int64_t protocol;
+ bool InitialR2T;
+ bool ImmediateData;
+ int64_t FirstBurstLength;
+ int64_t MaxBurstLength;
+ int64_t DefaultTime2Wait;
+ int64_t DefaultTime2Retain;
+ int64_t MaxConnections;
+ int64_t MaxOutstandingR2T;
+ int64_t ERL;
+ bool FastAbort;
+};
+
+struct iscsi_session_idbm {
+ uint32_t initial_cmdsn;
+ int64_t reopen_max;
+ int64_t initial_login_retry_max;
+ int64_t xmit_thread_priority;
+ uint16_t cmds_max;
+ uint16_t queue_depth;
+ int64_t nr_sessions;
+ enum init_scan_type scan;
+ struct iscsi_auth_config auth;
+ struct iscsi_session_tmo_cfg tmo;
+ struct iscsi_error_tmo_cfg err_tmo;
+ struct iscsi_session_op_cfg op_cfg;
+ struct iscsi_session *se;
+ uint32_t sid;
+ /*
+ * This is a flag passed to iscsid. If set, multiple sessions are
+ * allowed to be initiated on this record
+ */
+ unsigned char multiple;
+ char boot_root[BOOT_NAME_MAXLEN];
+ char boot_nic[BOOT_NAME_MAXLEN];
+ char boot_target[BOOT_NAME_MAXLEN];
+
+};
+
__DLL_LOCAL struct idbm *_idbm_new(void);
__DLL_LOCAL void _idbm_free(struct idbm *db);
__DLL_LOCAL int _idbm_lock(struct iscsi_context *ctx);
@@ -42,5 +209,12 @@ __DLL_LOCAL void _idbm_iface_print(struct iscsi_iface *iface, FILE *f);
__DLL_LOCAL int _idbm_iface_get(struct iscsi_context *ctx,
const char *iface_name,
struct iscsi_iface **iface);
+__DLL_LOCAL int _idbm_node_get(struct iscsi_context *ctx,
+ const char *target_name,
+ const char *portal,
+ const char *iface_name,
+ struct iscsi_node **node);
+__DLL_LOCAL void _idbm_node_print(struct iscsi_node *node, FILE *f,
+ bool show_secret);
#endif /* End of __ISCSI_OPEN_USR_IDBM_H__ */
diff --git a/libopeniscsiusr/idbm_fields.h b/libopeniscsiusr/idbm_fields.h
index ce30ad9..090c4f9 100644
--- a/libopeniscsiusr/idbm_fields.h
+++ b/libopeniscsiusr/idbm_fields.h
@@ -95,4 +95,63 @@
#define IFACE_DISCOVERY_AUTH "iface.discovery_auth"
#define IFACE_DISCOVERY_LOGOUT "iface.discovery_logout"
+/* node fields */
+#define NODE_NAME "node.name"
+#define NODE_TPGT "node.tpgt"
+#define NODE_STARTUP "node.startup"
+#define NODE_LEADING_LOGIN "node.leading_login"
+#define NODE_DISC_ADDR "node.discovery_address"
+#define NODE_DISC_PORT "node.discovery_port"
+#define NODE_DISC_TYPE "node.discovery_type"
+#define NODE_BOOT_LUN "node.boot_lun"
+
+/* session fields */
+#define SESSION_INIT_CMDSN "node.session.initial_cmdsn"
+#define SESSION_INIT_LOGIN_RETRY "node.session.initial_login_retry_max"
+#define SESSION_CMDS_MAX "node.session.cmds_max"
+#define SESSION_XMIT_THREAD_PRIORITY "node.session.xmit_thread_priority"
+#define SESSION_QDEPTH "node.session.queue_depth"
+#define SESSION_NR_SESSIONS "node.session.nr_sessions"
+#define SESSION_AUTH_METHOD "node.session.auth.authmethod"
+#define SESSION_USERNAME "node.session.auth.username"
+#define SESSION_PASSWORD "node.session.auth.password"
+#define SESSION_PASSWORD_LEN "node.session.auth.password_length"
+#define SESSION_USERNAME_IN "node.session.auth.username_in"
+#define SESSION_PASSWORD_IN "node.session.auth.password_in"
+#define SESSION_PASSWORD_IN_LEN "node.session.auth.password_in_length"
+#define SESSION_REPLACEMENT_TMO "node.session.timeo.replacement_timeout"
+#define SESSION_ABORT_TMO "node.session.err_timeo.abort_timeout"
+#define SESSION_LU_RESET_TMO "node.session.err_timeo.lu_reset_timeout"
+#define SESSION_TGT_RESET_TMO "node.session.err_timeo.tgt_reset_timeout"
+#define SESSION_HOST_RESET_TMO "node.session.err_timeo.host_reset_timeout"
+#define SESSION_FAST_ABORT "node.session.iscsi.FastAbort"
+#define SESSION_INITIAL_R2T "node.session.iscsi.InitialR2T"
+#define SESSION_IMM_DATA "node.session.iscsi.ImmediateData"
+#define SESSION_FIRST_BURST "node.session.iscsi.FirstBurstLength"
+#define SESSION_MAX_BURST "node.session.iscsi.MaxBurstLength"
+#define SESSION_DEF_TIME2RETAIN "node.session.iscsi.DefaultTime2Retain"
+#define SESSION_DEF_TIME2WAIT "node.session.iscsi.DefaultTime2Wait"
+#define SESSION_MAX_CONNS "node.session.iscsi.MaxConnections"
+#define SESSION_MAX_R2T "node.session.iscsi.MaxOutstandingR2T"
+#define SESSION_ERL "node.session.iscsi.ERL"
+#define SESSION_SCAN "node.session.scan"
+
+/* connections fields */
+#define CONN_ADDR "node.conn[0].address"
+#define CONN_PORT "node.conn[0].port"
+#define CONN_STARTUP "node.conn[0].startup"
+#define CONN_WINDOW_SIZE "node.conn[0].tcp.window_size"
+#define CONN_SERVICE_TYPE "node.conn[0].tcp.type_of_service"
+#define CONN_LOGOUT_TMO "node.conn[0].timeo.logout_timeout"
+#define CONN_LOGIN_TMO "node.conn[0].timeo.login_timeout"
+#define CONN_AUTH_TMO "node.conn[0].timeo.auth_timeout"
+#define CONN_NOP_INT "node.conn[0].timeo.noop_out_interval"
+#define CONN_NOP_TMO "node.conn[0].timeo.noop_out_timeout"
+#define CONN_MAX_XMIT_DLEN "node.conn[0].iscsi.MaxXmitDataSegmentLength"
+#define CONN_MAX_RECV_DLEN "node.conn[0].iscsi.MaxRecvDataSegmentLength"
+#define CONN_HDR_DIGEST "node.conn[0].iscsi.HeaderDigest"
+#define CONN_DATA_DIGEST "node.conn[0].iscsi.DataDigest"
+#define CONN_IFMARKER "node.conn[0].iscsi.IFMarker"
+#define CONN_OFMARKER "node.conn[0].iscsi.OFMarker"
+
#endif /* End of __ISCSI_OPEN_USER_IDBM_FIELDS_H */
diff --git a/libopeniscsiusr/iface.c b/libopeniscsiusr/iface.c
index b6fb987..903bcd8 100644
--- a/libopeniscsiusr/iface.c
+++ b/libopeniscsiusr/iface.c
@@ -44,13 +44,9 @@
#include "iface.h"
#include "context.h"
#include "idbm.h"
+#include "default.h"
-#define DEFAULT_IFACENAME "default"
-#define DEFAULT_NETDEV "default"
-#define DEFAULT_IPADDRESS "default"
-#define DEFAULT_HWADDRESS "default"
#define ISCSIUIO_PATH "/sbin/iscsiuio"
-#define _IFACE_DUMP_SIZE 8192
struct _iscsi_net_drv {
const char *net_driver_name; // Ethernet driver.
@@ -846,11 +842,11 @@ const char *iscsi_iface_dump_config(struct iscsi_iface *iface)
assert(iface != NULL);
- buff = calloc(1, _IFACE_DUMP_SIZE);
+ buff = calloc(1, IDBM_DUMP_SIZE);
if (buff == NULL)
return NULL;
- f = fmemopen(buff, _IFACE_DUMP_SIZE - 1, "w");
+ f = fmemopen(buff, IDBM_DUMP_SIZE - 1, "w");
if (f == NULL) {
free(buff);
return NULL;
diff --git a/libopeniscsiusr/iface.h b/libopeniscsiusr/iface.h
index 8a1e670..af3d9c7 100644
--- a/libopeniscsiusr/iface.h
+++ b/libopeniscsiusr/iface.h
@@ -23,6 +23,7 @@
#include "libopeniscsiusr/libopeniscsiusr_common.h"
#include <stdint.h>
#include <netdb.h>
+#include <net/if.h>
#define VALUE_MAXVAL 256 /* the maximum length of 223 bytes in the RFC. */
/* ^ VALUE_MAXVAL is copied from usr/idbm.h
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
index 7b5a280..4395902 100644
--- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
@@ -30,6 +30,7 @@ extern "C" {
#include "libopeniscsiusr_common.h"
#include "libopeniscsiusr_session.h"
#include "libopeniscsiusr_iface.h"
+#include "libopeniscsiusr_node.h"
/**
* iscsi_log_priority_str() - Convert log priority to string.
@@ -480,6 +481,67 @@ __DLL_EXPORT int iscsi_iface_get(struct iscsi_context *ctx,
*/
__DLL_EXPORT void iscsi_iface_free(struct iscsi_iface *iface);
+/**
+ * iscsi_nodes_get() - Retrieve all iSCSI nodes.
+ *
+ * Retrieves all iSCSI nodes. For the properties of 'struct iscsi_node',
+ * please refer to the functions defined in 'libopeniscsiusr_node.h' file.
+ * The returned results contains iSCSI nodes configured in "/etc/iscsi/nodes/".
+ * Illegal configuration file will be skipped and warned.
+ * The returned 'struct iscsi_node' pointer array is sorted by target name,
+ * connection address, connection port and interface.
+ *
+ * @ctx:
+ * Pointer of 'struct iscsi_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @nodes:
+ * Output pointer of 'struct iscsi_node' pointer array. Its memory
+ * should be freed by iscsi_nodes_free().
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @node_count:
+ * Output pointer of uint32_t. Will store the size of
+ * 'struct iscsi_node' pointer array.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * LIBISCSI_OK
+ *
+ * * LIBISCSI_ERR_BUG
+ *
+ * * LIBISCSI_ERR_NOMEM
+ *
+ * * LIBISCSI_ERR_ACCESS
+ *
+ * * LIBISCSI_ERR_SYSFS_LOOKUP
+ *
+ * * LIBISCSI_ERR_IDBM
+ *
+ * Error number could be converted to string by iscsi_strerror().
+ */
+__DLL_EXPORT int iscsi_nodes_get(struct iscsi_context *ctx,
+ struct iscsi_node ***nodes,
+ uint32_t *node_count);
+
+/**
+ * iscsi_nodes_free() - Free the memory of 'struct iscsi_node' pointer
+ * array
+ *
+ * Free the memory of 'iscsi_node' pointer array generated by
+ * 'iscsi_nodes_get()'.
+ * If provided 'nodes' pointer is NULL or 'node_count' is 0, do nothing.
+ *
+ * @nodes:
+ * Pointer of 'struct iscsi_node' pointer array.
+ * @node_count:
+ * uint32_t, the size of 'struct iscsi_node' pointer array.
+ *
+ * Return:
+ * void
+ */
+__DLL_EXPORT void iscsi_nodes_free(struct iscsi_node **nodes,
+ uint32_t node_count);
+
#ifdef __cplusplus
} /* End of extern "C" */
#endif
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
index 334cc4e..7ae2906 100644
--- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
@@ -76,4 +76,6 @@ struct __DLL_EXPORT iscsi_session;
struct __DLL_EXPORT iscsi_iface;
+struct __DLL_EXPORT iscsi_node;
+
#endif /* End of _LIB_OPEN_ISCSI_USR_COMMON_H_ */
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h
index 23573bf..a1a2552 100644
--- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h
@@ -20,6 +20,9 @@
#ifndef _LIB_OPEN_ISCSI_USR_IFACE_H_
#define _LIB_OPEN_ISCSI_USR_IFACE_H_
+#include <stdint.h>
+#include <stdbool.h>
+
#include "libopeniscsiusr_common.h"
/**
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_node.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_node.h
new file mode 100644
index 0000000..e093d5a
--- /dev/null
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_node.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#ifndef _LIB_OPEN_ISCSI_USR_NODE_H_
+#define _LIB_OPEN_ISCSI_USR_NODE_H_
+
+#include "libopeniscsiusr_common.h"
+
+/**
+ * iscsi_node_dump_config() - Dump all configurations of specified iSCSI
+ * node.
+ *
+ * Dump all configurations of specified iSCSI node. Will skip empty
+ * configuration.
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @show_secret:
+ * Whether show CHAP secret. If set as false, will show password as
+ * "********"
+ *
+ * Return:
+ * const char *.
+ * Need to free this memory by free().
+ */
+__DLL_EXPORT const char *iscsi_node_dump_config(struct iscsi_node *node,
+ bool show_secret);
+
+/**
+ * iscsi_node_dump_config() - Print all configurations of specified iSCSI
+ * node to STDOUT.
+ *
+ * Print all configurations of specified iSCSI node.
+ * For empty configuration, it will be shown as "name = <empty>".
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @show_secret:
+ * Whether show CHAP secret. If set as false, will show password as
+ * "********"
+ *
+ * Return:
+ * void
+ */
+__DLL_EXPORT void iscsi_node_print_config(struct iscsi_node *node,
+ bool show_secret);
+
+/**
+ * iscsi_node_target_name_get() - Retrieve target name of specified iSCSI node.
+ *
+ * Retrieve the target name of specified iSCSI node.
+ * Examples: "iqn.2003-01.org.linux-iscsi.org:iscsi-targetcli"
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_node_free() or iscsi_nodes_free().
+ */
+__DLL_EXPORT const char *iscsi_node_target_name_get
+ (struct iscsi_node *node);
+
+/**
+ * iscsi_node_conn_is_ipv6() - Check whether specified node is using ipv6
+ * connection.
+ *
+ * Check whether specified node is using ipv6 connection.
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * bool
+ */
+__DLL_EXPORT bool iscsi_node_conn_is_ipv6(struct iscsi_node *node);
+
+/**
+ * iscsi_node_conn_address_get() - Retrieve connection address of specified
+ * iSCSI node.
+ *
+ * Retrieve the iscsi connection target address of specified iSCSI node.
+ * Examples: "192.168.1.1"
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_node_free() or iscsi_nodes_free().
+ */
+__DLL_EXPORT const char *iscsi_node_conn_address_get(struct iscsi_node *node);
+
+/**
+ * iscsi_node_conn_port_get() - Retrieve connection port of specified iSCSI
+ * node.
+ *
+ * Retrieve the iscsi connection target port of specified iSCSI node.
+ * Examples: "3260"
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * uint32_t
+ */
+__DLL_EXPORT uint32_t iscsi_node_conn_port_get(struct iscsi_node *node);
+
+/**
+ * iscsi_node_portal_get() - Retrieve connection portal of specified
+ * iSCSI node.
+ *
+ * Retrieve the iscsi connection target portal of specified iSCSI node.
+ * Just a combination of iscsi_node_conn_address_get() and
+ * iscsi_node_conn_port_get().
+ * Examples: "192.168.1.1:3260" and "[::1]:3260"
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_node_free() or iscsi_nodes_free().
+ */
+__DLL_EXPORT const char *iscsi_node_portal_get(struct iscsi_node *node);
+
+/**
+ * iscsi_node_tpgt_get() - Retrieve target portal group tag of specified
+ * iSCSI node.
+ *
+ * Retrieve the target portal group tag of specified iSCSI node.
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int32_t. -1 for unknown.
+ */
+__DLL_EXPORT int32_t iscsi_node_tpgt_get(struct iscsi_node *node);
+
+/**
+ * iscsi_node_iface_name_get() - Retrieve interface name of specified iSCSI
+ * node.
+ *
+ * Retrieve the interface name of specified iSCSI node.
+ * Examples: "default" for iscsi tcp interface.
+ *
+ * @node:
+ * Pointer of 'struct iscsi_node'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_node_free() or iscsi_nodes_free().
+ */
+__DLL_EXPORT const char *iscsi_node_iface_name_get(struct iscsi_node *node);
+
+#endif /* End of _LIB_OPEN_ISCSI_USR_NODE_H_ */
diff --git a/libopeniscsiusr/node.c b/libopeniscsiusr/node.c
new file mode 100644
index 0000000..e82bb0d
--- /dev/null
+++ b/libopeniscsiusr/node.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+/* ^ For strerror_r() */
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "libopeniscsiusr/libopeniscsiusr.h"
+#include "misc.h"
+#include "node.h"
+
+/* ptr is both input and output pointer.
+ * count is both input and output pointer.
+ * When success, both count and ptr will be updated.
+ * If fail, return LIBISCSI_ERR_NOMEM and no touch to old memory.
+ */
+static int _grow_node_array(struct iscsi_context *ctx,
+ struct iscsi_node ***nodes, uint32_t *count)
+{
+ int rc = LIBISCSI_OK;
+ struct iscsi_node **tmp = NULL;
+ uint32_t i = 0;
+
+ _debug(ctx, "Growing node array from size %" PRIu32 " to %" PRIu32,
+ *count, *count * 2);
+
+ tmp = realloc(*nodes, *count * 2 * sizeof(struct iscsi_node *));
+ _alloc_null_check(ctx, tmp, rc, out);
+ for (i = *count; i < *count * 2; ++i)
+ tmp[i] = NULL;
+
+ *count *= 2;
+ *nodes = tmp;
+
+out:
+ return rc;
+}
+
+static int nodes_append(struct iscsi_context *ctx, struct iscsi_node ***nodes,
+ uint32_t *real_node_count, uint32_t *array_size,
+ struct iscsi_node *node)
+{
+ int rc = LIBISCSI_OK;
+ if (*real_node_count >= *array_size)
+ _good(_grow_node_array(ctx, nodes, array_size), rc, out);
+
+ (*nodes)[(*real_node_count)++] = node;
+
+out:
+ return rc;
+}
+
+int iscsi_nodes_get(struct iscsi_context *ctx, struct iscsi_node ***nodes,
+ uint32_t *node_count)
+{
+ int rc = LIBISCSI_OK;
+ struct dirent **namelist = NULL;
+ int n = 0;
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ struct iscsi_node *node = NULL;
+ uint32_t real_node_count = 0;
+ const char *target_name = NULL;
+ const char *portal = NULL;
+ const char *iface_name = NULL;
+ struct dirent **namelist_portals = NULL;
+ int p = 0;
+ struct dirent **namelist_ifaces = NULL;
+ int f = 0;
+ char path[PATH_MAX];
+ struct stat path_stat;
+ char strerr_buff[_STRERR_BUFF_LEN];
+
+ assert(ctx != NULL);
+ assert(nodes != NULL);
+ assert(node_count != NULL);
+
+ *nodes = NULL;
+ *node_count = 0;
+
+ _good(_idbm_lock(ctx), rc, out);
+
+ _good(_scandir(ctx, NODE_CONFIG_DIR, &namelist, &n), rc, out);
+ _debug(ctx, "Got %d target from %s nodes folder", n, NODE_CONFIG_DIR);
+ *node_count = n & UINT32_MAX;
+ *nodes = (struct iscsi_node **) calloc(*node_count,
+ sizeof(struct iscsi_node *));
+ _alloc_null_check(ctx, *nodes, rc, out);
+
+ // New style of nodes folder:
+ // <target_name>/<address>,<port>,<tpgt>/<iface_name>
+ // Old style of nodes folder:
+ // <target_name>/<address>,<port>
+
+ for (i = 0; i < n; ++i) {
+ target_name = namelist[i]->d_name;
+ snprintf(path, sizeof(path)/sizeof(char),
+ "%s/%s", NODE_CONFIG_DIR, target_name);
+ _good(_scandir(ctx, path, &namelist_portals, &p), rc, out);
+ _debug(ctx, "Got %d portals from %s folder", p, path);
+ for (j = 0; j < p; ++j) {
+ portal = namelist_portals[j]->d_name;
+ snprintf(path, sizeof(path)/sizeof(char),
+ "%s/%s/%s", NODE_CONFIG_DIR, target_name,
+ portal);
+ if (stat(path, &path_stat) != 0) {
+ _warn(ctx, "Cannot stat path '%s': %d, %s",
+ path, errno,
+ _strerror(errno, strerr_buff));
+ continue;
+ }
+ if (S_ISREG(path_stat.st_mode)) {
+ // Old style of node
+ _good(_idbm_node_get(ctx, target_name, portal,
+ NULL, &node),
+ rc, out);
+ _good(nodes_append(ctx, nodes,
+ &real_node_count,
+ node_count, node),
+ rc, out);
+ continue;
+ }
+ if (! S_ISDIR(path_stat.st_mode)) {
+ _warn(ctx, "Invalid iSCSI node configuration "
+ "file %s, it should be a file or "
+ "directory.", path);
+ rc = LIBISCSI_ERR_IDBM;
+ goto out;
+ }
+ _good(_scandir(ctx, path, &namelist_ifaces, &f), rc,
+ out);
+ _debug(ctx, "Got %d ifaces from %s folder", f, path);
+ for (k = 0; k < f; ++k) {
+ iface_name = namelist_ifaces[k]->d_name;
+ _good(_idbm_node_get(ctx, target_name, portal,
+ iface_name, &node),
+ rc, out);
+ _good(nodes_append(ctx, nodes,
+ &real_node_count,
+ node_count, node),
+ rc, out);
+ }
+ _scandir_free(namelist_ifaces, f);
+ namelist_ifaces = NULL;
+ f = 0;
+ }
+ _scandir_free(namelist_portals, p);
+ namelist_portals = NULL;
+ p = 0;
+ }
+
+ *node_count = real_node_count;
+
+out:
+ _scandir_free(namelist, n);
+ _scandir_free(namelist_portals, p);
+ _scandir_free(namelist_ifaces, f);
+ _idbm_unlock(ctx);
+ if (rc != LIBISCSI_OK) {
+ iscsi_nodes_free(*nodes, *node_count);
+ *nodes = NULL;
+ *node_count = 0;
+ }
+ return rc;
+}
+
+void iscsi_nodes_free(struct iscsi_node **nodes, uint32_t node_count)
+{
+ uint32_t i = 0;
+
+ if ((nodes == NULL) || (node_count == 0))
+ return;
+
+ for (i = 0; i < node_count; ++i)
+ iscsi_node_free(nodes[i]);
+ free (nodes);
+}
+
+void iscsi_node_free(struct iscsi_node *node)
+{
+ free(node);
+}
+
+const char *iscsi_node_dump_config(struct iscsi_node *node, bool show_secret)
+{
+ FILE *f = NULL;
+ char *buff = NULL;
+
+ assert(node != NULL);
+
+ buff = calloc(1, IDBM_DUMP_SIZE);
+ if (buff == NULL)
+ return NULL;
+
+ f = fmemopen(buff, IDBM_DUMP_SIZE - 1, "w");
+ if (f == NULL) {
+ free(buff);
+ return NULL;
+ }
+
+ _idbm_node_print(node, f, show_secret);
+
+ fclose(f);
+
+ return buff;
+}
+
+void iscsi_node_print_config(struct iscsi_node *node, bool show_secret)
+{
+ assert(node != NULL);
+ _idbm_node_print(node, stdout, show_secret);
+}
+
+// TODO(Gris Ge): Convert below duplicated codes to macros.
+bool iscsi_node_conn_is_ipv6(struct iscsi_node *node)
+{
+ assert(node != NULL);
+ return node->conn.is_ipv6;
+}
+
+const char *iscsi_node_conn_address_get(struct iscsi_node *node)
+{
+ assert(node != NULL);
+ return node->conn.address;
+}
+
+uint32_t iscsi_node_conn_port_get(struct iscsi_node *node)
+{
+ assert(node != NULL);
+ return node->conn.port;
+}
+
+int32_t iscsi_node_tpgt_get(struct iscsi_node *node)
+{
+ assert(node != NULL);
+ return node->tpgt;
+}
+
+const char *iscsi_node_target_name_get(struct iscsi_node *node)
+{
+ assert(node != NULL);
+ return node->target_name;
+}
+
+const char *iscsi_node_iface_name_get(struct iscsi_node *node)
+{
+ assert(node != NULL);
+ return node->iface.name;
+}
+
+const char *iscsi_node_portal_get(struct iscsi_node *node)
+{
+ assert(node != NULL);
+ return node->portal;
+}
diff --git a/libopeniscsiusr/node.h b/libopeniscsiusr/node.h
new file mode 100644
index 0000000..39e07b3
--- /dev/null
+++ b/libopeniscsiusr/node.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* For NI_MAXHOST */
+#endif
+
+#ifndef __ISCSI_USR_NODE_H__
+#define __ISCSI_USR_NODE_H__
+
+#include <netdb.h>
+#include <stdint.h>
+
+#include "idbm.h"
+#include "iface.h"
+
+struct iscsi_node {
+ char target_name[TARGET_NAME_MAXLEN];
+ int32_t tpgt;
+ enum iscsi_startup_type startup;
+ enum leading_login_type leading_login;
+ struct iscsi_session_idbm session;
+ struct iscsi_conn conn;
+ struct iscsi_iface iface;
+ enum discovery_type disc_type;
+ char disc_address[NI_MAXHOST];
+ int32_t disc_port;
+ char portal[NI_MAXHOST * 2];
+};
+
+#define NODE_CONFIG_DIR ISCSI_CONFIG_ROOT"nodes"
+
+/* Might be public in the future */
+__DLL_LOCAL void iscsi_node_free(struct iscsi_node *node);
+
+#endif /* End of __ISCSI_USR_NODE_H__ */
diff --git a/libopeniscsiusr/rfc.h b/libopeniscsiusr/rfc.h
new file mode 100644
index 0000000..8f60652
--- /dev/null
+++ b/libopeniscsiusr/rfc.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+
+#ifndef _LIB_OPEN_ISCSI_USR_RFC_H_
+#define _LIB_OPEN_ISCSI_USR_RFC_H_
+
+#define ISCSI_DEFAULT_PORT 3260
+#define ISCSI_DEF_TIME2WAIT 2
+
+#endif /* End of _LIB_OPEN_ISCSI_USR_RFC_H_ */
diff --git a/libopeniscsiusr/tests/test_node.c b/libopeniscsiusr/tests/test_node.c
new file mode 100644
index 0000000..7ff7e9b
--- /dev/null
+++ b/libopeniscsiusr/tests/test_node.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <inttypes.h>
+
+#include <libopeniscsiusr/libopeniscsiusr.h>
+
+int main()
+{
+ struct iscsi_context *ctx = NULL;
+ struct iscsi_node **nodes = NULL;
+ uint32_t node_count = 0;
+ uint32_t i = 0;
+ const char *dump = NULL;
+ int rc = EXIT_SUCCESS;
+
+ ctx = iscsi_context_new();
+ iscsi_context_log_priority_set(ctx, LIBISCSI_LOG_PRIORITY_DEBUG);
+
+ if (iscsi_nodes_get(ctx, &nodes, &node_count) != LIBISCSI_OK) {
+ printf("FAILED\n");
+ rc = EXIT_FAILURE;
+ } else {
+ printf("\nGot %" PRIu32 " iSCSI nodes\n", node_count);
+ for (i = 0; i < node_count; ++i) {
+ dump = iscsi_node_dump_config(nodes[i], true);
+ assert(dump != NULL);
+ free((void *) dump);
+ iscsi_node_print_config(nodes[i], true);
+ }
+ iscsi_nodes_free(nodes, node_count);
+ }
+ iscsi_context_free(ctx);
+ exit(rc);
+}