diff options
author | Gris Ge <fge@redhat.com> | 2018-04-13 19:34:31 +0800 |
---|---|---|
committer | Gris Ge <fge@redhat.com> | 2018-04-13 20:14:40 +0800 |
commit | 87ea50a1c3a97abd5d223bd7f8530109062f1239 (patch) | |
tree | f95aeceecaea9a6a76d33f1465a5df13894d7392 /libopeniscsiusr | |
parent | 1b0aa296a5b0d4729ca864cab8fb9a7981a06c6d (diff) | |
download | open-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/Makefile | 6 | ||||
-rw-r--r-- | libopeniscsiusr/default.c | 108 | ||||
-rw-r--r-- | libopeniscsiusr/default.h | 73 | ||||
-rw-r--r-- | libopeniscsiusr/idbm.c | 393 | ||||
-rw-r--r-- | libopeniscsiusr/idbm.h | 174 | ||||
-rw-r--r-- | libopeniscsiusr/idbm_fields.h | 59 | ||||
-rw-r--r-- | libopeniscsiusr/iface.c | 10 | ||||
-rw-r--r-- | libopeniscsiusr/iface.h | 1 | ||||
-rw-r--r-- | libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h | 62 | ||||
-rw-r--r-- | libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h | 2 | ||||
-rw-r--r-- | libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h | 3 | ||||
-rw-r--r-- | libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_node.h | 186 | ||||
-rw-r--r-- | libopeniscsiusr/node.c | 281 | ||||
-rw-r--r-- | libopeniscsiusr/node.h | 52 | ||||
-rw-r--r-- | libopeniscsiusr/rfc.h | 27 | ||||
-rw-r--r-- | libopeniscsiusr/tests/test_node.c | 53 |
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); +} |