summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGris Ge <fge@redhat.com>2018-01-19 21:27:08 +0800
committerGris Ge <fge@redhat.com>2018-01-22 18:26:06 +0800
commit3fa1142dc14f5b15ad2ded32a3c6f34e4457e2af (patch)
tree99ad23eefa87248695651b6243cea7dfbccbf357
parent5e9916b05d0b6404f414410ae080bf51a56047de (diff)
downloadopen-iscsi-3fa1142dc14f5b15ad2ded32a3c6f34e4457e2af.tar.gz
libopeniscsiusr: Add iSCSI session support.
* iscsi_sessions_get()/iscsi_sessions_free() for query all iSCSI session. * iscsi_session_get()/iscsi_session_free() for query specified iSCSI session id. * And a lot more functions for property query of `struct iscsi_session`. * New unit test case created 'tests/test_session.c'. Signed-off-by: Gris Ge <fge@redhat.com>
-rw-r--r--.gitignore1
-rw-r--r--libopeniscsiusr/Makefile13
-rw-r--r--libopeniscsiusr/docs/libopeniscsiusr.h.322
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h121
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h23
-rw-r--r--libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_session.h331
-rw-r--r--libopeniscsiusr/misc.c21
-rw-r--r--libopeniscsiusr/misc.h23
-rw-r--r--libopeniscsiusr/session.c339
-rw-r--r--libopeniscsiusr/sysfs.c242
-rw-r--r--libopeniscsiusr/sysfs.h51
-rw-r--r--libopeniscsiusr/tests/test_session.c107
12 files changed, 1281 insertions, 13 deletions
diff --git a/.gitignore b/.gitignore
index 7449cee..fc80fef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ tags
libopeniscsiusr/docs/man/
# ^ This folder is used for kernel-doc generated manpages.
libopeniscsiusr/tests/test_context
+libopeniscsiusr/tests/test_session
diff --git a/libopeniscsiusr/Makefile b/libopeniscsiusr/Makefile
index d137167..143a177 100644
--- a/libopeniscsiusr/Makefile
+++ b/libopeniscsiusr/Makefile
@@ -33,11 +33,12 @@ LIBS = $(DEVLIB).$(SONAME)
LIBS_MAJOR = $(DEVLIB).$(LIBISCSI_USR_VERSION_MAJOR)
PKGFILE = libopeniscsiusr.pc
HEADERS = libopeniscsiusr/libopeniscsiusr.h \
- libopeniscsiusr/libopeniscsiusr_common.h
-TESTS = tests/test_context
+ libopeniscsiusr/libopeniscsiusr_common.h \
+ libopeniscsiusr/libopeniscsiusr_session.h
+TESTS = tests/test_context tests/test_session
EXTRA_MAN_FILES = libopeniscsiusr.h.3
-OBJS = context.o misc.o
+OBJS = context.o misc.o session.o sysfs.o
CFLAGS ?= -O2 -g
CFLAGS += -Wall -Werror -Wextra -fvisibility=hidden -fPIC
@@ -50,16 +51,16 @@ $(LIBS): $(OBJS)
$(CC) $(LDFLAGS) -shared -Wl,-soname=$@ -o $@ $(OBJS) $(LIBADD)
ln -sf $@ $(DEVLIB)
-$(LIBS_MAJOR):
+$(LIBS_MAJOR): $(LIBS)
ln -sf $(LIBS) $@
clean:
$(RM) vgcore* core *.a *.o *.gz *.so *.so.* $(TESTS)
$(RM) -r docs/man
-$(TESTS): CFLAGS = -Wall -Werror -Wextra -I$(TOPDIR)/libopeniscsiusr -g
-$(TESTS): LDFLAGS += $(LIBADD) -L$(TOPDIR)/libopeniscsiusr -lopeniscsiusr
$(TESTS): $(LIBS)
+$(TESTS): CFLAGS += -I$(TOPDIR)/libopeniscsiusr -g
+$(TESTS): LDFLAGS += $(LIBADD) -L$(TOPDIR)/libopeniscsiusr -lopeniscsiusr
check: $(LIBS) $(TESTS)
sudo env LD_LIBRARY_PATH=$(LIBISCSI_USR_DIR) TESTS="$(TESTS)" \
diff --git a/libopeniscsiusr/docs/libopeniscsiusr.h.3 b/libopeniscsiusr/docs/libopeniscsiusr.h.3
index eb6b483..9aecbb6 100644
--- a/libopeniscsiusr/docs/libopeniscsiusr.h.3
+++ b/libopeniscsiusr/docs/libopeniscsiusr.h.3
@@ -60,7 +60,27 @@ below in case you want to take it as an example to create your own log handler.
.SH "SAMPLE CODE"
-TODO, will add the sample code once we have real iscsi functions.
+ struct iscsi_context *ctx = NULL;
+ struct iscsi_session **ses = NULL;
+ uint32_t se_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_sessions_get(ctx, &ses, &se_count) != LIBISCSI_OK) {
+ printf("FAILED\n");
+ rc = EXIT_FAILURE;
+ } else {
+ printf("\nGot %" PRIu32 " iSCSI sessions\n", se_count);
+ for (i = 0; i < se_count; ++i)
+ printf("SID is %" PRIu32 "\n",
+ iscsi_session_sid_get(ses[i]));
+ iscsi_sessions_free(ses, se_count);
+ }
+ iscsi_context_free(ctx);
+ exit(rc);
.SH "LICENSE"
GPLv3+
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
index 281e191..45d42c1 100644
--- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr.h
@@ -27,6 +27,7 @@ extern "C" {
#include <stdarg.h>
#include "libopeniscsiusr_common.h"
+#include "libopeniscsiusr_session.h"
/**
* iscsi_log_priority_str() - Convert log priority to string.
@@ -58,6 +59,16 @@ __DLL_EXPORT const char *iscsi_log_priority_str(int priority);
*
* * LIBISCSI_OK -- "OK"
*
+ * * LIBISCSI_ERR_BUG -- "BUG of libopeniscsiusr library"
+ *
+ * * LIBISCSI_ERR_SESS_NOT_FOUND - "Specified iSCSI session not found"
+ *
+ * * LIBISCSI_ERR_ACCESS - "Permission deny"
+ *
+ * * LIBISCSI_ERR_NOMEM - "Out of memory"
+ *
+ * * LIBISCSI_ERR_SYSFS_LOOKUP - "Could not lookup object in sysfs"
+ *
* * Other invalid error number -- "Invalid argument"
*
* @rc:
@@ -203,6 +214,116 @@ __DLL_EXPORT void iscsi_context_userdata_set(struct iscsi_context *ctx,
*/
__DLL_EXPORT void *iscsi_context_userdata_get(struct iscsi_context *ctx);
+/**
+ * iscsi_sessions_get() - Retrieve all iSCSI sessions.
+ *
+ * Retrieves all iSCSI sessions. For the properties of 'struct iscsi_session',
+ * please refer to the functions defined in 'libopeniscsiusr_session.h' file.
+ *
+ * @ctx:
+ * Pointer of 'struct iscsi_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @ses:
+ * Output pointer of 'struct iscsi_session' pointer array. Its memory
+ * should be freed by iscsi_sessions_free().
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @se_count:
+ * Output pointer of uint32_t. Will store the size of
+ * 'struct iscsi_session' 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_sessions_get(struct iscsi_context *ctx,
+ struct iscsi_session ***ses,
+ uint32_t *se_count);
+
+/**
+ * iscsi_sessions_free() - Free the memory of 'struct iscsi_session' pointer
+ * array
+ *
+ * Free the memory of 'iscsi_session' pointer array generated by
+ * 'iscsi_sessions_get()'.
+ * If provided 'ses' pointer is NULL or 'session_count' is 0, do nothing.
+ *
+ * @ses:
+ * Pointer of 'struct iscsi_session' pointer array.
+ * @session_count:
+ * uint32_t, the size of 'struct iscsi_session' pointer array.
+ *
+ * Return:
+ * void
+ */
+__DLL_EXPORT void iscsi_sessions_free(struct iscsi_session **ses,
+ uint32_t session_count);
+
+/**
+ * iscsi_session_get() - Retrieve specified iSCSI sessions.
+ *
+ * Retrieves specified iSCSI sessions. For the properties of
+ * 'struct iscsi_session', please refer to the functions defined in
+ * 'libopeniscsiusr_session.h' file.
+ *
+ * @ctx:
+ * Pointer of 'struct iscsi_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @sid:
+ * uint32_t, iSCSI session ID.
+ * @se:
+ * Output pointer of 'struct iscsi_session' pointer. Its memory
+ * should be freed by iscsi_session_free().
+ * If this pointer is NULL, your program will be terminated by assert.
+ * If specified iSCSI session does not exists, this pointer will be set to
+ * NULL with LIBISCSI_OK returned.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * LIBISCSI_OK
+ *
+ * * LIBISCSI_ERR_BUG
+ *
+ * * LIBISCSI_ERR_NOMEM
+ *
+ * * LIBISCSI_ERR_ACCESS
+ *
+ * * LIBISCSI_ERR_SYSFS_LOOKUP
+ *
+ * * LIBISCSI_ERR_SESS_NOT_FOUND
+ *
+ * Error number could be converted to string by iscsi_strerror().
+ */
+__DLL_EXPORT int iscsi_session_get(struct iscsi_context *ctx, uint32_t sid,
+ struct iscsi_session **se);
+
+/**
+ * iscsi_session_free() - Free the memory of 'struct iscsi_session'.
+ *
+ * Free the memory of 'iscsi_session' pointer generated by
+ * 'iscsi_sessions_get()'.
+ * If provided 'se' pointer is NULL, do nothing.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session' pointer.
+ *
+ * Return:
+ * void
+ */
+__DLL_EXPORT void iscsi_session_free(struct iscsi_session *se);
+
+
#ifdef __cplusplus
} /* End of extern "C" */
#endif
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
index 7952c59..b001904 100644
--- a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_common.h
@@ -21,7 +21,26 @@
#ifndef _LIB_OPEN_ISCSI_USR_COMMON_H_
#define _LIB_OPEN_ISCSI_USR_COMMON_H_
+#include <errno.h>
+
+/* Below error numbers should align with 'open-iscsi/include/iscsi_err.h' */
#define LIBISCSI_OK 0
+/* ^ No error */
+
+#define LIBISCSI_ERR_BUG 1
+/* ^ Bug of library */
+
+#define LIBISCSI_ERR_SESS_NOT_FOUND 2
+/* ^ session could not be found */
+
+#define LIBISCSI_ERR_NOMEM 3
+/* ^ Could not allocate resource for operation */
+
+#define LIBISCSI_ERR_ACCESS 13
+/* ^ Permission denied */
+
+#define LIBISCSI_ERR_SYSFS_LOOKUP 22
+/* ^ Could not lookup object in sysfs */
/*
* Use the syslog severity level as log priority
@@ -33,8 +52,6 @@
#define LIBISCSI_LOG_PRIORITY_DEFAULT LIBISCSI_LOG_PRIORITY_WARNING
-/* TODO(Gris Ge): Documentation */
-
#define __DLL_EXPORT __attribute__ ((visibility ("default")))
/* ^ Mark function or struct as external use.
* Check https://gcc.gnu.org/wiki/Visibility for detail
@@ -46,4 +63,6 @@
struct __DLL_EXPORT iscsi_context;
+struct __DLL_EXPORT iscsi_session;
+
#endif /* End of _LIB_OPEN_ISCSI_USR_COMMON_H_ */
diff --git a/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_session.h b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_session.h
new file mode 100644
index 0000000..4397258
--- /dev/null
+++ b/libopeniscsiusr/libopeniscsiusr/libopeniscsiusr_session.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#ifndef _LIB_OPEN_ISCSI_USR_SESSION_H_
+#define _LIB_OPEN_ISCSI_USR_SESSION_H_
+
+#include "libopeniscsiusr_common.h"
+
+#include <stdint.h>
+
+/**
+ * iscsi_session_sid_get() - Retrieve iSCSI session ID of specified session.
+ *
+ * Retrieve iSCSI session ID. The session ID here is the integer used
+ * in '/sys/class/iscsi_session/session<session_id>/'
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * uint32_t.
+ */
+__DLL_EXPORT uint32_t iscsi_session_sid_get(struct iscsi_session *se);
+
+/**
+ * iscsi_session_persistent_address_get() - Retrieve iSCSI target persistent
+ * address of specified session
+ *
+ * Retrieve the iSCSI target persistent address of specified iSCSI session.
+ * The 'persistent address' is the network address where iSCSI initiator send
+ * initial request. When iSCSI redirection in use, this address might not be
+ * the network address used for actual iSCSI transaction.
+ * Please use `iscsi_session_address_get()` for target network address of
+ * iSCSI transaction.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *. Empty string if not supported.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_session_free() or iscsi_sessions_free().
+ */
+__DLL_EXPORT const char *iscsi_session_persistent_address_get
+ (struct iscsi_session *se);
+
+/**
+ * iscsi_session_persistent_port_get() - Retrieve iSCSI target persistent
+ * port of specified session
+ *
+ * Retrieve the iSCSI target persistent port of specified iSCSI session.
+ * The 'persistent port' is the network port where iSCSI initiator send
+ * initial request. When iSCSI redirection in use, this port might not be
+ * the network port used for actual iSCSI transaction.
+ * Please use `iscsi_session_port_get()` for target network address of
+ * iSCSI transaction.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int32_t. -1 if not supported.
+ */
+__DLL_EXPORT int32_t iscsi_session_persistent_port_get
+ (struct iscsi_session *se);
+
+/**
+ * iscsi_session_target_name_get() - Retrieve iSCSI target name of specified
+ * session
+ *
+ * Retrieve the iSCSI target name of specified iSCSI session.
+ * The iSCSI Target Name specifies the worldwide unique name of the target.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_session_free() or iscsi_sessions_free().
+ */
+__DLL_EXPORT const char *iscsi_session_target_name_get
+ (struct iscsi_session *se);
+
+/**
+ * iscsi_session_username_get() - Retrieve authentication username of specified
+ * session.
+ *
+ * Retrieve the authentication username of specified iSCSI session.
+ * Currently open-iscsi only support CHAP authentication method.
+ * It's controlled this setting in iscsid.conf:
+ * 'node.session.auth.username'
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *. Empty string if not using CHAP authentication or failed
+ * to read authentication information.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_session_free() or iscsi_sessions_free().
+ */
+__DLL_EXPORT const char *iscsi_session_username_get(struct iscsi_session *se);
+
+/**
+ * iscsi_session_password_get() - Retrieve authentication password of specified
+ * session.
+ *
+ * Retrieve the authentication password of specified iSCSI session.
+ * Currently open-iscsi only support CHAP authentication method.
+ * It's controlled this setting in iscsid.conf:
+ * 'node.session.auth.password'
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *. Empty string if not using CHAP authentication or failed
+ * to read authentication information.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_session_free() or iscsi_sessions_free().
+ */
+__DLL_EXPORT const char *iscsi_session_password_get(struct iscsi_session *se);
+
+/**
+ * iscsi_session_username_in_get() - Retrieve authentication username of
+ * specified session.
+ *
+ * Retrieve the inbound authentication username of specified iSCSI session.
+ * Currently open-iscsi only support CHAP authentication method.
+ * The inbound authentication here means the iSCSI initiator authenticates the
+ * iSCSI target using CHAP.
+ * It's controlled this setting in iscsid.conf:
+ * 'node.session.auth.username_in'
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *. Empty string if not using inbound CHAP authentication or
+ * failed to read authentication information.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_session_free() or iscsi_sessions_free().
+ */
+__DLL_EXPORT const char *iscsi_session_username_in_get
+ (struct iscsi_session *se);
+
+/**
+ * iscsi_session_password_in_get() - Retrieve authentication password of
+ * specified session.
+ *
+ * Retrieve the inbound authentication password of specified iSCSI session.
+ * Currently open-iscsi only support CHAP authentication method.
+ * The inbound authentication here means the iSCSI initiator authenticates the
+ * iSCSI target using CHAP.
+ * It's controlled this setting in iscsid.conf:
+ * 'node.session.auth.password_in'
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *. Empty string if not using inbound CHAP authentication or
+ * failed to read authentication information.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_session_free() or iscsi_sessions_free().
+ */
+__DLL_EXPORT const char *iscsi_session_password_in_get
+ (struct iscsi_session *se);
+
+/**
+ * iscsi_session_recovery_tmo_get() - Retrieve recovery timeout value of
+ * specified session
+ *
+ * Retrieve the recovery timeout value of specified iSCSI session.
+ * The recovery timeout here means the seconds of time to wait for session
+ * re-establishment before failing SCSI commands back to the application when
+ * running the Linux SCSI Layer error handler.
+ * It could be controlled via this setting in iscsid.conf:
+ * 'node.session.timeo.replacement_timeout'.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int32_t. If the value is 0, IO will be failed immediately. If the value
+ * is less than 0, IO will remain queued until the session is logged back
+ * in, or until the user runs the logout command.
+ */
+__DLL_EXPORT int32_t iscsi_session_recovery_tmo_get(struct iscsi_session *se);
+
+/**
+ * iscsi_session_lu_reset_tmo_get() - Retrieve logical unit timeout value of
+ * specified session
+ *
+ * Retrieve the logical unit timeout value of specified iSCSI session.
+ * The logical unit timeout here means the seconds of time to wait for a logical
+ * unit response before before failing the operation and trying session
+ * re-establishment.
+ * It could be controlled via this setting in iscsid.conf:
+ * 'node.session.err_timeo.lu_reset_timeout'
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int32_t. -1 if not supported.
+ */
+__DLL_EXPORT int32_t iscsi_session_lu_reset_tmo_get(struct iscsi_session *se);
+
+/**
+ * iscsi_session_tgt_reset_tmo_get() - Retrieve target response timeout value of
+ * of specified session
+ *
+ * Retrieve the target response timeout value of specified iSCSI session.
+ * The target response timeout here means the seconds of time to wait for a
+ * target response before before failing the operation and trying session
+ * re-establishment.
+ * It could be controlled via this setting in iscsid.conf:
+ * 'node.session.err_timeo.tgt_reset_timeout'.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int32_t. -1 if not supported.
+ */
+__DLL_EXPORT int32_t iscsi_session_tgt_reset_tmo_get(struct iscsi_session *se);
+
+/**
+ * iscsi_session_abort_tmo_get() - Retrieve abort response timeout value of
+ * specified session
+ *
+ * Retrieve the abort response timeout value of specified iSCSI session.
+ * The abort response timeout here means the seconds of time to wait for a
+ * abort response before before failing the operation and trying session
+ * re-establishment.
+ * It could be controlled via this setting in iscsid.conf:
+ * 'node.session.err_timeo.abort_timeout'.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int32_t. -1 if not supported.
+ */
+__DLL_EXPORT int32_t iscsi_session_abort_tmo_get(struct iscsi_session *se);
+
+/**
+ * iscsi_session_tpgt_get() - Retrieve target portal group tag of specified
+ * session
+ *
+ * Retrieve the target portal group tag of specified iSCSI session.
+ *
+ * The target portal group tag is a value that uniquely identifies a portal
+ * group within an iSCSI target node. This key carries the value of the tag of
+ * the portal group that is servicing the Login request. The iSCSI target
+ * returns this key to the initiator in the Login Response PDU to the first
+ * Login Request PDU that has the C bit set to 0 when TargetName is given by the
+ * initiator.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int32_t. -1 if not supported.
+ */
+__DLL_EXPORT int32_t iscsi_session_tpgt_get(struct iscsi_session *se);
+
+/**
+ * iscsi_session_address_get() - Retrieve iSCSI target address of specified
+ * session
+ *
+ * Retrieve the iSCSI target network address of specified iSCSI session.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * const char *. Empty string if not supported.
+ * No need to free this memory, the resources will get freed by
+ * iscsi_session_free() or iscsi_sessions_free().
+ */
+__DLL_EXPORT const char *iscsi_session_address_get
+ (struct iscsi_session *se);
+
+/**
+ * iscsi_session_port_get() - Retrieve iSCSI target port of specified session
+ *
+ * Retrieve the iSCSI target port of specified iSCSI session.
+ *
+ * @se:
+ * Pointer of 'struct iscsi_session'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int32_t. -1 if not supported.
+ */
+__DLL_EXPORT int32_t iscsi_session_port_get(struct iscsi_session *se);
+
+#endif /* End of _LIB_OPEN_ISCSI_USR_SESSION_H_ */
diff --git a/libopeniscsiusr/misc.c b/libopeniscsiusr/misc.c
index 94d1f23..93db919 100644
--- a/libopeniscsiusr/misc.c
+++ b/libopeniscsiusr/misc.c
@@ -23,6 +23,7 @@
#include <errno.h>
#include <dirent.h>
#include <string.h>
+#include <unistd.h>
#include "libopeniscsiusr/libopeniscsiusr.h"
#include "misc.h"
@@ -46,7 +47,7 @@ const char *func_name(var_type var) { \
size_t i = 0; \
uint32_t tmp_var = var & UINT32_MAX; \
errno = 0; \
- /* In the whole libiscsi, we don't have negative value */ \
+ /* In the whole libopeniscsiusr, we don't have negative value */ \
for (; i < sizeof(conv_array)/sizeof(conv_array[0]); ++i) { \
if ((conv_array[i].value) == tmp_var) \
return conv_array[i].str; \
@@ -57,6 +58,11 @@ const char *func_name(var_type var) { \
static const struct _num_str_conv _ISCSI_RC_MSG_CONV[] = {
{LIBISCSI_OK, "OK"},
+ {LIBISCSI_ERR_BUG, "BUG of libopeniscsiusr library"},
+ {LIBISCSI_ERR_SESS_NOT_FOUND, "Specified iSCSI session not found"},
+ {LIBISCSI_ERR_ACCESS, "Permission deny"},
+ {LIBISCSI_ERR_NOMEM, "Out of memory"},
+ {LIBISCSI_ERR_SYSFS_LOOKUP, "Could not lookup object in sysfs"},
};
_iscsi_str_func_gen(iscsi_strerror, int, rc, _ISCSI_RC_MSG_CONV);
@@ -103,3 +109,16 @@ void _iscsi_log(struct iscsi_context *ctx, int priority, const char *file,
ctx->log_func(ctx, priority, file, line, func_name, format, args);
va_end(args);
}
+
+int _scan_filter_skip_dot(const struct dirent *dir)
+{
+ return strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..");
+}
+
+bool _file_exists(const char *path)
+{
+ if (access(path, F_OK) == 0)
+ return true;
+ else
+ return false;
+}
diff --git a/libopeniscsiusr/misc.h b/libopeniscsiusr/misc.h
index 441b885..1c5d696 100644
--- a/libopeniscsiusr/misc.h
+++ b/libopeniscsiusr/misc.h
@@ -20,12 +20,12 @@
#define __ISCSI_USR_MISC_H__
#include <stdint.h>
-#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <dirent.h>
-#include "libopeniscsiusr/libopeniscsiusr_common.h"
+#include "libopeniscsiusr/libopeniscsiusr.h"
#define _good(rc, rc_val, out) \
do { \
@@ -66,4 +66,21 @@ __DLL_LOCAL void _iscsi_log_stderr(struct iscsi_context *ctx, int priority,
return d->prop_name; \
}
+/*
+ * Check pointer returned by malloc() or strdup() or calloc(), if NULL, set
+ * rc as LIBISCSI_ERR_NO_MEMORY, report error and goto goto_out.
+ */
+#define _alloc_null_check(ctx, ptr, rc, goto_out) \
+ do { \
+ if (ptr == NULL) { \
+ rc = LIBISCSI_ERR_NOMEM; \
+ _error(ctx, iscsi_strerror(rc)); \
+ goto goto_out; \
+ } \
+ } while(0)
+
+__DLL_LOCAL int _scan_filter_skip_dot(const struct dirent *dir);
+
+__DLL_LOCAL bool _file_exists(const char *path);
+
#endif /* End of __ISCSI_USR_MISC_H__ */
diff --git a/libopeniscsiusr/session.c b/libopeniscsiusr/session.c
new file mode 100644
index 0000000..b7b39b2
--- /dev/null
+++ b/libopeniscsiusr/session.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* For NI_MAXHOST */
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <string.h>
+#include <errno.h>
+
+#include "libopeniscsiusr/libopeniscsiusr.h"
+#include "misc.h"
+#include "sysfs.h"
+
+#define _ISCSI_NAME_MAX_LEN 223
+/* ^ RFC 3720:
+ * Each iSCSI node, whether an initiator or target, MUST have an iSCSI
+ * name.
+ *
+ * Initiators and targets MUST support the receipt of iSCSI names of up
+ * to the maximum length of 223 bytes.
+ */
+
+#define _ISCSI_CHAP_AUTH_STR_MAX_LEN 256
+/* ^ No official document found for this value, just copy from usr/auth.h
+ */
+
+struct iscsi_session {
+ uint32_t sid;
+ /* ^ It's actually a int according to Linux kernel code but
+ * the dev_set_name() in iscsi_add_session() of scsi_transport_iscsi.c
+ * are using %u to output this.
+ */
+ char persistent_address[NI_MAXHOST + 1];
+ int32_t persistent_port;
+ char target_name[_ISCSI_NAME_MAX_LEN + 1];
+ char username[_ISCSI_CHAP_AUTH_STR_MAX_LEN];
+ char password[_ISCSI_CHAP_AUTH_STR_MAX_LEN];
+ char username_in[_ISCSI_CHAP_AUTH_STR_MAX_LEN];
+ char password_in[_ISCSI_CHAP_AUTH_STR_MAX_LEN];
+ int32_t recovery_tmo;
+ /* ^ It's actually a int according to Linux kernel code.
+ */
+ int32_t lu_reset_tmo;
+ /* ^ It's actually a int according to Linux kernel code.
+ */
+ int32_t tgt_reset_tmo;
+ /* ^ It's actually a int according to Linux kernel code.
+ */
+ int32_t abort_tmo;
+ /* ^ It's actually a int according to Linux kernel code.
+ */
+ int32_t tpgt;
+ /* ^ It's actually a int according to Linux kernel code.
+ */
+ char address[NI_MAXHOST + 1];
+
+ int32_t port;
+};
+
+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);
+_iscsi_getter_func_gen(iscsi_session, target_name, const char *);
+_iscsi_getter_func_gen(iscsi_session, username, const char *);
+_iscsi_getter_func_gen(iscsi_session, password, const char *);
+_iscsi_getter_func_gen(iscsi_session, username_in, const char *);
+_iscsi_getter_func_gen(iscsi_session, password_in, const char *);
+_iscsi_getter_func_gen(iscsi_session, recovery_tmo, int32_t);
+_iscsi_getter_func_gen(iscsi_session, lu_reset_tmo, int32_t);
+_iscsi_getter_func_gen(iscsi_session, tgt_reset_tmo, int32_t);
+_iscsi_getter_func_gen(iscsi_session, abort_tmo, int32_t);
+_iscsi_getter_func_gen(iscsi_session, tpgt, int32_t);
+_iscsi_getter_func_gen(iscsi_session, address, const char *);
+_iscsi_getter_func_gen(iscsi_session, port, int32_t);
+
+/*
+ * 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)
+{
+ int rc = LIBISCSI_OK;
+ char sysfs_se_dir_path[PATH_MAX];
+ char sysfs_con_dir_path[PATH_MAX];
+
+ assert(ctx != NULL);
+ assert(se != NULL);
+
+ _debug(ctx, "Querying iSCSI session for sid %" PRIu32, sid);
+
+ snprintf(sysfs_se_dir_path, PATH_MAX, "%s/session%" PRIu32,
+ _ISCSI_SYS_SESSION_DIR, sid);
+ snprintf(sysfs_con_dir_path, PATH_MAX, "%s/connection%" PRIu32 ":0",
+ _ISCSI_SYS_CONNECTION_DIR, sid);
+ /* ^ BUG(Gris Ge): ':0' here in kernel is referred as connection id.
+ * but the open-iscsi assuming it's always 0, need
+ * investigation.
+ */
+
+ *se = (struct iscsi_session *)
+ calloc(sizeof(struct iscsi_session), 1);
+ _alloc_null_check(ctx, *se , rc, out);
+
+ if (! _file_exists(sysfs_se_dir_path)) {
+ _info(ctx, "Sysfs path '%s' does not exists",
+ sysfs_se_dir_path);
+ rc = LIBISCSI_ERR_SESS_NOT_FOUND;
+ }
+ if (! _file_exists(sysfs_con_dir_path)) {
+ _info(ctx, "Sysfs path '%s' does not exists",
+ sysfs_se_dir_path);
+ rc = LIBISCSI_ERR_SESS_NOT_FOUND;
+ }
+ if (rc == LIBISCSI_ERR_SESS_NOT_FOUND) {
+ _error(ctx, "Specified SID %" PRIu32, "does not exists",
+ sid);
+ goto out;
+ }
+
+ (*se)->sid = sid;
+ _good(_sysfs_prop_get_str(ctx, sysfs_se_dir_path, "targetname",
+ (*se)->target_name,
+ sizeof((*se)->target_name) / sizeof(char),
+ NULL), rc, out);
+
+ _good(_sysfs_prop_get_str(ctx, sysfs_se_dir_path, "username",
+ (*se)->username,
+ sizeof((*se)->username) / sizeof(char),
+ ""),
+ rc, out);
+
+ _good(_sysfs_prop_get_str(ctx, sysfs_se_dir_path, "password",
+ (*se)->password,
+ sizeof((*se)->password) / sizeof(char),
+ ""),
+ rc, out);
+
+ _good(_sysfs_prop_get_str(ctx, sysfs_se_dir_path, "username_in",
+ (*se)->username_in,
+ sizeof((*se)->username_in) / sizeof(char),
+ ""),
+ rc, out);
+
+ _good(_sysfs_prop_get_str(ctx, sysfs_se_dir_path, "password_in",
+ (*se)->password_in,
+ sizeof((*se)->password_in) / sizeof(char),
+ ""),
+ rc, out);
+
+ _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "recovery_tmo",
+ &((*se)->recovery_tmo),
+ -1),
+ rc, out);
+
+ _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "lu_reset_tmo",
+ &((*se)->lu_reset_tmo), -1),
+ rc, out);
+
+ _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path,
+ "tgt_reset_tmo", &((*se)->tgt_reset_tmo), -1),
+ rc, out);
+
+ _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "abort_tmo",
+ &((*se)->abort_tmo), -1),
+ rc, out);
+
+ _good(_sysfs_prop_get_i32(ctx, sysfs_se_dir_path, "tpgt",
+ &((*se)->tpgt),
+ INT32_MAX /* raise error if not found */),
+ rc, out);
+
+ _good(_sysfs_prop_get_str(ctx, sysfs_con_dir_path, "persistent_address",
+ (*se)->persistent_address,
+ sizeof((*se)->persistent_address) /
+ sizeof(char), ""),
+ rc, out);
+
+ _good(_sysfs_prop_get_i32(ctx, sysfs_con_dir_path, "persistent_port",
+ &((*se)->persistent_port), -1),
+ rc, out);
+
+ _good(_sysfs_prop_get_str(ctx, sysfs_con_dir_path, "address",
+ (*se)->address,
+ sizeof((*se)->address) / sizeof(char),
+ ""),
+ rc, out);
+
+ _good(_sysfs_prop_get_i32(ctx, sysfs_con_dir_path, "port",
+ &((*se)->port), -1), rc, out);
+
+ if ((strcmp((*se)->address, "") == 0) &&
+ (strcmp((*se)->persistent_address, "") != 0))
+ 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));
+
+ if (((*se)->persistent_port != -1) &&
+ ((*se)->port == -1))
+ (*se)->persistent_port = (*se)->port;
+
+ if (((*se)->persistent_port != -1) &&
+ ((*se)->port == -1))
+ (*se)->port = (*se)->persistent_port;
+
+out:
+ if (rc != LIBISCSI_OK) {
+ iscsi_session_free(*se);
+ *se = NULL;
+ }
+ return rc;
+}
+
+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;
+
+ assert(ctx != NULL);
+ assert(sessions != NULL);
+ assert(session_count != NULL);
+
+ *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);
+
+ *session_count = n & UINT32_MAX;
+
+ 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);
+ }
+
+out:
+ for (j = n - 1; j >= 0; --j)
+ free(namelist[j]);
+ free(namelist);
+ if (rc != LIBISCSI_OK) {
+ iscsi_sessions_free(*sessions, *session_count);
+ *sessions = NULL;
+ *session_count = 0;
+ }
+ return rc;
+}
+
+void iscsi_session_free(struct iscsi_session *se)
+{
+ free(se);
+}
+
+void iscsi_sessions_free(struct iscsi_session **ses, uint32_t se_count)
+{
+ uint32_t i = 0;
+
+ if (ses == NULL)
+ return;
+
+ for (i = 0; i < se_count; ++i)
+ iscsi_session_free(ses[i]);
+ free (ses);
+}
diff --git a/libopeniscsiusr/sysfs.c b/libopeniscsiusr/sysfs.c
new file mode 100644
index 0000000..c1aa576
--- /dev/null
+++ b/libopeniscsiusr/sysfs.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "libopeniscsiusr/libopeniscsiusr_common.h"
+#include "sysfs.h"
+#include "misc.h"
+
+#define _INT32_STR_MAX_LEN 12
+/* ^ The max uint32_t is 4294967296 which requires 11 bytes for string.
+ * The max/min in32_t is 2147483647 or -2147483646 which requires 12 bytes.
+ */
+
+#define _SYS_NULL_STR "(null)"
+
+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);
+/*
+ * dev_path should be char[PATH_MAX].
+ */
+static int sysfs_read_file(const char *path, uint8_t *buff, size_t buff_size)
+{
+ int fd = -1;
+ int errno_save = 0;
+ ssize_t readed = 0;
+ ssize_t i = 0;
+
+ assert(path != NULL);
+ assert(buff != NULL);
+ assert(buff_size != 0);
+
+ memset(buff, 0, buff_size);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return errno;
+ readed = read(fd, buff, buff_size);
+ errno_save = errno;
+ close(fd);
+
+ if (readed < 0) {
+ buff[0] = '\0';
+ return errno_save;
+ }
+
+ buff[buff_size - 1] = '\0';
+ /* Remove the trailing \n */
+ for (i = readed - 1; i >= 0; --i) {
+ if (buff[i] == '\n') {
+ buff[i] = '\0';
+ break;
+ }
+ }
+
+ if (strcmp((char *) buff, _SYS_NULL_STR) == 0)
+ buff[0] = '\0';
+
+ return 0;
+}
+
+int _sysfs_prop_get_str(struct iscsi_context *ctx, const char *dir_path,
+ const char *prop_name, char *buff, size_t buff_size,
+ const char *default_value)
+{
+ char file_path[PATH_MAX];
+ int rc = LIBISCSI_OK;
+ int errno_save = 0;
+
+ assert(dir_path != NULL);
+ assert(prop_name != NULL);
+ assert(buff != NULL);
+
+ snprintf(file_path, PATH_MAX, "%s/%s", dir_path, prop_name);
+
+ errno_save = sysfs_read_file(file_path, (uint8_t *) buff, buff_size);
+ if (errno_save != 0) {
+ if (errno_save == ENOENT) {
+ if (default_value == NULL) {
+ rc = LIBISCSI_ERR_SYSFS_LOOKUP;
+ _error(ctx, "Failed to read '%s': "
+ "file '%s' does not exists", prop_name,
+ file_path);
+ } else {
+ _info(ctx, "Failed to read '%s': "
+ "file '%s' does not exists, "
+ "using default value %s", prop_name,
+ file_path, default_value);
+ memcpy(buff, (void *) default_value,
+ strlen(default_value) + 1);
+ }
+ } else if (errno_save == EACCES) {
+ rc = LIBISCSI_ERR_ACCESS;
+ _error(ctx, "Failed to read '%s': "
+ "permission deny when reading '%s'", prop_name,
+ file_path);
+ } else {
+ rc = LIBISCSI_ERR_BUG;
+ _error(ctx, "Failed to read '%s': "
+ "error when reading '%s': %d", prop_name,
+ file_path, errno_save);
+ }
+ } else {
+ _debug(ctx, "Open '%s', got '%s'", file_path, buff);
+ }
+ return rc;
+}
+
+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)
+{
+ char file_path[PATH_MAX];
+ int rc = LIBISCSI_OK;
+ int errno_save = 0;
+ uint8_t buff[_INT32_STR_MAX_LEN];
+ long long int tmp_val = 0;
+
+ assert(dir_path != NULL);
+ assert(prop_name != NULL);
+ assert(val != NULL);
+
+ *val = 0;
+
+ snprintf(file_path, PATH_MAX, "%s/%s", dir_path, prop_name);
+
+ 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) {
+ rc = LIBISCSI_ERR_SYSFS_LOOKUP;
+ _error(ctx, "Failed to read '%s': "
+ "file '%s' does not exists",
+ prop_name, file_path);
+ return rc;
+ } else {
+ _info(ctx,
+ "Failed to read '%s': "
+ "File '%s' does not exists, using ",
+ "default value %lld",
+ file_path, default_value);
+ *val = default_value;
+ return rc;
+ }
+ } else if (errno_save == EACCES) {
+ rc = LIBISCSI_ERR_ACCESS;
+ _error(ctx, "Permission deny when reading '%s'",
+ file_path);
+ return rc;
+ } else {
+ rc = LIBISCSI_ERR_BUG;
+ _error(ctx, "Error when reading '%s': %d", file_path,
+ errno_save);
+ return rc;
+ }
+ }
+
+ tmp_val = strtoll((const char *) buff, NULL, 10 /* base */);
+ errno_save = errno;
+ if ((errno_save != 0) && (tmp_val == LONG_MAX)) {
+ rc = LIBISCSI_ERR_BUG;
+ _error(ctx, "Sysfs: %s: Error when converting '%s' "
+ "to number", file_path, (char *) buff, errno_save);
+ return rc;
+ }
+
+ *val = tmp_val;
+
+ _debug(ctx, "Open '%s', got %lld", file_path, tmp_val);
+
+ 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;
+}
diff --git a/libopeniscsiusr/sysfs.h b/libopeniscsiusr/sysfs.h
new file mode 100644
index 0000000..835c0fe
--- /dev/null
+++ b/libopeniscsiusr/sysfs.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+#ifndef __ISCSI_USR_SYSFS_H__
+#define __ISCSI_USR_SYSFS_H__
+
+#include <stdint.h>
+
+#include "libopeniscsiusr/libopeniscsiusr_common.h"
+
+#define _ISCSI_SYS_SESSION_DIR "/sys/class/iscsi_session"
+#define _ISCSI_SYS_CONNECTION_DIR "/sys/class/iscsi_connection"
+
+/*
+ * When default_value == NULL, treat no such file as LIB_BUG.
+ */
+__DLL_LOCAL int _sysfs_prop_get_str(struct iscsi_context *ctx,
+ const char *dir_path, const char *prop_name,
+ char *buff, size_t buff_size,
+ const char *default_value);
+
+/*
+ * 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);
+
+/*
+ * 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);
+
+#endif /* End of __ISCSI_USR_SYSFS_H__ */
diff --git a/libopeniscsiusr/tests/test_session.c b/libopeniscsiusr/tests/test_session.c
new file mode 100644
index 0000000..f7abc03
--- /dev/null
+++ b/libopeniscsiusr/tests/test_session.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Gris Ge <fge@redhat.com>
+ */
+
+#include <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)
+
+#define _assert_print_prop_u32_not_zero(struct_name, obj, prop_name) \
+ do { \
+ assert(struct_name##_##prop_name##_get(obj) != 0); \
+ printf("\t" # prop_name ": %" PRIu32 "\n", \
+ struct_name##_##prop_name##_get(obj)); \
+ } while(0)
+
+#define _assert_print_prop_i32_not_zero(struct_name, obj, prop_name) \
+ do { \
+ assert(struct_name##_##prop_name##_get(obj) != 0); \
+ printf("\t" # prop_name ": %" PRIi32 "\n", \
+ struct_name##_##prop_name##_get(obj)); \
+ } while(0)
+
+static void test_session(struct iscsi_session *se);
+
+static void test_session(struct iscsi_session *se)
+{
+ assert(se != NULL);
+ printf("Session %" PRIu32 ":\n", iscsi_session_sid_get(se));
+
+ _assert_print_prop_u32_not_zero(iscsi_session, se, sid);
+ _assert_print_prop_str_not_empty(iscsi_session, se, persistent_address);
+ _assert_print_prop_i32_not_zero(iscsi_session, se, persistent_port);
+ _assert_print_prop_str_not_empty(iscsi_session, se, target_name);
+ _assert_print_prop_str_can_empty(iscsi_session, se, username);
+ _assert_print_prop_str_can_empty(iscsi_session, se, password);
+ _assert_print_prop_str_can_empty(iscsi_session, se, username_in);
+ _assert_print_prop_str_can_empty(iscsi_session, se, password_in);
+ _assert_print_prop_u32_not_zero(iscsi_session, se, recovery_tmo);
+ _assert_print_prop_u32_not_zero(iscsi_session, se, lu_reset_tmo);
+ _assert_print_prop_u32_not_zero(iscsi_session, se, tgt_reset_tmo);
+ _assert_print_prop_u32_not_zero(iscsi_session, se, abort_tmo);
+ _assert_print_prop_u32_not_zero(iscsi_session, se, tpgt);
+ _assert_print_prop_str_not_empty(iscsi_session, se, address);
+ _assert_print_prop_i32_not_zero(iscsi_session, se, port);
+}
+
+int main()
+{
+ struct iscsi_context *ctx = NULL;
+ struct iscsi_session **ses = NULL;
+ uint32_t se_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_sessions_get(ctx, &ses, &se_count) != LIBISCSI_OK) {
+ printf("FAILED\n");
+ rc = EXIT_FAILURE;
+ } else {
+ printf("\nGot %" PRIu32 " iSCSI sessions\n", se_count);
+ for (i = 0; i < se_count; ++i) {
+ test_session(ses[i]);
+ }
+ iscsi_sessions_free(ses, se_count);
+ }
+ iscsi_context_free(ctx);
+ exit(rc);
+}