diff options
Diffstat (limited to 'libopeniscsiusr')
-rw-r--r-- | libopeniscsiusr/Makefile | 6 | ||||
-rw-r--r-- | libopeniscsiusr/TODO | 3 | ||||
-rw-r--r-- | libopeniscsiusr/context.c | 7 | ||||
-rw-r--r-- | libopeniscsiusr/context.h | 5 | ||||
-rw-r--r-- | libopeniscsiusr/idbm.c | 708 | ||||
-rw-r--r-- | libopeniscsiusr/idbm.h | 46 | ||||
-rw-r--r-- | libopeniscsiusr/idbm_fields.h | 98 | ||||
-rw-r--r-- | libopeniscsiusr/iface.c | 934 | ||||
-rw-r--r-- | libopeniscsiusr/iface.h | 120 | ||||
-rw-r--r-- | libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h | 155 | ||||
-rw-r--r-- | libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h | 9 | ||||
-rw-r--r-- | libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h | 53 | ||||
-rw-r--r-- | libopeniscsiusr/misc.c | 207 | ||||
-rw-r--r-- | libopeniscsiusr/misc.h | 29 | ||||
-rw-r--r-- | libopeniscsiusr/session.c | 103 | ||||
-rw-r--r-- | libopeniscsiusr/sysfs.c | 266 | ||||
-rw-r--r-- | libopeniscsiusr/sysfs.h | 37 | ||||
-rw-r--r-- | libopeniscsiusr/tests/test_iface.c | 112 | ||||
-rw-r--r-- | libopeniscsiusr/version.h | 30 |
19 files changed, 2616 insertions, 312 deletions
diff --git a/libopeniscsiusr/Makefile b/libopeniscsiusr/Makefile index 8b9b523..05a77da 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.0 +LIBISCSI_USR_VERSION=0.1.1 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 = tests/test_context tests/test_session tests/test_iface EXTRA_MAN_FILES = libopeniscsiusr.h.3 -OBJS = context.o misc.o session.o sysfs.o iface.o +OBJS = context.o misc.o session.o sysfs.o iface.o idbm.o CFLAGS ?= -O2 -g CFLAGS += -Wall -Werror -Wextra -fvisibility=hidden -fPIC diff --git a/libopeniscsiusr/TODO b/libopeniscsiusr/TODO new file mode 100644 index 0000000..21f3893 --- /dev/null +++ b/libopeniscsiusr/TODO @@ -0,0 +1,3 @@ +TODO: + * Add more debug message. + * Support of SYSFS_PATH environment. diff --git a/libopeniscsiusr/context.c b/libopeniscsiusr/context.c index 4c323b3..fe92155 100644 --- a/libopeniscsiusr/context.c +++ b/libopeniscsiusr/context.c @@ -44,12 +44,19 @@ struct iscsi_context *iscsi_context_new(void) ctx->log_func = _iscsi_log_stderr; ctx->log_priority = LIBISCSI_LOG_PRIORITY_DEFAULT; ctx->userdata = NULL; + ctx->db = _idbm_new(); + if (ctx->db == NULL) { + free(ctx); + return NULL; + } return ctx; } void iscsi_context_free(struct iscsi_context *ctx) { + if (ctx != NULL) + _idbm_free(ctx->db); free(ctx); } diff --git a/libopeniscsiusr/context.h b/libopeniscsiusr/context.h index df113f2..f37c90c 100644 --- a/libopeniscsiusr/context.h +++ b/libopeniscsiusr/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Red Hat, Inc. + * 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 @@ -19,12 +19,15 @@ #ifndef __ISCSI_USR_CONTEXT_H__ #define __ISCSI_USR_CONTEXT_H__ +#include "idbm.h" + struct iscsi_context { void (*log_func)(struct iscsi_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, va_list args); int log_priority; void *userdata; + struct idbm *db; }; diff --git a/libopeniscsiusr/idbm.c b/libopeniscsiusr/idbm.c new file mode 100644 index 0000000..a273fa8 --- /dev/null +++ b/libopeniscsiusr/idbm.c @@ -0,0 +1,708 @@ +/* + * 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> + */ + +/* The code below is modified from usr/idbm.c which licensed like below: + * + * iSCSI Discovery Database Library + * + * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman + * Copyright (C) 2006 Mike Christie + * Copyright (C) 2006 Red Hat, Inc. All rights reserved. + * maintained by open-iscsi@@googlegroups.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* For strerror_r() */ +#endif + +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <inttypes.h> +#include <limits.h> +#include <stdlib.h> +#include <ctype.h> + +#include "libopeniscsiusr/libopeniscsiusr_common.h" + +#include "context.h" +#include "idbm.h" +#include "misc.h" +#include "idbm_fields.h" +#include "iface.h" +#include "version.h" + +#define TYPE_STR 2 +#define TYPE_UINT8 3 +#define TYPE_UINT16 4 +#define TYPE_UINT32 5 +#define MAX_KEYS 256 /* number of keys total(including CNX_MAX) */ +#define NAME_MAXVAL 128 /* the maximum length of key name */ +#define VALUE_MAXVAL 256 /* the maximum length of 223 bytes in the RFC. */ +/* ^ MAX_KEYS, NAME_MAXVAL and VALUE_MAXVAL are copied from usr/idbm.h + * The RFC 3720 only said: + * If not otherwise specified, the maximum length of a simple-value (not + * its encoded representation) is 255 bytes, not including the delimiter + * (comma or zero byte). + */ + +#define OPTS_MAXVAL 8 + +#define IDBM_HIDE 0 /* Hide parameter when print. */ +#define IDBM_SHOW 1 /* Show parameter when print. */ +#define IDBM_MASKED 2 /* Show "stars" instead of real value when print */ + +#define ISCSI_BEGIN_REC "# BEGIN RECORD "ISCSI_VERSION_STR +#define ISCSI_END_REC "# END RECORD" + +#ifndef LOCK_DIR +#define LOCK_DIR "/var/lock/iscsi" +#endif +#define LOCK_FILE LOCK_DIR"/lock" +#define LOCK_WRITE_FILE LOCK_DIR"/lock.write" + +#define _rec_str(_key, _recs, _org, _name, _show, _n, _mod) \ +do { \ + _recs[_n].type = TYPE_STR; \ + _strncpy(_recs[_n].name, _key, NAME_MAXVAL); \ + if (strlen((char*)_org->_name)) \ + _strncpy((char*)_recs[_n].value, (char*)_org->_name, \ + VALUE_MAXVAL); \ + _recs[_n].data = &_org->_name; \ + _recs[_n].data_len = sizeof(_org->_name); \ + _recs[_n].visible = _show; \ + _recs[_n].can_modify = _mod; \ + _n++; \ +} while(0) + +#define _rec_uint8(_key, _recs, _org, _name, _show, _n, _mod) \ +do { \ + _recs[_n].type = TYPE_UINT8; \ + _strncpy(_recs[_n].name, _key, NAME_MAXVAL); \ + snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu8, _org->_name); \ + _recs[_n].data = &_org->_name; \ + _recs[_n].data_len = sizeof(_org->_name); \ + _recs[_n].visible = _show; \ + _recs[_n].can_modify = _mod; \ + _n++; \ +} while (0) + +#define _rec_uint16(_key, _recs, _org, _name, _show, _n, _mod) \ +do { \ + _recs[_n].type = TYPE_UINT16; \ + _strncpy(_recs[_n].name, _key, NAME_MAXVAL); \ + snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu16, _org->_name); \ + _recs[_n].data = &_org->_name; \ + _recs[_n].data_len = sizeof(_org->_name); \ + _recs[_n].visible = _show; \ + _recs[_n].can_modify = _mod; \ + _n++; \ +} while (0) + +#define _rec_uint32(_key, _recs, _org, _name, _show, _n, _mod) \ +do { \ + _recs[_n].type = TYPE_UINT32; \ + _strncpy(_recs[_n].name, _key, NAME_MAXVAL); \ + snprintf(_recs[_n].value, VALUE_MAXVAL, "%" PRIu32, _org->_name); \ + _recs[_n].data = &_org->_name; \ + _recs[_n].data_len = sizeof(_org->_name); \ + _recs[_n].visible = _show; \ + _recs[_n].can_modify = _mod; \ + _n++; \ +} while (0) + +enum modify_mode { + _CANNOT_MODIFY, + _CAN_MODIFY, +}; + +struct idbm_rec { + int type; + char name[NAME_MAXVAL]; + char value[VALUE_MAXVAL]; + void *data; + int data_len; + int visible; + /* + * TODO: make it a enum that can indicate whether it also requires + * a relogin to pick up if a session is running. + */ + enum modify_mode can_modify; +}; + +int _idbm_lock(struct iscsi_context *ctx) +{ + int fd, i, ret; + struct idbm *db = NULL; + char strerr_buff[_STRERR_BUFF_LEN]; + int errno_save = 0; + + assert(ctx != NULL); + + db = ctx->db; + + if (db->refs > 0) { + db->refs++; + return 0; + } + + if (access(LOCK_DIR, F_OK) != 0) { + if (mkdir(LOCK_DIR, 0660) != 0) { + _error(ctx, "Could not open %s: %d %s", LOCK_DIR, errno, + _strerror(errno, strerr_buff)); + return LIBISCSI_ERR_IDBM; + } + } + + fd = open(LOCK_FILE, O_RDWR | O_CREAT, 0666); + if (fd >= 0) + close(fd); + + for (i = 0; i < 3000; i++) { + ret = link(LOCK_FILE, LOCK_WRITE_FILE); + if (ret == 0) + break; + errno_save = errno; + + if (errno != EEXIST) { + _error(ctx, "Maybe you are not root? " + "Could not lock discovery DB: %s: %d %s", + LOCK_WRITE_FILE, errno_save, + _strerror(errno_save, strerr_buff)); + return LIBISCSI_ERR_IDBM; + } else if (i == 0) + _debug(ctx, "Waiting for discovery DB lock on %s", + LOCK_WRITE_FILE); + + usleep(10000); + } + + if (ret != 0) { + _error(ctx, "Timeout on acquiring lock on DB: %s, errno: %d %s", + LOCK_WRITE_FILE, errno_save, + _strerror(errno_save, strerr_buff)); + return LIBISCSI_ERR_IDBM; + } + + db->refs = 1; + return 0; +} + +void _idbm_unlock(struct iscsi_context *ctx) +{ + struct idbm *db = NULL; + + assert(ctx != NULL); + + db = ctx->db; + + if (db->refs > 1) { + db->refs--; + return; + } + + db->refs = 0; + unlink(LOCK_WRITE_FILE); +} + +static struct idbm_rec* _idbm_recs_alloc(void) +{ + return calloc(MAX_KEYS, sizeof(struct idbm_rec)); +} + +static void _idbm_recs_free(struct idbm_rec* recs) +{ + free(recs); +} + +static void _idbm_iface_rec_link(struct iscsi_iface *iface, + struct idbm_rec *recs) +{ + int num = 0; + + _rec_str(IFACE_ISCSINAME, recs, iface, name, IDBM_SHOW, num, + _CANNOT_MODIFY); + _rec_str(IFACE_NETNAME, recs, iface, netdev, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_IPADDR, recs, iface, ipaddress, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_HWADDR, recs, iface, hwaddress, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_TRANSPORTNAME, recs, iface, transport_name, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_INAME, recs, iface, iname, IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_STATE, recs, iface, state, IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint16(IFACE_VLAN_ID, recs, iface, vlan_id, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint8(IFACE_VLAN_PRIORITY, recs, iface, vlan_priority, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_VLAN_STATE, recs, iface, vlan_state, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint32(IFACE_NUM, recs, iface, iface_num, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint16(IFACE_MTU, recs, iface, mtu, IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint16(IFACE_PORT, recs, iface, port, IDBM_SHOW, num, _CAN_MODIFY); + + if (! iface->is_ipv6) { + _rec_str(IFACE_BOOT_PROTO, recs, iface, bootproto, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_SUBNET_MASK, recs, iface, subnet_mask, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_GATEWAY, recs, iface, gateway, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_DHCP_ALT_CID, recs, iface, + dhcp_alt_client_id_state, IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_DHCP_ALT_CID_STR, recs, iface, + dhcp_alt_client_id, IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_DHCP_DNS, recs, iface, dhcp_dns, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_DHCP_LEARN_IQN, recs, iface, dhcp_learn_iqn, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_DHCP_REQ_VID, recs, iface, + dhcp_req_vendor_id_state, IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_DHCP_VID, recs, iface, dhcp_vendor_id_state, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_DHCP_VID_STR, recs, iface, dhcp_vendor_id, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_DHCP_SLP_DA, recs, iface, dhcp_slp_da, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_FRAGMENTATION, recs, iface, fragmentation, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_GRAT_ARP, recs, iface, gratuitous_arp, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_IN_FORWARD, recs, iface, incoming_forwarding, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_TOS_STATE, recs, iface, tos_state, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_uint8(IFACE_TOS, recs, iface, tos, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint8(IFACE_TTL, recs, iface, ttl, IDBM_SHOW, num, + _CAN_MODIFY); + } else { + _rec_str(IFACE_IPV6_AUTOCFG, recs, iface, ipv6_autocfg, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_LINKLOCAL_AUTOCFG, recs, iface, + linklocal_autocfg, IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_ROUTER_AUTOCFG, recs, iface, router_autocfg, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_LINKLOCAL, recs, iface, ipv6_linklocal, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_ROUTER, recs, iface, ipv6_router, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint8(IFACE_DUP_ADDR_DETECT_CNT, recs, iface, + dup_addr_detect_cnt, IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint32(IFACE_FLOW_LABEL, recs, iface, flow_label, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_GRAT_NEIGHBOR_ADV, recs, iface, + gratuitous_neighbor_adv, IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint8(IFACE_HOP_LIMIT, recs, iface, hop_limit, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_MLD, recs, iface, mld, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint32(IFACE_ND_REACHABLE_TMO, recs, iface, + nd_reachable_tmo, IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint32(IFACE_ND_REXMIT_TIME, recs, iface, nd_rexmit_time, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint32(IFACE_ND_STALE_TMO, recs, iface, nd_stale_tmo, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint32(IFACE_RTR_ADV_LINK_MTU, recs, iface, + router_adv_link_mtu, IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint8(IFACE_TRAFFIC_CLASS, recs, iface, traffic_class, + IDBM_SHOW, num, _CAN_MODIFY); + } + + _rec_str(IFACE_DELAYED_ACK, recs, iface, delayed_ack, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_TCP_NAGLE, recs, iface, nagle, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_TCP_WSF_STATE, recs, iface, tcp_wsf_state, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_uint8(IFACE_TCP_WSF, recs, iface, tcp_wsf, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint8(IFACE_TCP_TIMER_SCALE, recs, iface, tcp_timer_scale, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_TCP_TIMESTAMP, recs, iface, tcp_timestamp, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_REDIRECT, recs, iface, redirect, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint16(IFACE_DEF_TMF_TMO, recs, iface, def_task_mgmt_tmo, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_HDRDGST, recs, iface, header_digest, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_DATADGST, recs, iface, data_digest, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_IMM_DATA, recs, iface, immediate_data, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_INITIAL_R2T, recs, iface, initial_r2t, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_DSEQ_INORDER, recs, iface, data_seq_inorder, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_DPDU_INORDER, recs, iface, data_pdu_inorder, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_uint8(IFACE_ERL, recs, iface, erl, IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint32(IFACE_MAX_RECV_DLEN, recs, iface, max_recv_dlength, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_uint32(IFACE_FIRST_BURST, recs, iface, first_burst_len, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_uint16(IFACE_MAX_R2T, recs, iface, max_out_r2t, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_uint32(IFACE_MAX_BURST, recs, iface, max_burst_len, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_CHAP_AUTH, recs, iface, chap_auth, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_BIDI_CHAP, recs, iface, bidi_chap, IDBM_SHOW, num, + _CAN_MODIFY); + _rec_str(IFACE_STRICT_LOGIN_COMP, recs, iface, strict_login_comp, + IDBM_SHOW, num, _CAN_MODIFY); + _rec_str(IFACE_DISCOVERY_AUTH, recs, iface, discovery_auth, IDBM_SHOW, + num, _CAN_MODIFY); + _rec_str(IFACE_DISCOVERY_LOGOUT, recs, iface, discovery_logout, + IDBM_SHOW, num, _CAN_MODIFY); +} + +static void _idbm_recs_print(struct idbm_rec *recs, FILE *f, int show) +{ + int i; + fprintf(f, "%s\n", ISCSI_BEGIN_REC); + for (i = 0; i < MAX_KEYS; i++) { + if (recs[i].visible == IDBM_HIDE) + continue; + if (!show && recs[i].visible == IDBM_MASKED) { + if (*(char*)recs[i].data) { + fprintf(f, "%s = ********\n", recs[i].name); + continue; + } + /* fall through */ + } + + if (strlen(recs[i].value)) + fprintf(f, "%s = %s\n", recs[i].name, recs[i].value); + else if (f == stdout) + fprintf(f, "%s = <empty>\n", recs[i].name); + } + fprintf(f, "%s\n", ISCSI_END_REC); +} + +void _idbm_iface_print(struct iscsi_iface *iface, FILE *f) +{ + struct idbm_rec *recs = NULL; + + recs = _idbm_recs_alloc(); + if (recs == NULL) + return; + + _idbm_iface_rec_link(iface, recs); + + _idbm_recs_print(recs, f, IDBM_SHOW); + + _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 passwd_done = 0; + char passwd_len[8]; + + assert(ctx != NULL); + assert(recs != NULL); + assert(name != NULL); + assert(value != NULL); + +setup_passwd_len: + for (i = 0; i < MAX_KEYS; ++i) { + if (!strcmp(name, recs[i].name)) { + _debug(ctx, "updated '%s', '%s' => '%s'", name, + recs[i].value, value); + /* parse recinfo by type */ + switch (recs[i].type) { + case TYPE_UINT8: + if (!recs[i].data) + continue; + + *(uint8_t *)recs[i].data = + strtoul(value, NULL, 10); + goto updated; + case TYPE_UINT16: + if (!recs[i].data) + continue; + + *(uint16_t *)recs[i].data = + strtoul(value, NULL, 10); + goto updated; + case TYPE_UINT32: + if (!recs[i].data) + continue; + + *(uint32_t *)recs[i].data = + strtoul(value, NULL, 10); + goto updated; + case TYPE_STR: + if (!recs[i].data) + continue; + + _strncpy((char*)recs[i].data, + value, recs[i].data_len); + goto updated; + default: + _error(ctx, "Got unknown data type %d " + "for name '%s', value '%s'", + recs[i].data, recs[i].name, + recs[i].value); + rc = LIBISCSI_ERR_BUG; + goto out; + } + if (line_number) { + _warn(ctx, "config file line %d contains " + "unknown value format '%s' for " + "parameter name '%s'", + line_number, value, name); + } else { + _error(ctx, "unknown value format '%s' for " + "parameter name '%s'", value, name); + rc = LIBISCSI_ERR_INVAL; + } + goto out; + } + } + _error(ctx, "Unknown parameter name %s", name); + rc = LIBISCSI_ERR_INVAL; + goto out; + +updated: + _strncpy((char*)recs[i].value, value, VALUE_MAXVAL); + +#define check_password_param(_param) \ + if (!passwd_done && !strcmp(#_param, name)) { \ + passwd_done = 1; \ + name = #_param "_length"; \ + snprintf(passwd_len, 8, "%d", (int)strlen(value)); \ + value = passwd_len; \ + goto setup_passwd_len; \ + } + + check_password_param(node.session.auth.password); + check_password_param(node.session.auth.password_in); + check_password_param(discovery.sendtargets.auth.password); + check_password_param(discovery.sendtargets.auth.password_in); + check_password_param(discovery.slp.auth.password); + check_password_param(discovery.slp.auth.password_in); + check_password_param(host.auth.password); + check_password_param(host.auth.password_in); + +out: + return rc; +} + +/* + * from linux kernel + */ +static char *strstrip(char *s) +{ + size_t size; + char *end; + + size = strlen(s); + if (!size) + return s; + + end = s + size - 1; + while (end >= s && isspace(*end)) + end--; + *(end + 1) = '\0'; + + while (*s && isspace(*s)) + s++; + + return s; +} + +static int _idbm_recs_read(struct iscsi_context *ctx, struct idbm_rec *recs, + const char *conf_path) +{ + int rc = LIBISCSI_OK; + char name[NAME_MAXVAL]; + char value[VALUE_MAXVAL]; + char *line = NULL; + char *nl = NULL; + char buffer[2048]; + int line_number = 0; + int c = 0; + int i = 0; + FILE *f = NULL; + int errno_save = 0; + char strerr_buff[_STRERR_BUFF_LEN]; + + assert(ctx != NULL); + assert(recs != NULL); + assert(conf_path != NULL); + + f = fopen(conf_path, "r"); + errno_save = errno; + if (!f) { + _error(ctx, "Failed to open %s using read mode: %d %s", + conf_path, errno_save, + _strerror(errno_save, strerr_buff)); + rc = LIBISCSI_ERR_IDBM; + goto out; + } + + _info(ctx, "Parsing iSCSI interface configuration %s", conf_path); + /* process the config file */ + do { + line = fgets(buffer, sizeof (buffer), f); + line_number++; + if (!line) + continue; + if (strlen(line) == 0) + continue; + + nl = line + strlen(line) - 1; + if (*nl != '\n') { + _warn(ctx, "Config file %s line %d too long.", + conf_path, line_number); + continue; + } + + line = strstrip(line); + /* process any non-empty, non-comment lines */ + if (!*line || *line == '\0' || *line == '\n' || *line == '#') + continue; + + /* parse name */ + i=0; nl = line; *name = 0; + while (*nl && !isspace(c = *nl) && *nl != '=') { + *(name+i) = *nl; i++; nl++; + } + if (!*nl) { + _warn(ctx, "config file %s line %d do not has value", + conf_path, line_number); + continue; + } + *(name+i)=0; nl++; + /* skip after-name traling spaces */ + while (*nl && isspace(c = *nl)) nl++; + if (*nl && *nl != '=') { + _warn(ctx, "config file %s line %d has not '=' " + "separator", conf_path, line_number); + continue; + } + /* skip '=' sepa */ + nl++; + /* skip after-sepa traling spaces */ + while (*nl && isspace(c = *nl)) nl++; + if (!*nl) { + _warn(ctx, "config file %s line %d do not has value", + conf_path, line_number); + continue; + } + /* parse value */ + i=0; *value = 0; + while (*nl) { + *(value+i) = *nl; i++; nl++; + } + *(value+i) = 0; + + rc = _idbm_rec_update_param(ctx, recs, name, value, + line_number); + if (rc == LIBISCSI_ERR_INVAL) { + _error(ctx, "config file %s invalid.", conf_path); + goto out; + } else if (rc != LIBISCSI_OK) + goto out; + } while (line); + +out: + if (f != NULL) + fclose(f); + return rc; +} + +int _idbm_iface_get(struct iscsi_context *ctx, const char *iface_name, struct + iscsi_iface **iface) +{ + int rc = LIBISCSI_OK; + char conf_path[PATH_MAX]; + struct idbm_rec *recs = NULL; + + assert(iface != NULL); + assert(ctx != NULL); + + *iface = NULL; + + if (iface_name == NULL) + goto out; + + snprintf(conf_path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, iface_name); + + *iface = calloc(1, sizeof(struct iscsi_iface)); + _alloc_null_check(ctx, *iface, rc, out); + + snprintf((*iface)->name, sizeof((*iface)->name)/sizeof(char), + "%s", iface_name); + + recs = _idbm_recs_alloc(); + _alloc_null_check(ctx, recs, rc, out); + + _idbm_iface_rec_link(*iface, recs); + + _good(_idbm_recs_read(ctx, recs, conf_path), rc, out); + + if (! _iface_is_valid(*iface)) { + _warn(ctx, "'%s' is not a valid iSCSI interface configuration " + "file", conf_path); + iscsi_iface_free(*iface); + *iface = NULL; + /* We still treat this as pass(no error) */ + } + +out: + if (rc != LIBISCSI_OK) { + iscsi_iface_free(*iface); + *iface = NULL; + } + _idbm_recs_free(recs); + return rc; +} +struct idbm *_idbm_new(void) +{ + return calloc(1, sizeof(struct idbm)); +} + +void _idbm_free(struct idbm *db) +{ + free(db); +} diff --git a/libopeniscsiusr/idbm.h b/libopeniscsiusr/idbm.h new file mode 100644 index 0000000..31cbe6b --- /dev/null +++ b/libopeniscsiusr/idbm.h @@ -0,0 +1,46 @@ +/* + * 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_OPEN_USR_IDBM_H__ +#define __ISCSI_OPEN_USR_IDBM_H__ + +#include <stdio.h> +#include <stdbool.h> + +#include "libopeniscsiusr/libopeniscsiusr_common.h" + +#define ISCSI_CONFIG_ROOT "/etc/iscsi/" +#define IFACE_CONFIG_DIR ISCSI_CONFIG_ROOT"ifaces" + +struct __DLL_LOCAL idbm; + +struct idbm { + int refs; +}; + +__DLL_LOCAL struct idbm *_idbm_new(void); +__DLL_LOCAL void _idbm_free(struct idbm *db); +__DLL_LOCAL int _idbm_lock(struct iscsi_context *ctx); +__DLL_LOCAL void _idbm_unlock(struct iscsi_context *ctx); +__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); + +#endif /* End of __ISCSI_OPEN_USR_IDBM_H__ */ diff --git a/libopeniscsiusr/idbm_fields.h b/libopeniscsiusr/idbm_fields.h new file mode 100644 index 0000000..ce30ad9 --- /dev/null +++ b/libopeniscsiusr/idbm_fields.h @@ -0,0 +1,98 @@ +/* + * 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 __ISCSI_OPEN_USER_IDBM_FIELDS_H +#define __ISCSI_OPEN_USER_IDBM_FIELDS_H + +/* iface fields */ +#define IFACE_HWADDR "iface.hwaddress" +#define IFACE_ISCSINAME "iface.iscsi_ifacename" +#define IFACE_NETNAME "iface.net_ifacename" +#define IFACE_TRANSPORTNAME "iface.transport_name" +#define IFACE_INAME "iface.initiatorname" +#define IFACE_ISID "iface.isid" +#define IFACE_BOOT_PROTO "iface.bootproto" +#define IFACE_IPADDR "iface.ipaddress" +#define IFACE_SUBNET_MASK "iface.subnet_mask" +#define IFACE_GATEWAY "iface.gateway" +#define IFACE_PRIMARY_DNS "iface.primary_dns" +#define IFACE_SEC_DNS "iface.secondary_dns" +#define IFACE_VLAN_ID "iface.vlan_id" +#define IFACE_VLAN_PRIORITY "iface.vlan_priority" +#define IFACE_VLAN_STATE "iface.vlan_state" +#define IFACE_LINKLOCAL "iface.ipv6_linklocal" +#define IFACE_ROUTER "iface.ipv6_router" +#define IFACE_IPV6_AUTOCFG "iface.ipv6_autocfg" +#define IFACE_LINKLOCAL_AUTOCFG "iface.linklocal_autocfg" +#define IFACE_ROUTER_AUTOCFG "iface.router_autocfg" +#define IFACE_STATE "iface.state" +#define IFACE_NUM "iface.iface_num" +#define IFACE_MTU "iface.mtu" +#define IFACE_PORT "iface.port" +#define IFACE_DELAYED_ACK "iface.delayed_ack" +#define IFACE_TCP_NAGLE "iface.tcp_nagle" +#define IFACE_TCP_WSF_STATE "iface.tcp_wsf_state" +#define IFACE_TCP_WSF "iface.tcp_wsf" +#define IFACE_TCP_TIMER_SCALE "iface.tcp_timer_scale" +#define IFACE_TCP_TIMESTAMP "iface.tcp_timestamp" +#define IFACE_DHCP_DNS "iface.dhcp_dns" +#define IFACE_DHCP_SLP_DA "iface.dhcp_slp_da" +#define IFACE_TOS_STATE "iface.tos_state" +#define IFACE_TOS "iface.tos" +#define IFACE_GRAT_ARP "iface.gratuitous_arp" +#define IFACE_DHCP_ALT_CID "iface.dhcp_alt_client_id_state" +#define IFACE_DHCP_ALT_CID_STR "iface.dhcp_alt_client_id" +#define IFACE_DHCP_REQ_VID "iface.dhcp_req_vendor_id_state" +#define IFACE_DHCP_VID "iface.dhcp_vendor_id_state" +#define IFACE_DHCP_VID_STR "iface.dhcp_vendor_id" +#define IFACE_DHCP_LEARN_IQN "iface.dhcp_learn_iqn" +#define IFACE_FRAGMENTATION "iface.fragmentation" +#define IFACE_IN_FORWARD "iface.incoming_forwarding" +#define IFACE_TTL "iface.ttl" +#define IFACE_GRAT_NEIGHBOR_ADV "iface.gratuitous_neighbor_adv" +#define IFACE_REDIRECT "iface.redirect" +#define IFACE_IGNORE_ICMP_ECHO_REQ "iface.ignore_icmp_echo_request" +#define IFACE_MLD "iface.mld" +#define IFACE_FLOW_LABEL "iface.flow_label" +#define IFACE_TRAFFIC_CLASS "iface.traffic_class" +#define IFACE_HOP_LIMIT "iface.hop_limit" +#define IFACE_ND_REACHABLE_TMO "iface.nd_reachable_tmo" +#define IFACE_ND_REXMIT_TIME "iface.nd_rexmit_time" +#define IFACE_ND_STALE_TMO "iface.nd_stale_tmo" +#define IFACE_DUP_ADDR_DETECT_CNT "iface.dup_addr_detect_cnt" +#define IFACE_RTR_ADV_LINK_MTU "iface.router_adv_link_mtu" +#define IFACE_DEF_TMF_TMO "iface.def_task_mgmt_timeout" +#define IFACE_HDRDGST "iface.header_digest" +#define IFACE_DATADGST "iface.data_digest" +#define IFACE_IMM_DATA "iface.immediate_data" +#define IFACE_INITIAL_R2T "iface.initial_r2t" +#define IFACE_DSEQ_INORDER "iface.data_seq_inorder" +#define IFACE_DPDU_INORDER "iface.data_pdu_inorder" +#define IFACE_ERL "iface.erl" +#define IFACE_MAX_RECV_DLEN "iface.max_receive_data_len" +#define IFACE_FIRST_BURST "iface.first_burst_len" +#define IFACE_MAX_R2T "iface.max_outstanding_r2t" +#define IFACE_MAX_BURST "iface.max_burst_len" +#define IFACE_CHAP_AUTH "iface.chap_auth" +#define IFACE_BIDI_CHAP "iface.bidi_chap" +#define IFACE_STRICT_LOGIN_COMP "iface.strict_login_compliance" +#define IFACE_DISCOVERY_AUTH "iface.discovery_auth" +#define IFACE_DISCOVERY_LOGOUT "iface.discovery_logout" + +#endif /* End of __ISCSI_OPEN_USER_IDBM_FIELDS_H */ diff --git a/libopeniscsiusr/iface.c b/libopeniscsiusr/iface.c index 79898df..5cccbf2 100644 --- a/libopeniscsiusr/iface.c +++ b/libopeniscsiusr/iface.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Red Hat, Inc. + * 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 @@ -27,117 +27,61 @@ #include <dirent.h> #include <string.h> #include <net/if.h> -#include <netdb.h> #include <assert.h> #include <inttypes.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdbool.h> +#ifdef USE_KMOD +#include <libkmod.h> +#endif #include "libopeniscsiusr/libopeniscsiusr.h" #include "misc.h" #include "sysfs.h" #include "iface.h" - -#define ISCSI_MAX_IFACE_LEN 65 -#define ISCSI_TRANSPORT_NAME_MAXLEN 16 -#define ISCSI_MAX_STR_LEN 80 -#define ISCSI_HWADDRESS_BUF_SIZE 18 -#define TARGET_NAME_MAXLEN 255 -/* ^ TODO(Gris Ge): Above 5 constants are copy from usr/config.h, need to - * verify them in linux kernel code - */ +#include "context.h" +#include "idbm.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. + const char *iscsi_driver_name; // iSCSI offload driver. + const char *transport_name; // iSCSI transport name. +}; + +static struct _iscsi_net_drv _ISCSI_NET_DRVS[] = { + {"cxgb3", "cxgb3i", "cxgb3i"}, + {"cxgb4", "cxgb4i", "cxgb4i"}, + {"bnx2", "bnx2i" , "bnx2i"}, + {"bnx2x", "bnx2i", "bnx2i"}, +}; -/* Just copy from `struct iface_rec` from usr/config.h */ -struct iscsi_iface { - /* iscsi iface record name */ - char name[ISCSI_MAX_IFACE_LEN]; - uint32_t iface_num; - /* network layer iface name (eth0) */ - char netdev[IFNAMSIZ]; - char ipaddress[NI_MAXHOST]; - char subnet_mask[NI_MAXHOST]; - char gateway[NI_MAXHOST]; - char bootproto[ISCSI_MAX_STR_LEN]; - char ipv6_linklocal[NI_MAXHOST]; - char ipv6_router[NI_MAXHOST]; - char ipv6_autocfg[NI_MAXHOST]; - char linklocal_autocfg[NI_MAXHOST]; - char router_autocfg[NI_MAXHOST]; - uint16_t vlan_id; - uint8_t vlan_priority; - char vlan_state[ISCSI_MAX_STR_LEN]; - char state[ISCSI_MAX_STR_LEN]; /* 0 = disable, - * 1 = enable */ - uint16_t mtu; - uint16_t port; - char delayed_ack[ISCSI_MAX_STR_LEN]; - char nagle[ISCSI_MAX_STR_LEN]; - char tcp_wsf_state[ISCSI_MAX_STR_LEN]; - uint8_t tcp_wsf; - uint8_t tcp_timer_scale; - char tcp_timestamp[ISCSI_MAX_STR_LEN]; - char dhcp_dns[ISCSI_MAX_STR_LEN]; - char dhcp_slp_da[ISCSI_MAX_STR_LEN]; - char tos_state[ISCSI_MAX_STR_LEN]; - uint8_t tos; - char gratuitous_arp[ISCSI_MAX_STR_LEN]; - char dhcp_alt_client_id_state[ISCSI_MAX_STR_LEN]; - char dhcp_alt_client_id[ISCSI_MAX_STR_LEN]; - char dhcp_req_vendor_id_state[ISCSI_MAX_STR_LEN]; - char dhcp_vendor_id_state[ISCSI_MAX_STR_LEN]; - char dhcp_vendor_id[ISCSI_MAX_STR_LEN]; - char dhcp_learn_iqn[ISCSI_MAX_STR_LEN]; - char fragmentation[ISCSI_MAX_STR_LEN]; - char incoming_forwarding[ISCSI_MAX_STR_LEN]; - uint8_t ttl; - char gratuitous_neighbor_adv[ISCSI_MAX_STR_LEN]; - char redirect[ISCSI_MAX_STR_LEN]; - char mld[ISCSI_MAX_STR_LEN]; - uint32_t flow_label; - uint32_t traffic_class; - uint8_t hop_limit; - uint32_t nd_reachable_tmo; - uint32_t nd_rexmit_time; - uint32_t nd_stale_tmo; - uint8_t dup_addr_detect_cnt; - uint32_t router_adv_link_mtu; - uint16_t def_task_mgmt_tmo; - char header_digest[ISCSI_MAX_STR_LEN]; - char data_digest[ISCSI_MAX_STR_LEN]; - char immediate_data[ISCSI_MAX_STR_LEN]; - char initial_r2t[ISCSI_MAX_STR_LEN]; - char data_seq_inorder[ISCSI_MAX_STR_LEN]; - char data_pdu_inorder[ISCSI_MAX_STR_LEN]; - uint8_t erl; - uint32_t max_recv_dlength; - uint32_t first_burst_len; - uint16_t max_out_r2t; - uint32_t max_burst_len; - char chap_auth[ISCSI_MAX_STR_LEN]; - char bidi_chap[ISCSI_MAX_STR_LEN]; - char strict_login_comp[ISCSI_MAX_STR_LEN]; - char discovery_auth[ISCSI_MAX_STR_LEN]; - char discovery_logout[ISCSI_MAX_STR_LEN]; - char port_state[ISCSI_MAX_STR_LEN]; - char port_speed[ISCSI_MAX_STR_LEN]; - /* - * TODO: we may have to make this bigger and interconnect - * specific for infiniband - */ - char hwaddress[ISCSI_HWADDRESS_BUF_SIZE]; - char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; - /* - * This is only used for boot now, but the iser guys - * can use this for their virtualization idea. - */ - char alias[TARGET_NAME_MAXLEN + 1]; - char iname[TARGET_NAME_MAXLEN + 1]; +const struct iscsi_iface _DEFAULT_IFACES[] = { + { + .name = "default", + .transport_name = "tcp", + }, + { + .name = "iser", + .transport_name = "iser", + }, }; +static int _load_kernel_module(struct iscsi_context *ctx, const char *drv_name); +static int _iface_conf_write(struct iscsi_context *ctx, + struct iscsi_iface *iface); +static int _fill_hw_iface_from_sys(struct iscsi_context *ctx, + struct iscsi_iface *iface, + const char *iface_kern_id); + _iscsi_getter_func_gen(iscsi_iface, hwaddress, const char *); _iscsi_getter_func_gen(iscsi_iface, transport_name, const char *); _iscsi_getter_func_gen(iscsi_iface, ipaddress, const char *); @@ -147,36 +91,49 @@ _iscsi_getter_func_gen(iscsi_iface, port_state, const char *); _iscsi_getter_func_gen(iscsi_iface, port_speed, const char *); _iscsi_getter_func_gen(iscsi_iface, name, const char *); -int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid, - const char *iface_kern_id, struct iscsi_iface **iface) +int _iscsi_iface_get_from_sysfs(struct iscsi_context *ctx, uint32_t host_id, + uint32_t sid, struct iscsi_iface **iface) { int rc = LIBISCSI_OK; - char sysfs_se_dir_path[PATH_MAX]; - char sysfs_sh_dir_path[PATH_MAX]; - char sysfs_scsi_host_dir_path[PATH_MAX]; - char sysfs_iface_dir_path[PATH_MAX]; + char *sysfs_se_dir_path = NULL; + char *sysfs_sh_dir_path = NULL; + char *sysfs_scsi_host_dir_path = NULL; + char *sysfs_iface_dir_path = NULL; + char iface_kern_id[PATH_MAX]; char proc_name[ISCSI_TRANSPORT_NAME_MAXLEN]; + struct iscsi_iface **ifaces = NULL; + uint32_t iface_count = 0; + uint32_t i = 0; + struct iscsi_iface *tmp_iface = NULL; + bool bound_by_hwaddr = false; + bool bound_by_netdev = false; + bool matched = false; assert(ctx != NULL); assert(host_id != 0); - assert(sid != 0); - /* TODO(Gris Ge): Handle when sid == 0(ignored) */ assert(iface != NULL); *iface = NULL; - *iface = (struct iscsi_iface *) malloc(sizeof(struct iscsi_iface)); - _alloc_null_check(ctx, *iface, rc, out); + if (sid != 0) { + sysfs_se_dir_path = malloc(PATH_MAX); + _alloc_null_check(ctx, sysfs_se_dir_path, rc, out); + snprintf(sysfs_se_dir_path, PATH_MAX, "%s/session%" PRIu32, + _ISCSI_SYS_SESSION_DIR, sid); + } - snprintf(sysfs_se_dir_path, PATH_MAX, "%s/session%" PRIu32, - _ISCSI_SYS_SESSION_DIR, sid); + sysfs_sh_dir_path = malloc(PATH_MAX); + _alloc_null_check(ctx, sysfs_sh_dir_path, rc, out); snprintf(sysfs_sh_dir_path, PATH_MAX, "%s/host%" PRIu32, _ISCSI_SYS_HOST_DIR, host_id); + + sysfs_scsi_host_dir_path = malloc(PATH_MAX); + _alloc_null_check(ctx, sysfs_scsi_host_dir_path, rc, out); snprintf(sysfs_scsi_host_dir_path, PATH_MAX, "%s/host%" PRIu32, _SCSI_SYS_HOST_DIR, host_id); - if (iface_kern_id != NULL) - snprintf(sysfs_iface_dir_path, PATH_MAX, "%s/%s", - _ISCSI_SYS_IFACE_DIR, iface_kern_id); + + *iface = (struct iscsi_iface *) calloc(1, sizeof(struct iscsi_iface)); + _alloc_null_check(ctx, *iface, rc, out); _good(_sysfs_prop_get_str(ctx, sysfs_scsi_host_dir_path, "proc_name", proc_name, sizeof(proc_name) / sizeof(char), @@ -184,10 +141,10 @@ int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid, rc, out); if (strncmp(proc_name, "iscsi_", strlen("iscsi_")) == 0) - strncpy((*iface)->transport_name, proc_name + strlen("iscsi_"), - sizeof((*iface)->transport_name) / sizeof(char)); + _strncpy((*iface)->transport_name, proc_name + strlen("iscsi_"), + sizeof((*iface)->transport_name) / sizeof(char)); else - strncpy((*iface)->transport_name, proc_name, + _strncpy((*iface)->transport_name, proc_name, sizeof((*iface)->transport_name) / sizeof(char)); _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "hwaddress", @@ -195,41 +152,27 @@ int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid, sizeof((*iface)->hwaddress) / sizeof(char), DEFAULT_HWADDRESS), rc, out); - - if (iface_kern_id != NULL) - _good(_sysfs_prop_get_str(ctx, sysfs_iface_dir_path, - "ipaddress", - (*iface)->ipaddress, - sizeof((*iface)->ipaddress) / - sizeof(char), DEFAULT_IPADDRESS), - rc, out); - else - _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "ipaddress", - (*iface)->ipaddress, - sizeof((*iface)->ipaddress) / - sizeof(char), DEFAULT_IPADDRESS), - rc, out); + if (strcmp((*iface)->hwaddress, DEFAULT_HWADDRESS) != 0) + bound_by_hwaddr = true; _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "netdev", (*iface)->netdev, sizeof((*iface)->netdev) / sizeof(char), DEFAULT_NETDEV), rc, out); + if (strcmp((*iface)->netdev, DEFAULT_NETDEV) != 0) + bound_by_netdev = true; - if (_sysfs_prop_get_str(NULL /* Ignore error */, sysfs_se_dir_path, - "initiatorname", - (*iface)->iname, - sizeof((*iface)->iname) / sizeof(char), - "") - != LIBISCSI_OK) { - _debug(ctx, "Failed to read initiatorname from %s folder", - sysfs_se_dir_path); + if (sysfs_se_dir_path) + _sysfs_prop_get_str(ctx, sysfs_se_dir_path, "initiatorname", + (*iface)->iname, + sizeof((*iface)->iname) / sizeof(char), ""); + if (strcmp((*iface)->iname, "") == 0) _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "initiatorname", (*iface)->iname, sizeof((*iface)->iname) / sizeof(char), ""), rc, out); - } _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "port_state", (*iface)->port_state, @@ -238,8 +181,8 @@ int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid, rc, out); if (strcmp((*iface)->port_state, "Unknown!") == 0) - strncpy((*iface)->port_state, "unknown", - sizeof((*iface)->port_state) / sizeof(char)); + _strncpy((*iface)->port_state, "unknown", + sizeof((*iface)->port_state) / sizeof(char)); _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "port_speed", (*iface)->port_speed, @@ -248,42 +191,709 @@ int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, uint32_t sid, rc, out); if (strncmp((*iface)->port_speed, "Unknown", strlen("Unknown")) == 0) - strncpy((*iface)->port_speed, "unknown", - sizeof((*iface)->port_speed) / sizeof(char)); - - _good(_sysfs_prop_get_str(NULL /* Ignore error */, sysfs_se_dir_path, - "ifacename", - (*iface)->name, - sizeof((*iface)->name)/sizeof(char), - ""), - rc, out); + _strncpy((*iface)->port_speed, "unknown", + sizeof((*iface)->port_speed) / sizeof(char)); + + if (sysfs_se_dir_path != NULL) + _sysfs_prop_get_str(ctx, sysfs_se_dir_path, "ifacename", + (*iface)->name, + sizeof((*iface)->name)/sizeof(char), ""); + + rc = _iscsi_iface_kern_id_of_host_id(ctx, host_id, iface_kern_id); + if (rc == LIBISCSI_OK) { + _good(_fill_hw_iface_from_sys(ctx, *iface, iface_kern_id), + rc, out); + } else { + _good(_sysfs_prop_get_str(ctx, sysfs_sh_dir_path, "ipaddress", + (*iface)->ipaddress, + sizeof((*iface)->ipaddress) / + sizeof(char), DEFAULT_IPADDRESS), + rc, out); + /* bnx2i does not create + * /sys/class/iscsi_iface/<iface_kernl_id> + * We need to use transport_name.hwaddress as iface name. + */ + _debug(ctx, "HAHA: hwaddress %s", (*iface)->hwaddress); + if (bound_by_hwaddr) + snprintf((*iface)->name, + sizeof((*iface)->name)/sizeof(char), + "%s.%s", (*iface)->transport_name, + (*iface)->hwaddress); + } + if (strcmp((*iface)->name, "") == 0) { - _debug(ctx, "Failed to query ifacename from %s folder", - sysfs_se_dir_path); /* - * if the ifacename file is not there then we are - * using a older kernel and can try to find the - * binding by the net info which was used on these - * older kernels. + * Before 2.0.870, we only could bind by netdeivce or hwaddress, + * so we did a simple reverse lookup to go from sysfs info to + * the iface name. After 2.0.870 we added a lot of options to + * the iface binding so we added the ifacename to the kernel. + * + * Below codes are for older kernels that do not export the + * ifacename. If the user was doing iscsi_tcp session binding + * we will find the iface by matching net info. */ - /*TODO(Gris Ge): need to parse /etc/iscsi/ifaces/<iface_name> - * files to find a match. I will add the code later when - * we expose more defiled information on iscsi_iface. - */ - strncpy((*iface)->name, DEFAULT_IFACENAME, - sizeof((*iface)->name) / sizeof(char)); + _good(iscsi_ifaces_get(ctx, &ifaces, &iface_count), rc, out); + + for (i = 0; i < iface_count; ++i) { + tmp_iface = ifaces[i]; + if ((bound_by_hwaddr == true) && + (strcmp(tmp_iface->hwaddress, (*iface)->hwaddress) + == 0)) { + _strncpy((*iface)->name, tmp_iface->name, + sizeof((*iface)->name)/sizeof(char)); + matched = true; + break; + } + if ((bound_by_netdev == true) && + (strcmp(tmp_iface->netdev, (*iface)->netdev) + == 0)) { + _strncpy((*iface)->name, tmp_iface->name, + sizeof((*iface)->name)/sizeof(char)); + matched = true; + break; + } + } + if (!matched) + _strncpy((*iface)->name, DEFAULT_IFACENAME, + sizeof((*iface)->name) / sizeof(char)); } out: if (rc != LIBISCSI_OK) { - _iscsi_iface_free(*iface); + iscsi_iface_free(*iface); *iface = NULL; } + free(sysfs_se_dir_path); + free(sysfs_sh_dir_path); + free(sysfs_scsi_host_dir_path); + free(sysfs_iface_dir_path); + iscsi_ifaces_free(ifaces, iface_count); + return rc; +} + +int iscsi_default_iface_setup(struct iscsi_context *ctx) +{ + int rc = LIBISCSI_OK; + char strerr_buff[_STRERR_BUFF_LEN]; + int errno_save = 0; + struct _eth_if **eifs = NULL; + uint32_t eif_count = 0; + uint32_t i = 0; + size_t j = 0; + struct _iscsi_net_drv *ind = NULL; + uint32_t *hids = NULL; + uint32_t hid_count = 0; + struct iscsi_iface *iface = NULL; + char path[PATH_MAX]; + + assert(ctx != NULL); + + _good(_idbm_lock(ctx), rc, out); + + if ((access(IFACE_CONFIG_DIR, F_OK) != 0) && + (mkdir(IFACE_CONFIG_DIR, 0660) != 0)) { + errno_save = errno; + _idbm_unlock(ctx); + _error(ctx, "Could not make %s folder(%d %s). " + "HW/OFFLOAD iscsi may not be supported.", + IFACE_CONFIG_DIR, errno_save, + _strerror(errno_save, strerr_buff)); + if (errno_save == EACCES) + return LIBISCSI_ERR_ACCESS; + return LIBISCSI_ERR_BUG; + } + _idbm_unlock(ctx); + + /* Load kernel driver for iSCSI offload cards, like cxgb3i */ + _good(_eth_ifs_get(ctx, &eifs, &eif_count), rc, out); + + for (i = 0; i < eif_count; ++i) { + for (j = 0; + j < sizeof(_ISCSI_NET_DRVS)/sizeof(struct _iscsi_net_drv); + ++j) { + ind = &(_ISCSI_NET_DRVS[j]); + if ((ind->net_driver_name == NULL) || + (strcmp(eifs[i]->driver_name, + ind->net_driver_name) != 0)) + continue; + /* + * iSCSI hardware offload for bnx2{,x} is only supported + * if the iscsiuio executable is available. + */ + if ((strcmp(eifs[i]->driver_name, "bnx2x") == 0) || + (strcmp(eifs[i]->driver_name, "bnx2") == 0)) { + if (access(ISCSIUIO_PATH, F_OK) != 0) { + _debug(ctx, "iSCSI offload on %s(%s) " + "via %s is not supported due to " + "missing %s", eifs[i]->if_name, + eifs[i]->driver_name, + ind->iscsi_driver_name, + ISCSIUIO_PATH); + continue; + } + } + + if (_iscsi_transport_is_loaded(ind->transport_name)) + continue; + + _debug(ctx, "Loading kernel module %s for iSCSI " + "offload on %s(%s)", ind->iscsi_driver_name, + eifs[i]->if_name, eifs[i]->driver_name); + _good(_load_kernel_module(ctx, ind->iscsi_driver_name), + rc, out); + } + } + + _good(_iscsi_hids_get(ctx, &hids, &hid_count), rc, out); + for (i = 0; i < hid_count; ++i) { + /* Create /etc/iscsi/ifaces/<iface_name> file if not found + */ + _good(_iscsi_iface_get_from_sysfs(ctx, hids[i], 0, &iface), + rc, out); + if ( ! iscsi_is_default_iface(iface)) { + snprintf(path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, + iface->name); + if (access(path, F_OK) != 0) + rc = _iface_conf_write(ctx, iface); + } + iscsi_iface_free(iface); + if (rc != LIBISCSI_OK) + goto out; + } + +out: + _eth_ifs_free(eifs, eif_count); + free(hids); + return rc; +} + +static int _load_kernel_module(struct iscsi_context *ctx, const char *drv_name) +{ +#ifdef USE_KMOD + struct kmod_ctx *kctx = NULL; + struct kmod_module *mod = NULL; + int rc = LIBISCSI_OK; + + kctx = kmod_new(NULL, NULL); + _alloc_null_check(ctx, kctx, rc, out); + + kmod_load_resources(kctx); + + if (kmod_module_new_from_name(kctx, drv_name, &mod)) { + _error(ctx, "Failed to load module %s.", drv_name); + rc = LIBISCSI_ERR_TRANS_NOT_FOUND; + goto out; + } + + if (kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, + NULL, NULL, NULL, NULL)) { + _error(ctx, "Could not insert module %s. Kmod error %d", + drv_name, rc); + rc = LIBISCSI_ERR_TRANS_NOT_FOUND; + } + kmod_module_unref(mod); + +out: + if (kctx != NULL) + kmod_unref(kctx); + return rc; + +#else + char *cmdline[4]; + pid_t pid = 0; + char strerr_buff[_STRERR_BUFF_LEN]; + int errno_save = 0; + + cmdline[0] = "/sbin/modprobe"; + cmdline[1] = "-qb"; + cmdline[2] = (char *) drv_name; + cmdline[3] = NULL; + + pid = fork(); + if (pid == 0) { + if (execv("/sbin/modprobe", cmdline) < 0) { + errno_save = errno; + _error(ctx, "Failed to load module %s, error %d: %s", + drv_name, errno_save, + _strerror(errno_save, strerr_buff)); + exit(-errno_save); + } + exit(0); + } else if (pid < 0) { + _error(ctx, "Failed to fork process to load module %s: %s", + drv_name, _strerror(errno_save, strerr_buff)); + return LIBISCSI_ERR_TRANS_NOT_FOUND; + } + + if (waitpid(pid, NULL, 0) < 0) { + _error(ctx, "Failed to load module %s", drv_name); + return LIBISCSI_ERR_TRANS_NOT_FOUND; + } + + return LIBISCSI_OK; +#endif +} + +static int _iface_conf_write(struct iscsi_context *ctx, + struct iscsi_iface *iface) +{ + char conf_path[PATH_MAX]; + char strerr_buff[_STRERR_BUFF_LEN]; + int errno_save = 0; + FILE *f = NULL; + int rc = 0; + + if (iscsi_is_default_iface(iface)) { + _error(ctx, "iface %s is not a special interface and " + "is not stored in %s", iface->name, IFACE_CONFIG_DIR); + return LIBISCSI_ERR_INVAL; + } + + _good(_idbm_lock(ctx), rc, out); + + snprintf(conf_path, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, iface->name); + _debug(ctx, "Creating iSCSI interface configuration file '%s' " + "using kernel information", conf_path); + f = fopen(conf_path, "w"); + errno_save = errno; + if (!f) { + _error(ctx, "Failed to open %s using write mode: %d %s", + conf_path, errno_save, + _strerror(errno_save, strerr_buff)); + rc = LIBISCSI_ERR_IDBM; + goto out; + } + + _idbm_iface_print(iface, f); + + _idbm_unlock(ctx); + +out: + if (f != NULL) + fclose(f); + return rc; +} + +// mimic of iscsi_sysfs_read_iface() in iscsi_sysfs.c. +static int _fill_hw_iface_from_sys(struct iscsi_context *ctx, + struct iscsi_iface *iface, + const char *iface_kern_id) +{ + int rc = LIBISCSI_OK; + char *sysfs_iface_dir_path = NULL; + uint32_t tmp_host_no = 0; + uint32_t iface_num = 0; + int iface_type = 0; + + + assert(ctx != NULL); + assert(iface != NULL); + assert(iface_kern_id != NULL); + + sysfs_iface_dir_path = malloc(PATH_MAX); + _alloc_null_check(ctx, sysfs_iface_dir_path, rc, out); + snprintf(sysfs_iface_dir_path, PATH_MAX, "%s/%s", + _ISCSI_SYS_IFACE_DIR, iface_kern_id); + + _good(_sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "ipaddress", + iface->ipaddress, + sizeof(iface->ipaddress) / + sizeof(char), DEFAULT_IPADDRESS), + rc, out); + + if (strncmp(iface_kern_id, "ipv4", strlen("ipv4")) == 0) { + iface->is_ipv6 = false; + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "bootproto", iface->bootproto, + sizeof(iface->bootproto) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "gateway", + iface->gateway, + sizeof(iface->gateway) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "subnet", + iface->subnet_mask, + sizeof(iface->subnet_mask) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "dhcp_alt_client_id_en", + iface->dhcp_alt_client_id, + sizeof(iface->dhcp_alt_client_id) / + sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "dhcp_alt_client_id", + iface->dhcp_alt_client_id, + sizeof(iface->dhcp_alt_client_id) / + sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "dhcp_dns_address_en", iface->dhcp_dns, + sizeof(iface->dhcp_dns) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "dhcp_learn_iqn_en", iface->dhcp_learn_iqn, + sizeof(iface->dhcp_learn_iqn) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "dhcp_req_vendor_id_en", + iface->dhcp_req_vendor_id_state, + sizeof(iface->dhcp_req_vendor_id_state) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "dhcp_use_vendor_id_en", + iface->dhcp_vendor_id_state, + sizeof(iface->dhcp_vendor_id_state) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "dhcp_vendor_id", iface->dhcp_vendor_id, + sizeof(iface->dhcp_vendor_id) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "dhcp_slp_da_info_en", iface->dhcp_slp_da, + sizeof(iface->dhcp_slp_da) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "fragment_disable", iface->fragmentation, + sizeof(iface->fragmentation) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "grat_arp_en", iface->gratuitous_arp, + sizeof(iface->gratuitous_arp) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "incoming_forwarding_en", + iface->incoming_forwarding, + sizeof(iface->incoming_forwarding) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "tos_en", + iface->tos_state, sizeof(iface->tos_state) / + sizeof(char), ""); + _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "tos", + &iface->tos, 0, true); + _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "ttl", + &iface->ttl, 0, true); + } else { + iface->is_ipv6 = true; + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "ipaddr_autocfg", + iface->ipv6_autocfg, + sizeof(iface->ipv6_autocfg) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "link_local_addr", iface->ipv6_linklocal, + sizeof(iface->ipv6_linklocal) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "link_local_autocfg", + iface->linklocal_autocfg, + sizeof(iface->linklocal_autocfg) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "router_addr", iface->ipv6_router, + sizeof(iface->ipv6_router) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "router_state", iface->router_autocfg, + sizeof(iface->router_autocfg) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "grat_neighbor_adv_en", + iface->gratuitous_neighbor_adv, + sizeof(iface->gratuitous_neighbor_adv) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "mld_en", + iface->mld, sizeof(iface->mld) / + sizeof(char), ""); + _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, + "dup_addr_detect_cnt", + &iface->dup_addr_detect_cnt, 0, true); + _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "hop_limit", + &iface->hop_limit, 0, true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, + "flow_label", &iface->flow_label, 0, true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, + "nd_reachable_tmo", + &iface->nd_reachable_tmo, 0, true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "nd_rexmit_time", + &iface->nd_rexmit_time, 0, true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "nd_stale_tmo", + &iface->nd_stale_tmo, 0, true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, + "router_adv_link_mtu", + &iface->router_adv_link_mtu, 0, true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "traffic_class", + &iface->traffic_class, 0, true); + } + + _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "port", &iface->port, 0, + true); + _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "mtu", &iface->mtu, 0, + true); + _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "vlan_id", + &iface->vlan_id, UINT16_MAX, true); + _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "vlan_priority", + &iface->vlan_priority, UINT8_MAX, true); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "vlan_enabled", + iface->vlan_state, sizeof(iface->vlan_state) / + sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "enabled", iface->state, + sizeof(iface->state) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "delayed_ack_en", + iface->delayed_ack, + sizeof(iface->delayed_ack) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "tcp_nagle_disable", + iface->nagle, sizeof(iface->nagle) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "tcp_wsf_disable", + iface->tcp_wsf_state, + sizeof(iface->tcp_wsf_state) / sizeof(char), ""); + _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "tcp_wsf", + &iface->tcp_wsf, 0, true); + _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "tcp_timer_scale", + &iface->tcp_timer_scale, 0, true); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "tcp_timestamp_en", + iface->tcp_timestamp, + sizeof(iface->tcp_timestamp) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "redirect_en", + iface->redirect, + sizeof(iface->redirect) / sizeof(char), ""); + _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "def_taskmgmt_tmo", + &iface->def_task_mgmt_tmo, 0, true); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "header_digest", + iface->header_digest, + sizeof(iface->header_digest) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "data_digest", + iface->data_digest, + sizeof(iface->data_digest) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "immediate_data", + iface->immediate_data, + sizeof(iface->immediate_data) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "initial_r2t", + iface->initial_r2t, + sizeof(iface->initial_r2t) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "data_seq_in_order", + iface->data_seq_inorder, + sizeof(iface->data_seq_inorder) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "data_pdu_in_order", + iface->data_pdu_inorder, + sizeof(iface->data_pdu_inorder) / sizeof(char), ""); + _sysfs_prop_get_u8(ctx, sysfs_iface_dir_path, "erl", &iface->erl, 0, + true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "max_recv_dlength", + &iface->max_recv_dlength, 0, true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "first_burst_len", + &iface->first_burst_len, 0, true); + _sysfs_prop_get_u16(ctx, sysfs_iface_dir_path, "max_outstanding_r2t", + &iface->max_out_r2t, 0, true); + _sysfs_prop_get_u32(ctx, sysfs_iface_dir_path, "max_burst_len", + &iface->max_burst_len, 0, true); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "chap_auth", + iface->chap_auth, + sizeof(iface->chap_auth) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "bidi_chap", + iface->bidi_chap, + sizeof(iface->bidi_chap) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, "strict_login_comp_en", + iface->strict_login_comp, + sizeof(iface->strict_login_comp) / sizeof(char), + ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "discovery_auth_optional", + iface->discovery_auth, + sizeof(iface->discovery_auth) / sizeof(char), ""); + _sysfs_prop_get_str(ctx, sysfs_iface_dir_path, + "discovery_logout", + iface->discovery_logout, + sizeof(iface->discovery_logout) / sizeof(char), ""); + + if (sscanf(iface_kern_id, "ipv%d-iface-%" SCNu32 "-%" SCNu32, + &iface_type, &tmp_host_no, &iface_num) == 3) + iface->iface_num = iface_num; + + snprintf(iface->name, sizeof(iface->name)/sizeof(char), + "%s.%s.%s.%u", iface->transport_name, + iface->hwaddress, iface->is_ipv6 ? "ipv6" : "ipv4", + iface->iface_num); + +out: + free(sysfs_iface_dir_path); + return rc; +} + +int iscsi_ifaces_get(struct iscsi_context *ctx, struct iscsi_iface ***ifaces, + uint32_t *iface_count) +{ + int rc = LIBISCSI_OK; + struct dirent **namelist = NULL; + int n = 0; + size_t i = 0; + struct iscsi_iface *iface = NULL; + int j = 0; + uint32_t real_iface_count = 0; + + assert(ctx != NULL); + assert(ifaces != NULL); + assert(iface_count != NULL); + + *ifaces = NULL; + *iface_count = 0; + + _good(_idbm_lock(ctx), rc, out); + + _good(_scandir(ctx, IFACE_CONFIG_DIR, &namelist, &n), rc, out); + _debug(ctx, "Got %d iface from %s folder", *iface_count, + IFACE_CONFIG_DIR); + *iface_count = (n + sizeof(_DEFAULT_IFACES)/sizeof(struct iscsi_iface)) + & UINT32_MAX; + *ifaces = (struct iscsi_iface **) calloc(*iface_count, + sizeof(struct iscsi_iface *)); + _alloc_null_check(ctx, *ifaces, rc, out); + + for (j = 0; j < n; ++j) { + _good(_idbm_iface_get(ctx, namelist[j]->d_name, &iface), + rc, out); + if (iface != NULL) { + (*ifaces)[real_iface_count++] = iface; + } + } + + for (i = 0; i < sizeof(_DEFAULT_IFACES)/sizeof(struct iscsi_iface); + ++i) { + iface = calloc(1, sizeof(struct iscsi_iface)); + _alloc_null_check(ctx, iface, rc, out); + (*ifaces)[real_iface_count++] = iface; + memcpy(iface, &_DEFAULT_IFACES[i], sizeof(struct iscsi_iface)); + } + + *iface_count = real_iface_count; + +out: + _scandir_free(namelist, n); + _idbm_unlock(ctx); + if (rc != LIBISCSI_OK) { + iscsi_ifaces_free(*ifaces, *iface_count); + *ifaces = NULL; + *iface_count = 0; + } + return rc; +} + +void iscsi_ifaces_free(struct iscsi_iface **ifaces, uint32_t iface_count) +{ + uint32_t i = 0; + + if ((ifaces == NULL) || (iface_count == 0)) + return; + + for (i = 0; i < iface_count; ++i) + iscsi_iface_free(ifaces[i]); + free (ifaces); +} + +static bool _iface_is_bound_by_hwaddr(struct iscsi_iface *iface) +{ + if (iface && strlen(iface->hwaddress) && + strcmp(iface->hwaddress, DEFAULT_HWADDRESS)) + return true; + return false; +} + +static bool _iface_is_bound_by_netdev(struct iscsi_iface *iface) +{ + if (iface && strlen(iface->netdev) && + strcmp(iface->netdev, DEFAULT_NETDEV)) + return true; + return false; +} + +bool _iface_is_valid(struct iscsi_iface *iface) +{ + if (!iface) + return false; + + if (strlen(iface->name) == 0) + return false; + + if (strlen(iface->transport_name) == 0) + return false; + + if (_iface_is_bound_by_hwaddr(iface)) + return true; + + if (_iface_is_bound_by_netdev(iface)) + return true; + + /* bound by transport name */ + return true; +} + +bool iscsi_is_default_iface(struct iscsi_iface *iface) +{ + size_t i = 0; + for (; i < sizeof(_DEFAULT_IFACES)/sizeof(struct iscsi_iface); ++i) { + if (strcmp(iface->name, _DEFAULT_IFACES[i].name) == 0) + return true; + } + return false; +} + +const char *iscsi_iface_dump_config(struct iscsi_iface *iface) +{ + FILE *f = NULL; + char *buff = NULL; + + assert(iface != NULL); + + buff = calloc(1, _IFACE_DUMP_SIZE); + if (buff == NULL) + return NULL; + + f = fmemopen(buff, _IFACE_DUMP_SIZE - 1, "w"); + if (f == NULL) { + free(buff); + return NULL; + } + + _idbm_iface_print(iface, f); + + fclose(f); + + return buff; +} + +void iscsi_iface_print_config(struct iscsi_iface *iface) +{ + assert(iface != NULL); + _idbm_iface_print(iface, stdout); +} + +int iscsi_iface_get(struct iscsi_context *ctx, const char *iface_name, + struct iscsi_iface **iface) +{ + int rc = LIBISCSI_OK; + assert(ctx != NULL); + assert(iface_name != NULL); + assert(strlen(iface_name) != 0); + assert(iface != NULL); + + *iface = NULL; + + rc = _idbm_lock(ctx); + if (rc != LIBISCSI_OK) + return rc; + + rc = _idbm_iface_get(ctx, iface_name, iface); + if (*iface == NULL) + rc = LIBISCSI_ERR_IDBM; + + _idbm_unlock(ctx); + return rc; } -void _iscsi_iface_free(struct iscsi_iface *iface) +void iscsi_iface_free(struct iscsi_iface *iface) { free(iface); } diff --git a/libopeniscsiusr/iface.h b/libopeniscsiusr/iface.h index 85aa97c..8a1e670 100644 --- a/libopeniscsiusr/iface.h +++ b/libopeniscsiusr/iface.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Red Hat, Inc. + * 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 @@ -16,20 +16,126 @@ * * Author: Gris Ge <fge@redhat.com> */ + #ifndef __ISCSI_USR_IFACE_H__ #define __ISCSI_USR_IFACE_H__ #include "libopeniscsiusr/libopeniscsiusr_common.h" #include <stdint.h> +#include <netdb.h> -/* - * BUG(Gris Ge): Should include 'iface_kern_id' parameter. +#define VALUE_MAXVAL 256 /* the maximum length of 223 bytes in the RFC. */ +/* ^ VALUE_MAXVAL is copied from usr/idbm.h + * The RFC 3720 only said: + * If not otherwise specified, the maximum length of a simple-value (not + * its encoded representation) is 255 bytes, not including the delimiter + * (comma or zero byte). */ -__DLL_LOCAL int _iscsi_iface_get(struct iscsi_context *ctx, uint32_t host_id, - uint32_t sid, const char *iface_kern_id, - struct iscsi_iface **iface); -__DLL_LOCAL void _iscsi_iface_free(struct iscsi_iface *iface); +#define ISCSI_MAX_IFACE_LEN 65 +#define ISCSI_TRANSPORT_NAME_MAXLEN 16 +#define ISCSI_MAX_STR_LEN 80 +#define ISCSI_HWADDRESS_BUF_SIZE 18 +#define TARGET_NAME_MAXLEN VALUE_MAXVAL +/* ^ TODO(Gris Ge): Above 5 constants are copy from usr/config.h, need to + * verify them in linux kernel code + */ + +struct iscsi_iface { + /* iscsi iface record name */ + char name[ISCSI_MAX_IFACE_LEN]; + uint32_t iface_num; + /* network layer iface name (eth0) */ + char netdev[IFNAMSIZ]; + char ipaddress[NI_MAXHOST]; + char subnet_mask[NI_MAXHOST]; + char gateway[NI_MAXHOST]; + char bootproto[ISCSI_MAX_STR_LEN]; + char ipv6_linklocal[NI_MAXHOST]; + char ipv6_router[NI_MAXHOST]; + char ipv6_autocfg[NI_MAXHOST]; + char linklocal_autocfg[NI_MAXHOST]; + char router_autocfg[NI_MAXHOST]; +// uint8_t prefix_len; + /* ^ prefix_len is removed, as linux kernel has no such sysfs property + * and there is no actual code in usr/ folder set this property + */ + uint16_t vlan_id; + uint8_t vlan_priority; + char vlan_state[ISCSI_MAX_STR_LEN]; + char state[ISCSI_MAX_STR_LEN]; /* 0 = disable, + * 1 = enable */ + uint16_t mtu; + uint16_t port; + char delayed_ack[ISCSI_MAX_STR_LEN]; + char nagle[ISCSI_MAX_STR_LEN]; + char tcp_wsf_state[ISCSI_MAX_STR_LEN]; + uint8_t tcp_wsf; + uint8_t tcp_timer_scale; + char tcp_timestamp[ISCSI_MAX_STR_LEN]; + char dhcp_dns[ISCSI_MAX_STR_LEN]; + char dhcp_slp_da[ISCSI_MAX_STR_LEN]; + char tos_state[ISCSI_MAX_STR_LEN]; + uint8_t tos; + char gratuitous_arp[ISCSI_MAX_STR_LEN]; + char dhcp_alt_client_id_state[ISCSI_MAX_STR_LEN]; + char dhcp_alt_client_id[ISCSI_MAX_STR_LEN]; + char dhcp_req_vendor_id_state[ISCSI_MAX_STR_LEN]; + char dhcp_vendor_id_state[ISCSI_MAX_STR_LEN]; + char dhcp_vendor_id[ISCSI_MAX_STR_LEN]; + char dhcp_learn_iqn[ISCSI_MAX_STR_LEN]; + char fragmentation[ISCSI_MAX_STR_LEN]; + char incoming_forwarding[ISCSI_MAX_STR_LEN]; + uint8_t ttl; + char gratuitous_neighbor_adv[ISCSI_MAX_STR_LEN]; + char redirect[ISCSI_MAX_STR_LEN]; + char mld[ISCSI_MAX_STR_LEN]; + uint32_t flow_label; + uint32_t traffic_class; + uint8_t hop_limit; + uint32_t nd_reachable_tmo; + uint32_t nd_rexmit_time; + uint32_t nd_stale_tmo; + uint8_t dup_addr_detect_cnt; + uint32_t router_adv_link_mtu; + uint16_t def_task_mgmt_tmo; + char header_digest[ISCSI_MAX_STR_LEN]; + char data_digest[ISCSI_MAX_STR_LEN]; + char immediate_data[ISCSI_MAX_STR_LEN]; + char initial_r2t[ISCSI_MAX_STR_LEN]; + char data_seq_inorder[ISCSI_MAX_STR_LEN]; + char data_pdu_inorder[ISCSI_MAX_STR_LEN]; + uint8_t erl; + uint32_t max_recv_dlength; + uint32_t first_burst_len; + uint16_t max_out_r2t; + uint32_t max_burst_len; + char chap_auth[ISCSI_MAX_STR_LEN]; + char bidi_chap[ISCSI_MAX_STR_LEN]; + char strict_login_comp[ISCSI_MAX_STR_LEN]; + char discovery_auth[ISCSI_MAX_STR_LEN]; + char discovery_logout[ISCSI_MAX_STR_LEN]; + char port_state[ISCSI_MAX_STR_LEN]; + char port_speed[ISCSI_MAX_STR_LEN]; + /* + * TODO: we may have to make this bigger and interconnect + * specific for infiniband + */ + char hwaddress[ISCSI_HWADDRESS_BUF_SIZE]; + char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; + /* + * This is only used for boot now, but the iser guys + * can use this for their virtualization idea. + */ + char alias[TARGET_NAME_MAXLEN + 1]; + char iname[TARGET_NAME_MAXLEN + 1]; + bool is_ipv6; +}; + +__DLL_LOCAL int _iscsi_iface_get_from_sysfs(struct iscsi_context *ctx, + uint32_t host_id, uint32_t sid, + struct iscsi_iface **iface); +__DLL_LOCAL bool _iface_is_valid(struct iscsi_iface *iface); #endif /* End of __ISCSI_USR_IFACE_H__ */ diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h index bf8a845..7b5a280 100644 --- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h +++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h @@ -25,6 +25,7 @@ extern "C" { #include <stdint.h> #include <stdarg.h> +#include <stdbool.h> #include "libopeniscsiusr_common.h" #include "libopeniscsiusr_session.h" @@ -324,6 +325,160 @@ __DLL_EXPORT int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid, */ __DLL_EXPORT void iscsi_session_free(struct iscsi_session *se); +/** + * iscsi_default_iface_setup() - Setup default iSCSI interfaces. + * + * Setup default iSCSI interfaces for iSCSI TCP, iSER and iSCSI hardware offload + * cards. It is required after new iSCSI hardware offload card installed. + * + * Below kernel modules will be loaded when required by this function: + * + * * cxgb3i + * * cxgb4i + * * bnx2i + * + * It will also create configuration files for iSCSI hardware offload cards in + * /etc/iscsi/ifaces/<iface_name>. + * + * @ctx: + * Pointer of 'struct iscsi_context'. + * + * 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_default_iface_setup(struct iscsi_context *ctx); + +/** + * iscsi_ifaces_get() - Retrieve all iSCSI interfaces. + * + * Retrieves all iSCSI interfaces. For the properties of 'struct iscsi_iface', + * please refer to the functions defined in 'libopeniscsiusr_iface.h' file. + * The returned results contains default iSCSI interfaces(iser and iscsi_tcp) + * and iSCSI interfaces configured in "/etc/iscsi/ifaces/". + * Illegal configuration file will be skipped and warned. + * To generate iSCSI interface configuration when new card installed, please + * use iscsi_default_iface_setup(). + * + * @ctx: + * Pointer of 'struct iscsi_context'. + * If this pointer is NULL, your program will be terminated by assert. + * @ifaces: + * Output pointer of 'struct iscsi_iface' pointer array. Its memory + * should be freed by iscsi_ifaces_free(). + * If this pointer is NULL, your program will be terminated by assert. + * @iface_count: + * Output pointer of uint32_t. Will store the size of + * 'struct iscsi_iface' pointer array. + * + * Return: + * int. Valid error codes are: + * + * * LIBISCSI_OK + * + * * LIBISCSI_ERR_BUG + * + * * LIBISCSI_ERR_NOMEM + * + * * LIBISCSI_ERR_ACCESS + * + * * LIBISCSI_ERR_SYSFS_LOOKUP + * + * Error number could be converted to string by iscsi_strerror(). + */ +__DLL_EXPORT int iscsi_ifaces_get(struct iscsi_context *ctx, + struct iscsi_iface ***ifaces, + uint32_t *iface_count); + +/** + * iscsi_ifaces_free() - Free the memory of 'struct iscsi_iface' pointer + * array + * + * Free the memory of 'iscsi_iface' pointer array generated by + * 'iscsi_ifaces_get()'. + * If provided 'ifaces' pointer is NULL or 'iface_count' is 0, do nothing. + * + * @ifaces: + * Pointer of 'struct iscsi_iface' pointer array. + * @iface_count: + * uint32_t, the size of 'struct iscsi_iface' pointer array. + * + * Return: + * void + */ +__DLL_EXPORT void iscsi_ifaces_free(struct iscsi_iface **ifaces, + uint32_t iface_count); + +/** + * iscsi_iface_get() - Retrieve specified iSCSI interface. + * + * Retrieves specified iSCSI interfaces by reading configuration from + * "/etc/iscsi/iface/<iface_name>". + * To generate iSCSI interface configuration when new card installed, please + * use iscsi_default_iface_setup(). + * Illegal configuration file will be treated as error LIBISCSI_ERR_IDBM. + * Configuration file not found will be treated as error LIBISCSI_ERR_INVAL. + * + * @ctx: + * Pointer of 'struct iscsi_context'. + * If this pointer is NULL, your program will be terminated by assert. + * @iface_name: + * String. Name of iSCSI interface. Also the file name of configuration + * file "/etc/iscsi/iface/<iface_name>". + * If this pointer is NULL or empty string, your program will be terminated + * by assert. + * @iface: + * Output pointer of 'struct iscsi_iface'. Its memory should be freed by + * iscsi_iface_free(). + * If this pointer is NULL, your program will be terminated by assert. + * + * 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_iface_get(struct iscsi_context *ctx, + const char *iface_name, + struct iscsi_iface **iface); + +/** + * iscsi_iface_free() - Free the memory of 'struct iscsi_iface' pointer. + * + * Free the memory of 'iscsi_iface' pointer generated by 'iscsi_iface_get()'. + * If provided 'iface' pointer is NULL, do nothing. + * + * @iface: + * Pointer of 'struct iscsi_iface' pointer. + * + * Return: + * void + */ +__DLL_EXPORT void iscsi_iface_free(struct iscsi_iface *iface); #ifdef __cplusplus } /* End of extern "C" */ diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h index 383ff19..334cc4e 100644 --- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h +++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h @@ -36,6 +36,15 @@ #define LIBISCSI_ERR_NOMEM 3 /* ^ Could not allocate resource for operation */ +#define LIBISCSI_ERR_IDBM 6 +/* ^ Error accessing/managing iSCSI DB */ + +#define LIBISCSI_ERR_INVAL 7 +/* ^ Invalid argument */ + +#define LIBISCSI_ERR_TRANS_NOT_FOUND 12 +/* ^ iSCSI transport module not loaded in kernel or iscsid */ + #define LIBISCSI_ERR_ACCESS 13 /* ^ Permission denied */ diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h index ffba1f7..23573bf 100644 --- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h +++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_iface.h @@ -173,4 +173,57 @@ __DLL_EXPORT const char *iscsi_iface_port_speed_get(struct iscsi_iface *iface); */ __DLL_EXPORT const char *iscsi_iface_name_get(struct iscsi_iface *iface); +/** + * iscsi_iface_dump_config() - Dump all configurations of specified iSCSI + * interface. + * + * Dump all configurations of specified iSCSI interface. Will skip empty + * configuration so that output string could be saved directly to + * /etc/iscsi/ifaces/<iface_name> file. + * + * @iface: + * Pointer of 'struct iscsi_iface'. + * If this pointer is NULL, your program will be terminated by assert. + * + * Return: + * const char *. + * Need to free this memory by free(). + */ +__DLL_EXPORT const char *iscsi_iface_dump_config(struct iscsi_iface *iface); + +/** + * iscsi_iface_dump_config() - Print all configurations of specified iSCSI + * interface to STDOUT. + * + * Print all configurations of specified iSCSI interface. + * For empty configuration, it will be shown as "name = <empty>". + * + * @iface: + * Pointer of 'struct iscsi_iface'. + * If this pointer is NULL, your program will be terminated by assert. + * + * Return: + * void + */ +__DLL_EXPORT void iscsi_iface_print_config(struct iscsi_iface *iface); + +/** + * iscsi_is_default_iface() - Whether specified iSCSI interface is default + * interface. + * + * Check whether specified iSCSI interface is one of the default interfaces. + * Currently, default interfaces are : + * + * * Interface 'default' using 'iscsi_tcp' kernel module. + * + * * Interface 'iser' is using 'ib_iser' kernel module. + * + * @iface: + * Pointer of 'struct iscsi_iface'. + * + * Return: + * bool. + */ +__DLL_EXPORT bool iscsi_is_default_iface(struct iscsi_iface *iface); + #endif /* End of _LIB_OPEN_ISCSI_USR_IFACE_H_ */ diff --git a/libopeniscsiusr/misc.c b/libopeniscsiusr/misc.c index 93db919..9111966 100644 --- a/libopeniscsiusr/misc.c +++ b/libopeniscsiusr/misc.c @@ -24,6 +24,12 @@ #include <dirent.h> #include <string.h> #include <unistd.h> +#include <net/if.h> +#include <sys/socket.h> +#include <linux/ethtool.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <net/if_arp.h> #include "libopeniscsiusr/libopeniscsiusr.h" #include "misc.h" @@ -63,6 +69,10 @@ static const struct _num_str_conv _ISCSI_RC_MSG_CONV[] = { {LIBISCSI_ERR_ACCESS, "Permission deny"}, {LIBISCSI_ERR_NOMEM, "Out of memory"}, {LIBISCSI_ERR_SYSFS_LOOKUP, "Could not lookup object in sysfs"}, + {LIBISCSI_ERR_IDBM, "Error accessing/managing iSCSI DB"}, + {LIBISCSI_ERR_TRANS_NOT_FOUND, + "iSCSI transport module not loaded in kernel or iscsid"}, + {LIBISCSI_ERR_INVAL, "Invalid argument"}, }; _iscsi_str_func_gen(iscsi_strerror, int, rc, _ISCSI_RC_MSG_CONV); @@ -122,3 +132,200 @@ bool _file_exists(const char *path) else return false; } + +static bool _is_eth(struct iscsi_context *ctx, const char *if_name) +{ + struct ifreq ifr; + int sockfd = -1; + char strerr_buff[_STRERR_BUFF_LEN]; + + assert(if_name != NULL); + + memset(&ifr, 0, sizeof(ifr)); + + _strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + _warn(ctx, "Failed to create SOCK_DGRAM AF_INET socket: %d %s", + errno, _strerror(errno, strerr_buff)); + return false; + } + + if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) != 0) { + _warn(ctx, "IOCTL SIOCGIFHWADDR to %s failed: %d %s", if_name, + errno, _strerror(errno, strerr_buff)); + close(sockfd); + return false; + } + + close(sockfd); + + if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER) + return true; + + return false; +} + +/* + * driver_name should be char[_ETH_DRIVER_NAME_MAX_LEN] + */ +static int _eth_driver_get(struct iscsi_context *ctx, const char *if_name, + char *driver_name) +{ + int sockfd = -1; + struct ethtool_drvinfo drvinfo; + struct ifreq ifr; + char strerr_buff[_STRERR_BUFF_LEN]; + + assert(ctx != NULL); + assert(if_name != NULL); + assert(driver_name != NULL); + + memset(&ifr, 0, sizeof(ifr)); + memset(&drvinfo, 0, sizeof(drvinfo)); + + _strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t) &drvinfo; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + _error(ctx, "Failed to create SOCK_DGRAM AF_INET socket: %d %s", + errno, _strerror(errno, strerr_buff)); + return LIBISCSI_ERR_BUG; + } + + if (ioctl(sockfd, SIOCETHTOOL, &ifr) != 0) { + _warn(ctx, "IOCTL SIOCETHTOOL to %s failed: %d %s", if_name, + errno, _strerror(errno, strerr_buff)); + close(sockfd); + return LIBISCSI_ERR_BUG; + } + close(sockfd); + snprintf(driver_name, _ETH_DRIVER_NAME_MAX_LEN, "%s", drvinfo.driver); + + return LIBISCSI_OK; +} + +int _eth_ifs_get(struct iscsi_context *ctx, struct _eth_if ***eifs, + uint32_t *eif_count) +{ + int rc = LIBISCSI_OK; + struct if_nameindex *if_ni = NULL; + struct if_nameindex *if_i = NULL; + struct _eth_if *eif = NULL; + uint32_t tmp_count = 0; + + assert(ctx != NULL); + assert(eifs != NULL); + assert(eif_count != NULL); + + *eifs = NULL; + *eif_count = 0; + + if_ni = if_nameindex(); + _alloc_null_check(ctx, if_ni, rc, out); + + for (if_i = if_ni; if_i && if_i->if_index && if_i->if_name; ++if_i) + tmp_count++; + + if (tmp_count == 0) + goto out; + + *eifs = calloc(tmp_count, sizeof(struct _eth_if *)); + _alloc_null_check(ctx, *eifs, rc, out); + + for (if_i = if_ni; if_i && if_i->if_index && if_i->if_name; ++if_i) { + if (! _is_eth(ctx, if_i->if_name)) + continue; + eif = calloc(1, sizeof(struct _eth_if)); + _alloc_null_check(ctx, eif, rc, out); + (*eifs)[(*eif_count)++] = eif; + snprintf(eif->if_name, sizeof(eif->if_name)/sizeof(char), + "%s", if_i->if_name); + _good(_eth_driver_get(ctx, eif->if_name, eif->driver_name), + rc, out); + } + +out: + if (rc != LIBISCSI_OK) { + _eth_ifs_free(*eifs, *eif_count); + *eifs = NULL; + *eif_count = 0; + } + if (if_ni != NULL) + if_freenameindex(if_ni); + return rc; +} + +void _eth_ifs_free(struct _eth_if **eifs, uint32_t eif_count) +{ + uint32_t i = 0; + + if ((eif_count == 0) || (eifs == NULL)) + return; + + for (; i < eif_count; ++i) + free(eifs[i]); + free(eifs); +} + +void _scandir_free(struct dirent **namelist, int count) +{ + int i = 0; + + if ((namelist == NULL) || (count == 0)) + return; + + for (i = count - 1; i >= 0; --i) + free(namelist[i]); + free(namelist); +} + +int _scandir(struct iscsi_context *ctx, const char *dir_path, + struct dirent ***namelist, int *count) +{ + int rc = LIBISCSI_OK; + int errno_save = 0; + + assert(ctx != NULL); + assert(dir_path != NULL); + assert(namelist != NULL); + assert(count != NULL); + + *namelist = NULL; + *count = 0; + + *count = scandir(dir_path, namelist, _scan_filter_skip_dot, alphasort); + if (*count < 0) { + errno_save = errno; + if (errno_save == ENOENT) { + *count = 0; + goto out; + } + if (errno_save == ENOMEM) { + rc = LIBISCSI_ERR_NOMEM; + goto out; + } + if (errno_save == ENOTDIR) { + rc = LIBISCSI_ERR_BUG; + _error(ctx, "Got ENOTDIR error when scandir %s", + dir_path); + goto out; + } + rc = LIBISCSI_ERR_BUG; + _error(ctx, "Got unexpected error %d when scandir %s", + errno_save, dir_path); + goto out; + } + +out: + if (rc != LIBISCSI_OK) { + _scandir_free(*namelist, *count); + *namelist = NULL; + *count = 0; + } + + return rc; +} diff --git a/libopeniscsiusr/misc.h b/libopeniscsiusr/misc.h index 1c5d696..9208bbd 100644 --- a/libopeniscsiusr/misc.h +++ b/libopeniscsiusr/misc.h @@ -24,6 +24,7 @@ #include <assert.h> #include <stdarg.h> #include <dirent.h> +#include <net/if.h> #include "libopeniscsiusr/libopeniscsiusr.h" @@ -79,8 +80,36 @@ __DLL_LOCAL void _iscsi_log_stderr(struct iscsi_context *ctx, int priority, } \ } while(0) +#define _STRERR_BUFF_LEN 1024 +#define _strerror(err_no, buff) \ + strerror_r(err_no, buff, _STRERR_BUFF_LEN) + +#define _strncpy(dst, src, size) \ + do { \ + strncpy(dst, src, size); \ + * (char *) (dst + (size - 1)) = '\0'; \ + } while(0) + __DLL_LOCAL int _scan_filter_skip_dot(const struct dirent *dir); __DLL_LOCAL bool _file_exists(const char *path); + +#define _ETH_DRIVER_NAME_MAX_LEN 32 +/* ^ Defined in linux/ethtool.h `struct ethtool_drvinfo`. */ + +struct _eth_if { + char driver_name[_ETH_DRIVER_NAME_MAX_LEN]; + char if_name[IF_NAMESIZE]; +}; + +__DLL_LOCAL int _eth_ifs_get(struct iscsi_context *ctx, + struct _eth_if ***eifs, uint32_t *eif_count); + +__DLL_LOCAL void _eth_ifs_free(struct _eth_if **eifs, uint32_t eif_count); + +__DLL_LOCAL int _scandir(struct iscsi_context *ctx, const char *dir_path, + struct dirent ***namelist, int *count); +__DLL_LOCAL void _scandir_free(struct dirent **namelist, int count); + #endif /* End of __ISCSI_USR_MISC_H__ */ diff --git a/libopeniscsiusr/session.c b/libopeniscsiusr/session.c index 0a04f89..4d9c57c 100644 --- a/libopeniscsiusr/session.c +++ b/libopeniscsiusr/session.c @@ -84,8 +84,6 @@ struct iscsi_session { struct iscsi_iface *iface; }; -static uint32_t session_str_to_sid(const char *session_str); - _iscsi_getter_func_gen(iscsi_session, sid, uint32_t); _iscsi_getter_func_gen(iscsi_session, persistent_address, const char *); _iscsi_getter_func_gen(iscsi_session, persistent_port, int32_t); @@ -103,19 +101,6 @@ _iscsi_getter_func_gen(iscsi_session, address, const char *); _iscsi_getter_func_gen(iscsi_session, port, int32_t); _iscsi_getter_func_gen(iscsi_session, iface, struct iscsi_iface *); -/* - * The session string is "session%u" used by /sys/class/iscsi_session/session%u. - * Return 0 if error parsing session string. - */ -static uint32_t session_str_to_sid(const char *session_str) -{ - uint32_t sid = 0; - - if (sscanf(session_str, "session%" SCNu32, &sid) != 1) - return 0; /* error */ - return sid; -} - int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid, struct iscsi_session **se) { @@ -189,25 +174,23 @@ int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid, rc, out); _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "recovery_tmo", - &((*se)->recovery_tmo), - -1), + &((*se)->recovery_tmo), -1, true), rc, out); _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "lu_reset_tmo", - &((*se)->lu_reset_tmo), -1), + &((*se)->lu_reset_tmo), -1, true), rc, out); - _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, - "tgt_reset_tmo", &((*se)->tgt_reset_tmo), -1), + _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "tgt_reset_tmo", + &((*se)->tgt_reset_tmo), -1, true), rc, out); _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "abort_tmo", - &((*se)->abort_tmo), -1), + &((*se)->abort_tmo), -1, true), rc, out); _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "tpgt", - &((*se)->tpgt), - INT32_MAX /* raise error if not found */), + &((*se)->tpgt), -1, true), rc, out); _good(_sysfs_prop_get_str(ctx, sysfs_con_dir_path, "persistent_address", @@ -217,27 +200,24 @@ int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid, rc, out); _good(_sysfs_prop_get_i32(ctx, sysfs_con_dir_path, "persistent_port", - &((*se)->persistent_port), -1), + &((*se)->persistent_port), -1, true), rc, out); - _good(_sysfs_prop_get_str(ctx, sysfs_con_dir_path, "address", - (*se)->address, - sizeof((*se)->address) / sizeof(char), - ""), - rc, out); + _sysfs_prop_get_str(ctx, sysfs_con_dir_path, "address", (*se)->address, + sizeof((*se)->address) / sizeof(char), ""); _good(_sysfs_prop_get_i32(ctx, sysfs_con_dir_path, "port", - &((*se)->port), -1), rc, out); + &((*se)->port), -1, false), rc, out); if ((strcmp((*se)->address, "") == 0) && (strcmp((*se)->persistent_address, "") != 0)) - strncpy((*se)->persistent_address, (*se)->address, - sizeof((*se)->persistent_address) / sizeof(char)); + _strncpy((*se)->persistent_address, (*se)->address, + sizeof((*se)->persistent_address) / sizeof(char)); if ((strcmp((*se)->address, "") != 0) && (strcmp((*se)->persistent_address, "") == 0)) - strncpy((*se)->address, (*se)->persistent_address, - sizeof((*se)->address) / sizeof(char)); + _strncpy((*se)->address, (*se)->persistent_address, + sizeof((*se)->address) / sizeof(char)); if (((*se)->persistent_port != -1) && ((*se)->port == -1)) @@ -249,8 +229,7 @@ int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid, _good(_iscsi_host_id_of_session(ctx, sid, &host_id), rc, out); - _good(_iscsi_iface_get(ctx, host_id, sid, NULL /*iface kernel id */, - &((*se)->iface)), + _good(_iscsi_iface_get_from_sysfs(ctx, host_id, sid, &((*se)->iface)), rc, out); out: @@ -265,13 +244,9 @@ int iscsi_sessions_get(struct iscsi_context *ctx, struct iscsi_session ***sessions, uint32_t *session_count) { - struct dirent **namelist = NULL; - int n = 0; int rc = LIBISCSI_OK; - int errno_save = 0; uint32_t i = 0; - uint32_t sid = 0; - int j = 0; + uint32_t *sids = NULL; assert(ctx != NULL); assert(sessions != NULL); @@ -280,49 +255,19 @@ int iscsi_sessions_get(struct iscsi_context *ctx, *sessions = NULL; *session_count = 0; - n = scandir(_ISCSI_SYS_SESSION_DIR, &namelist, _scan_filter_skip_dot, - alphasort); - if (n < 0) { - errno_save = errno; - if (errno_save == ENOENT) - goto out; - if (errno_save == ENOMEM) { - rc = LIBISCSI_ERR_NOMEM; - goto out; - } - if (errno_save == ENOTDIR) { - rc = LIBISCSI_ERR_BUG; - _error(ctx, "Got ENOTDIR error when scandir %s", - _ISCSI_SYS_SESSION_DIR); - goto out; - } - rc = LIBISCSI_ERR_BUG; - _error(ctx, "Got unexpected error %d when scandir %s", - errno_save, _ISCSI_SYS_SESSION_DIR); - goto out; - } - _info(ctx, "Got %d iSCSI sessions", n); - *sessions = (struct iscsi_session **) - calloc (sizeof(struct iscsi_session *), n); - _alloc_null_check(ctx, *sessions, rc, out); + _good(_iscsi_sids_get(ctx, &sids, session_count), rc ,out); - *session_count = n & UINT32_MAX; + *sessions = calloc (*session_count, sizeof(struct iscsi_session *)); + _alloc_null_check(ctx, *sessions, rc, out); for (i = 0; i < *session_count; ++i) { - sid = session_str_to_sid(namelist[i]->d_name); - if (sid == 0) { - _error(ctx, "Got illegal iscsi session string %s", - namelist[i]->d_name); - rc = LIBISCSI_ERR_BUG; - goto out; - } - _good(iscsi_session_get(ctx, sid, &((*sessions)[i])), rc, out); + _debug(ctx, "sid %" PRIu32, sids[i]); + _good(iscsi_session_get(ctx, sids[i], &((*sessions)[i])), + rc, out); } out: - for (j = n - 1; j >= 0; --j) - free(namelist[j]); - free(namelist); + free(sids); if (rc != LIBISCSI_OK) { iscsi_sessions_free(*sessions, *session_count); *sessions = NULL; @@ -334,7 +279,7 @@ out: void iscsi_session_free(struct iscsi_session *se) { if (se != NULL) - _iscsi_iface_free(se->iface); + iscsi_iface_free(se->iface); free(se); } diff --git a/libopeniscsiusr/sysfs.c b/libopeniscsiusr/sysfs.c index 70298f2..f568245 100644 --- a/libopeniscsiusr/sysfs.c +++ b/libopeniscsiusr/sysfs.c @@ -43,13 +43,45 @@ #define _SYS_NULL_STR "(null)" +#define _sysfs_prop_get_int_func_gen(func_name, out_type, type_max_value) \ + int func_name(struct iscsi_context *ctx, const char *dir_path, \ + const char *prop_name, out_type *val, \ + out_type default_value, bool ignore_error) \ + { \ + long long int tmp_val = 0; \ + int rc = LIBISCSI_OK; \ + long long int dv = default_value; \ + rc = iscsi_sysfs_prop_get_ll(ctx, dir_path, prop_name, \ + &tmp_val, (long long int) dv, \ + ignore_error); \ + if (rc == LIBISCSI_OK) \ + *val = tmp_val & type_max_value; \ + return rc; \ + } + + +enum _sysfs_dev_class { + _SYSFS_DEV_CLASS_ISCSI_SESSION, + _SYSFS_DEV_CLASS_ISCSI_HOST, +}; + static int sysfs_read_file(const char *path, uint8_t *buff, size_t buff_size); static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx, const char *dir_path, const char *prop_name, long long int *val, - long long int default_value); + long long int default_value, + bool ignore_error); + +/* + * dev_path should be char[PATH_MAX] + */ static int sysfs_get_dev_path(struct iscsi_context *ctx, const char *path, - char *dev_path); + enum _sysfs_dev_class class, char *dev_path); + +_sysfs_prop_get_int_func_gen(_sysfs_prop_get_u8, uint8_t, UINT8_MAX); +_sysfs_prop_get_int_func_gen(_sysfs_prop_get_u16, uint16_t, UINT16_MAX); +_sysfs_prop_get_int_func_gen(_sysfs_prop_get_i32, int32_t, INT32_MAX); +_sysfs_prop_get_int_func_gen(_sysfs_prop_get_u32, uint32_t, UINT32_MAX); /* * dev_path should be char[PATH_MAX]. @@ -136,7 +168,13 @@ int _sysfs_prop_get_str(struct iscsi_context *ctx, const char *dir_path, file_path, errno_save); } } else { - _debug(ctx, "Open '%s', got '%s'", file_path, buff); + if ((buff[0] == '\0') && (default_value != NULL)) { + memcpy(buff, (void *) default_value, + strlen(default_value) + 1); + _debug(ctx, "Open '%s', got NULL, using default value", + file_path, default_value); + } else + _debug(ctx, "Open '%s', got '%s'", file_path, buff); } return rc; } @@ -144,7 +182,7 @@ int _sysfs_prop_get_str(struct iscsi_context *ctx, const char *dir_path, static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx, const char *dir_path, const char *prop_name, long long int *val, - long long int default_value) + long long int default_value, bool ignore_error) { char file_path[PATH_MAX]; int rc = LIBISCSI_OK; @@ -163,7 +201,7 @@ static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx, errno_save = sysfs_read_file(file_path, buff, _INT32_STR_MAX_LEN); if (errno_save != 0) { if (errno_save == ENOENT) { - if (default_value == LLONG_MAX) { + if (! ignore_error) { rc = LIBISCSI_ERR_SYSFS_LOOKUP; _error(ctx, "Failed to read '%s': " "file '%s' does not exists", @@ -193,7 +231,7 @@ static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx, tmp_val = strtoll((const char *) buff, NULL, 10 /* base */); errno_save = errno; - if ((errno_save != 0) && (tmp_val == LONG_MAX)) { + if ((errno_save != 0) && (! ignore_error)) { rc = LIBISCSI_ERR_BUG; _error(ctx, "Sysfs: %s: Error when converting '%s' " "to number", file_path, (char *) buff, errno_save); @@ -207,45 +245,8 @@ static int iscsi_sysfs_prop_get_ll(struct iscsi_context *ctx, return rc; } -int _sysfs_prop_get_u32(struct iscsi_context *ctx, const char *dir_path, - const char *prop_name, uint32_t *val, - uint32_t default_value) -{ - long long int tmp_val = 0; - int rc = LIBISCSI_OK; - long long int dv = default_value; - - if (default_value == UINT32_MAX) - dv = LLONG_MAX; - - rc = iscsi_sysfs_prop_get_ll(ctx, dir_path, prop_name, &tmp_val, - (long long int) dv); - if (rc == LIBISCSI_OK) - *val = tmp_val & UINT32_MAX; - return rc; -} - -int _sysfs_prop_get_i32(struct iscsi_context *ctx, const char *dir_path, - const char *prop_name, int32_t *val, - int32_t default_value) -{ - long long int tmp_val = 0; - int rc = LIBISCSI_OK; - long long int dv = default_value; - - if (default_value == INT32_MAX) - dv = LLONG_MAX; - - rc = iscsi_sysfs_prop_get_ll(ctx, dir_path, prop_name, &tmp_val, - (long long int) dv); - - if (rc == LIBISCSI_OK) - *val = tmp_val & INT32_MAX; - return rc; -} - static int sysfs_get_dev_path(struct iscsi_context *ctx, const char *path, - char *dev_path) + enum _sysfs_dev_class class, char *dev_path) { int rc = LIBISCSI_OK; int errno_save = 0; @@ -268,13 +269,27 @@ static int sysfs_get_dev_path(struct iscsi_context *ctx, const char *path, goto out; } - reg_rc = regcomp(®ex, - "\\(.\\{1,\\}/devices/.\\{1,\\}/host[0-9]\\{1,\\}\\)/" - "session[0-9]\\{1,\\}/iscsi_session/", - 0 /* no flag */); - /* ^ BUG(Gris Ge): This is based on GUESS, should check linux kernel - * code on this - */ + switch (class) { + case _SYSFS_DEV_CLASS_ISCSI_SESSION: + reg_rc = regcomp(®ex, + "\\(.\\{1,\\}/devices/.\\{1,\\}/" + "host[0-9]\\{1,\\}\\)/" + "session[0-9]\\{1,\\}/iscsi_session/", + 0 /* no flag */); + break; + case _SYSFS_DEV_CLASS_ISCSI_HOST: + reg_rc = regcomp(®ex, + "\\(.\\{1,\\}/devices/.\\{1,\\}/" + "host[0-9]\\{1,\\}\\)/" + "iscsi_host/", + 0 /* no flag */); + break; + default: + rc = LIBISCSI_ERR_BUG; + _error(ctx, "BUG: sysfs_get_dev_path(): got unknown class %d", + class); + goto out; + } if (reg_rc != 0) { rc = LIBISCSI_ERR_SYSFS_LOOKUP; _error(ctx, "regcomp() failed %d", reg_rc); @@ -310,7 +325,6 @@ int _iscsi_host_id_of_session(struct iscsi_context *ctx, uint32_t sid, struct dirent **namelist = NULL; int n = 0; const char *host_id_str = NULL; - int i = 0; assert(ctx != NULL); assert(sid != 0); @@ -321,13 +335,15 @@ int _iscsi_host_id_of_session(struct iscsi_context *ctx, uint32_t sid, *host_id = 0; - _good(sysfs_get_dev_path(ctx, sys_se_dir_path, sys_dev_path), rc, out); + _good(sysfs_get_dev_path(ctx, sys_se_dir_path, + _SYSFS_DEV_CLASS_ISCSI_SESSION, sys_dev_path), + rc, out); snprintf(sys_scsi_host_dir_path, PATH_MAX, "%s/iscsi_host/", sys_dev_path); - n = scandir(sys_scsi_host_dir_path, &namelist, _scan_filter_skip_dot, - alphasort); + _good(_scandir(ctx, sys_scsi_host_dir_path, &namelist, &n), rc, out); + if (n != 1) { rc = LIBISCSI_ERR_SYSFS_LOOKUP; _error(ctx, "Got unexpected(should be 1) file in folder %s", @@ -343,9 +359,143 @@ int _iscsi_host_id_of_session(struct iscsi_context *ctx, uint32_t sid, } out: - for (i = n - 1; i >= 0; --i) - free(namelist[i]); - free(namelist); + _scandir_free(namelist, n); + + return rc; +} + +static int _iscsi_ids_get(struct iscsi_context *ctx, + uint32_t **ids, uint32_t *id_count, + const char *dir_path, const char *file_prefix) +{ + int rc = LIBISCSI_OK; + struct dirent **namelist = NULL; + int n = 0; + uint32_t i = 0; + const char *id_str = NULL; + char fmt_buff[128]; + + assert(ctx != NULL); + assert(ids != 0); + assert(id_count != NULL); + + *ids = NULL; + *id_count = 0; + + _good(_scandir(ctx, dir_path, &namelist, &n), rc, out); + _debug(ctx, "Got %d iSCSI %s", n, file_prefix); + + *id_count = n & UINT32_MAX; + + *ids = calloc(*id_count, sizeof(uint32_t)); + _alloc_null_check(ctx, *ids, rc, out); + + snprintf(fmt_buff, sizeof(fmt_buff)/sizeof(char), "%s%%" SCNu32, + file_prefix); + + for (i = 0; i < *id_count; ++i) { + id_str = namelist[i]->d_name; + if (sscanf(id_str, fmt_buff, &((*ids)[i])) != 1) { + rc = LIBISCSI_ERR_SYSFS_LOOKUP; + _error(ctx, "sscanf() failed on string %s", + id_str); + goto out; + } + _debug(ctx, "Got iSCSI %s id %" PRIu32, file_prefix, (*ids)[i]); + } + +out: + _scandir_free(namelist, n); + if (rc != LIBISCSI_OK) { + free(*ids); + *ids = NULL; + *id_count = 0; + } + return rc; +} + +int _iscsi_sids_get(struct iscsi_context *ctx, uint32_t **sids, + uint32_t *sid_count) +{ + return _iscsi_ids_get(ctx, sids, sid_count, _ISCSI_SYS_SESSION_DIR, + "session"); +} + +int _iscsi_hids_get(struct iscsi_context *ctx, uint32_t **hids, + uint32_t *hid_count) +{ + return _iscsi_ids_get(ctx, hids, hid_count, _ISCSI_SYS_HOST_DIR, + "host"); +} + +bool _iscsi_transport_is_loaded(const char *transport_name) +{ + char path[PATH_MAX]; + + if (transport_name == NULL) + return false; + + snprintf(path, PATH_MAX, "%s/%s", _ISCSI_SYS_TRANSPORT_DIR, + transport_name); + + if (access(path, F_OK) == 0) + return true; + + return false; +} + +int _iscsi_iface_kern_id_of_host_id(struct iscsi_context *ctx, + uint32_t host_id, char *iface_kern_id) +{ + char *sysfs_sh_path = NULL; + char *dev_path = NULL; + char *sysfs_iface_path = NULL; + int rc = LIBISCSI_OK; + struct dirent **namelist = NULL; + int n = 0; + + sysfs_sh_path = malloc(PATH_MAX); + _alloc_null_check(ctx, sysfs_sh_path, rc, out); + + dev_path = malloc(PATH_MAX); + _alloc_null_check(ctx, dev_path, rc, out); + + sysfs_iface_path = malloc(PATH_MAX); + _alloc_null_check(ctx, sysfs_iface_path, rc, out); + + snprintf(sysfs_sh_path, PATH_MAX, "%s/host%" PRIu32, + _ISCSI_SYS_HOST_DIR, host_id); + + _good(sysfs_get_dev_path(ctx, sysfs_sh_path, + _SYSFS_DEV_CLASS_ISCSI_HOST, dev_path), + rc, out); + + snprintf(sysfs_iface_path, PATH_MAX, "%s/iscsi_iface", dev_path); + + _good(_scandir(ctx, sysfs_iface_path, &namelist, &n), rc, out); + if (n == 0) { + rc = LIBISCSI_ERR_SYSFS_LOOKUP; + _debug(ctx, "No iSCSI interface for iSCSI host %" PRIu32, + host_id); + goto out; + } + + if (n != 1) { + rc = LIBISCSI_ERR_SYSFS_LOOKUP; + _debug(ctx, "Got unexpected(got %d, should be 1) file in " + "folder %s", n, sysfs_iface_path); + goto out; + } + + snprintf(iface_kern_id, PATH_MAX, "%s", namelist[0]->d_name); + _debug(ctx, "Found iSCSI iface '%s' for iSCSI host %" PRIu32, + iface_kern_id, host_id); + +out: + _scandir_free(namelist, n); + free(sysfs_sh_path); + free(dev_path); + free(sysfs_iface_path); return rc; } diff --git a/libopeniscsiusr/sysfs.h b/libopeniscsiusr/sysfs.h index 3b501c2..e285537 100644 --- a/libopeniscsiusr/sysfs.h +++ b/libopeniscsiusr/sysfs.h @@ -20,6 +20,7 @@ #define __ISCSI_USR_SYSFS_H__ #include <stdint.h> +#include <stdbool.h> #include "libopeniscsiusr/libopeniscsiusr_common.h" @@ -27,6 +28,7 @@ #define _ISCSI_SYS_CONNECTION_DIR "/sys/class/iscsi_connection" #define _ISCSI_SYS_HOST_DIR "/sys/class/iscsi_host" #define _ISCSI_SYS_IFACE_DIR "/sys/class/iscsi_iface" +#define _ISCSI_SYS_TRANSPORT_DIR "/sys/class/iscsi_transport" #define _SCSI_SYS_HOST_DIR "/sys/class/scsi_host" /* @@ -37,21 +39,52 @@ __DLL_LOCAL int _sysfs_prop_get_str(struct iscsi_context *ctx, char *buff, size_t buff_size, const char *default_value); +int _sysfs_prop_get_u8(struct iscsi_context *ctx, const char *dir_path, + const char *prop_name, uint8_t *val, + uint8_t default_value, bool ignore_error); + +int _sysfs_prop_get_u16(struct iscsi_context *ctx, const char *dir_path, + const char *prop_name, uint16_t *val, + uint16_t default_value, bool ignore_error); + /* * When default_value == UINT32_MAX, treat no such file as LIB_BUG. */ __DLL_LOCAL int _sysfs_prop_get_u32(struct iscsi_context *ctx, const char *dir_path, const char *prop_name, - uint32_t *val, uint32_t default_value); + uint32_t *val, uint32_t default_value, + bool ignore_error); /* * When default_value == INT32_MAX, treat no such file as LIB_BUG. */ __DLL_LOCAL int _sysfs_prop_get_i32(struct iscsi_context *ctx, const char *dir_path, const char *prop_name, - int32_t *val, int32_t default_value); + int32_t *val, int32_t default_value, + bool ignore_error); __DLL_LOCAL int _iscsi_host_id_of_session(struct iscsi_context *ctx, uint32_t sid, uint32_t *host_id); +/* + * iface_kern_id should be char[PATH_MAX] + */ +__DLL_LOCAL int _iscsi_iface_kern_id_of_host_id(struct iscsi_context *ctx, + uint32_t host_id, + char *iface_kern_id); + +/* + * The memory of (uint32_t *sids) should be freed by free(). + */ +__DLL_LOCAL int _iscsi_sids_get(struct iscsi_context *ctx, + uint32_t **sids, uint32_t *sid_count); + +/* + * The memory of (uint32_t *hids) should be freed by free(). + */ +__DLL_LOCAL int _iscsi_hids_get(struct iscsi_context *ctx, uint32_t **hids, + uint32_t *hid_count); + +__DLL_LOCAL bool _iscsi_transport_is_loaded(const char *transport_name); + #endif /* End of __ISCSI_USR_SYSFS_H__ */ diff --git a/libopeniscsiusr/tests/test_iface.c b/libopeniscsiusr/tests/test_iface.c new file mode 100644 index 0000000..f3ad8c6 --- /dev/null +++ b/libopeniscsiusr/tests/test_iface.c @@ -0,0 +1,112 @@ +/* + * 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/licenifaces/>. + * + * Author: Gris Ge <fge@redhat.com> + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include <pthread.h> +#include <unistd.h> +#include <assert.h> + +#include <libopeniscsiusr/libopeniscsiusr.h> + +#define _assert_print_prop_str_can_empty(struct_name, obj, prop_name) \ + do { \ + assert(struct_name##_##prop_name##_get(obj) != NULL); \ + printf("\t" # prop_name ": '%s'\n", \ + struct_name##_##prop_name##_get(obj)); \ + } while(0) + +#define _assert_print_prop_str_not_empty(struct_name, obj, prop_name) \ + do { \ + assert(struct_name##_##prop_name##_get(obj) != NULL); \ + assert(strlen(struct_name##_##prop_name##_get(obj)) != 0); \ + printf("\t" # prop_name ": '%s'\n", \ + struct_name##_##prop_name##_get(obj)); \ + } while(0) + +static void test_iface(struct iscsi_context *ctx, struct iscsi_iface *iface) +{ + struct iscsi_iface *tmp_iface = NULL; + const char *conf = NULL; + + assert(iface != NULL); + printf("\t#### Interface info ####\n"); + _assert_print_prop_str_not_empty(iscsi_iface, iface, name); + if (! iscsi_is_default_iface(iface)) { + assert(iscsi_iface_get(ctx, iscsi_iface_name_get(iface), + &tmp_iface) == LIBISCSI_OK); + _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface, + ipaddress); + _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface, + transport_name); + _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface, iname); + _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface, + hwaddress); + _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface, + netdev); + _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface, + port_state); + _assert_print_prop_str_can_empty(iscsi_iface, tmp_iface, + port_speed); + iscsi_iface_free(tmp_iface); + } + printf("\t########################\n"); + + conf = iscsi_iface_dump_config(iface); + assert(conf != NULL); + free((char *) conf); + iscsi_iface_print_config(iface); +} + +int main() +{ + struct iscsi_context *ctx = NULL; + struct iscsi_iface **ifaces = NULL; + uint32_t iface_count = 0; + uint32_t i = 0; + int rc = EXIT_SUCCESS; + + ctx = iscsi_context_new(); + iscsi_context_log_priority_set(ctx, LIBISCSI_LOG_PRIORITY_DEBUG); + + if (iscsi_default_iface_setup(ctx) != LIBISCSI_OK) { + printf("FAILED\n"); + rc = EXIT_FAILURE; + } + + if (iscsi_ifaces_get(ctx, &ifaces, &iface_count) != LIBISCSI_OK) { + printf("FAILED\n"); + rc = EXIT_FAILURE; + } else { + assert(iface_count >= 2); + /* we will have at least the default ifaces: + * iser and iscsi_tcp + */ + printf("\nGot %" PRIu32 " iSCSI ifaces\n", iface_count); + for (i = 0; i < iface_count; ++i) { + test_iface(ctx, ifaces[i]); + } + iscsi_ifaces_free(ifaces, iface_count); + } + iscsi_context_free(ctx); + exit(rc); +} diff --git a/libopeniscsiusr/version.h b/libopeniscsiusr/version.h new file mode 100644 index 0000000..f05d159 --- /dev/null +++ b/libopeniscsiusr/version.h @@ -0,0 +1,30 @@ +/* + * 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_OPEN_USR_VERSION_H__ +#define __ISCSI_OPEN_USR_VERSION_H__ + +/* + * iSCSI tools version. + * This may not be the same value as the kernel versions because + * some other maintainer could merge a patch without going through us + */ +#define ISCSI_VERSION_STR "2.0-876" + +#endif /* End of __ISCSI_OPEN_USR_VERSION_H__ */ |