You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
8.5 KiB
354 lines
8.5 KiB
From 46ba8eed79fa13b32947b5c5b1bf0bc133b14c41 Mon Sep 17 00:00:00 2001 |
|
From: Chris Leech <cleech@redhat.com> |
|
Date: Tue, 3 Feb 2015 16:28:15 -0800 |
|
Subject: [PATCH] iscsid safe session logout |
|
|
|
Implement a safe logout option, which uses libmount from util-linux to |
|
check for active mounts (and swaps) over devices, their partitions, and |
|
any holders (like LVM and multipath device maps). When enabled iscsid |
|
will refuse to logout of sessions actively being used for mounts, |
|
returning a status of EBUSY to the ipc request. |
|
|
|
I've made it a configuration option (iscsid.safe_logout) that defaults |
|
to "No" to preserve the existing behavior as the default, while making |
|
it available for users that prefer a safety check. |
|
|
|
This does add a new dependency on libmount. |
|
|
|
Signed-off-by: Chris Leech <cleech@redhat.com> |
|
--- |
|
etc/iscsid.conf | 3 + |
|
usr/Makefile | 4 +- |
|
usr/initiator.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|
usr/sysfs.c | 40 ++++++++++++ |
|
usr/sysfs.h | 4 ++ |
|
5 files changed, 248 insertions(+), 2 deletions(-) |
|
|
|
diff --git a/etc/iscsid.conf b/etc/iscsid.conf |
|
index ef76dc0..6d9a5c0 100644 |
|
--- a/etc/iscsid.conf |
|
+++ b/etc/iscsid.conf |
|
@@ -22,6 +22,9 @@ |
|
# Default for upstream open-iscsi scripts (uncomment to activate). |
|
iscsid.startup = /sbin/iscsid |
|
|
|
+# Check for active mounts on devices reachable through a session |
|
+# and refuse to logout if there are any. Defaults to "No". |
|
+# iscsid.safe_logout = Yes |
|
|
|
############################# |
|
# NIC/HBA and driver settings |
|
diff --git a/usr/Makefile b/usr/Makefile |
|
index 3d8ee22..b2c1504 100644 |
|
--- a/usr/Makefile |
|
+++ b/usr/Makefile |
|
@@ -55,14 +55,14 @@ all: $(PROGRAMS) |
|
|
|
iscsid: $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(DISCOVERY_SRCS) \ |
|
iscsid.o session_mgmt.o discoveryd.o |
|
- $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns |
|
+ $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lmount |
|
|
|
iscsiadm: $(ISCSI_LIB_SRCS) $(DISCOVERY_SRCS) iscsiadm.o session_mgmt.o |
|
$(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns |
|
|
|
iscsistart: $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(FW_BOOT_SRCS) \ |
|
iscsistart.o statics.o |
|
- $(CC) $(CFLAGS) -static $^ -o $@ |
|
+ $(CC) $(CFLAGS) -static $^ -o $@ -lmount |
|
clean: |
|
rm -f *.o $(PROGRAMS) .depend $(LIBSYS) |
|
|
|
diff --git a/usr/initiator.c b/usr/initiator.c |
|
index 9d02f47..ac1a3ac 100644 |
|
--- a/usr/initiator.c |
|
+++ b/usr/initiator.c |
|
@@ -30,6 +30,7 @@ |
|
#include <errno.h> |
|
#include <dirent.h> |
|
#include <fcntl.h> |
|
+#include <libmount/libmount.h> |
|
|
|
#include "initiator.h" |
|
#include "transport.h" |
|
@@ -2140,11 +2141,200 @@ static int session_unbind(struct iscsi_session *session) |
|
return err; |
|
} |
|
|
|
+static struct libmnt_table *mtab, *swaps; |
|
+ |
|
+static void libmount_cleanup(void) |
|
+{ |
|
+ mnt_free_table(mtab); |
|
+ mnt_free_table(swaps); |
|
+ mtab = swaps = NULL; |
|
+} |
|
+ |
|
+static int libmount_init(void) |
|
+{ |
|
+ mnt_init_debug(0); |
|
+ mtab = mnt_new_table(); |
|
+ swaps = mnt_new_table(); |
|
+ if (!mtab || !swaps) { |
|
+ libmount_cleanup(); |
|
+ return -ENOMEM; |
|
+ } |
|
+ mnt_table_parse_mtab(mtab, NULL); |
|
+ mnt_table_parse_swaps(swaps, NULL); |
|
+ return 0; |
|
+} |
|
+ |
|
+static int trans_filter(const struct dirent *d) |
|
+{ |
|
+ if (!strcmp(".", d->d_name) || !strcmp("..", d->d_name)) |
|
+ return 0; |
|
+ return 1; |
|
+} |
|
+ |
|
+static int subdir_filter(const struct dirent *d) |
|
+{ |
|
+ if (!(d->d_type & DT_DIR)) |
|
+ return 0; |
|
+ return trans_filter(d); |
|
+} |
|
+ |
|
+static int is_partition(const char *path) |
|
+{ |
|
+ char *devtype; |
|
+ int rc = 0; |
|
+ |
|
+ devtype = sysfs_get_uevent_devtype(path); |
|
+ if (!devtype) |
|
+ return 0; |
|
+ if (strcmp(devtype, "partition") == 0) |
|
+ rc = 1; |
|
+ free(devtype); |
|
+ return rc; |
|
+} |
|
+ |
|
+static int blockdev_check_mnts(char *syspath) |
|
+{ |
|
+ struct libmnt_fs *fs; |
|
+ char *devname = NULL; |
|
+ char *_devname = NULL; |
|
+ int rc = 0; |
|
+ |
|
+ devname = sysfs_get_uevent_devname(syspath); |
|
+ if (!devname) |
|
+ goto out; |
|
+ |
|
+ _devname = calloc(1, PATH_MAX); |
|
+ if (!_devname) |
|
+ goto out; |
|
+ snprintf(_devname, PATH_MAX, "/dev/%s", devname); |
|
+ |
|
+ fs = mnt_table_find_source(mtab, _devname, MNT_ITER_FORWARD); |
|
+ if (fs) { |
|
+ rc = 1; |
|
+ goto out; |
|
+ } |
|
+ fs = mnt_table_find_source(swaps, _devname, MNT_ITER_FORWARD); |
|
+ if (fs) |
|
+ rc = 1; |
|
+out: |
|
+ free(devname); |
|
+ free(_devname); |
|
+ return rc; |
|
+} |
|
+ |
|
+static int count_device_users(char *syspath); |
|
+ |
|
+static int blockdev_get_partitions(char *syspath) |
|
+{ |
|
+ struct dirent **parts = NULL; |
|
+ int n, i; |
|
+ int count = 0; |
|
+ |
|
+ n = scandir(syspath, &parts, subdir_filter, alphasort); |
|
+ for (i = 0; i < n; i++) { |
|
+ char *newpath; |
|
+ |
|
+ newpath = calloc(1, PATH_MAX); |
|
+ if (!newpath) |
|
+ continue; |
|
+ snprintf(newpath, PATH_MAX, "%s/%s", syspath, parts[i]->d_name); |
|
+ free(parts[i]); |
|
+ if (is_partition(newpath)) { |
|
+ count += count_device_users(newpath); |
|
+ } |
|
+ free(newpath); |
|
+ } |
|
+ free(parts); |
|
+ return count; |
|
+} |
|
+ |
|
+static int blockdev_get_holders(char *syspath) |
|
+{ |
|
+ char *path = NULL; |
|
+ struct dirent **holds = NULL; |
|
+ int n, i; |
|
+ int count = 0; |
|
+ |
|
+ path = calloc(1, PATH_MAX); |
|
+ if (!path) |
|
+ return 0; |
|
+ snprintf(path, PATH_MAX, "%s/holders", syspath); |
|
+ |
|
+ n = scandir(path, &holds, trans_filter, alphasort); |
|
+ for (i = 0; i < n; i++) { |
|
+ char *newpath; |
|
+ char *rp; |
|
+ |
|
+ newpath = calloc(1, PATH_MAX); |
|
+ if (!newpath) |
|
+ continue; |
|
+ snprintf(newpath, PATH_MAX, "%s/%s", path, holds[i]->d_name); |
|
+ |
|
+ free(holds[i]); |
|
+ rp = realpath(newpath, NULL); |
|
+ if (rp) |
|
+ count += count_device_users(rp); |
|
+ free(newpath); |
|
+ free(rp); |
|
+ } |
|
+ free(path); |
|
+ free(holds); |
|
+ return count; |
|
+} |
|
+ |
|
+static int count_device_users(char *syspath) |
|
+{ |
|
+ int count = 0; |
|
+ count += blockdev_check_mnts(syspath); |
|
+ count += blockdev_get_partitions(syspath); |
|
+ count += blockdev_get_holders(syspath); |
|
+ return count; |
|
+}; |
|
+ |
|
+static void device_in_use(void *data, int host_no, int target, int lun) |
|
+{ |
|
+ char *syspath = NULL; |
|
+ char *devname = NULL; |
|
+ int *count = data; |
|
+ |
|
+ devname = iscsi_sysfs_get_blockdev_from_lun(host_no, target, lun); |
|
+ if (!devname) |
|
+ goto out; |
|
+ syspath = calloc(1, PATH_MAX); |
|
+ if (!syspath) |
|
+ goto out; |
|
+ snprintf(syspath, PATH_MAX, "/sys/class/block/%s", devname); |
|
+ *count += count_device_users(syspath); |
|
+out: |
|
+ free(syspath); |
|
+ free(devname); |
|
+} |
|
+ |
|
+static int session_in_use(int sid) |
|
+{ |
|
+ int host_no = -1, err = 0; |
|
+ int count = 0; |
|
+ |
|
+ if (libmount_init()) { |
|
+ log_error("Failed to initialize libmount, " |
|
+ "not checking for active mounts on session [%d].\n", sid); |
|
+ return 0; |
|
+ } |
|
+ |
|
+ host_no = iscsi_sysfs_get_host_no_from_sid(sid, &err); |
|
+ if (!err) |
|
+ iscsi_sysfs_for_each_device(&count, host_no, sid, device_in_use); |
|
+ |
|
+ libmount_cleanup(); |
|
+ return count; |
|
+} |
|
+ |
|
int session_logout_task(int sid, queue_task_t *qtask) |
|
{ |
|
iscsi_session_t *session; |
|
iscsi_conn_t *conn; |
|
int rc = ISCSI_SUCCESS; |
|
+ char *safe; |
|
|
|
session = session_find_by_sid(sid); |
|
if (!session) { |
|
@@ -2167,6 +2357,15 @@ invalid_state: |
|
return ISCSI_ERR_INTERNAL; |
|
} |
|
|
|
+ safe = cfg_get_string_param(dconfig->config_file, "iscsid.safe_logout"); |
|
+ if (safe && !strcmp(safe, "Yes") && session_in_use(sid)) { |
|
+ log_error("Session is actively in use for mounted storage, " |
|
+ "and iscsid.safe_logout is configured.\n"); |
|
+ free(safe); |
|
+ return ISCSI_ERR_BUSY; |
|
+ } |
|
+ free(safe); |
|
+ |
|
/* FIXME: logout all active connections */ |
|
conn = &session->conn[0]; |
|
if (conn->logout_qtask) |
|
diff --git a/usr/sysfs.c b/usr/sysfs.c |
|
index d00c925..bbb00c0 100644 |
|
--- a/usr/sysfs.c |
|
+++ b/usr/sysfs.c |
|
@@ -709,3 +709,43 @@ int sysfs_set_param(char *id, char *subsys, char *attr_name, |
|
close(fd); |
|
return rc; |
|
} |
|
+ |
|
+char *sysfs_get_uevent_field(const char *path, const char *field) |
|
+{ |
|
+ char *uevent_path = NULL; |
|
+ FILE *f = NULL; |
|
+ char *line, buffer[1024]; |
|
+ char *ff, *d; |
|
+ char *out = NULL; |
|
+ |
|
+ uevent_path = calloc(1, PATH_MAX); |
|
+ if (!uevent_path) |
|
+ return NULL; |
|
+ snprintf(uevent_path, PATH_MAX, "%s/uevent", path); |
|
+ |
|
+ f = fopen(uevent_path, "r"); |
|
+ if (!f) |
|
+ goto out; |
|
+ while ((line = fgets(buffer, sizeof (buffer), f))) { |
|
+ ff = strtok(line, "="); |
|
+ d = strtok(NULL, "\n"); |
|
+ if (strcmp(ff, field)) |
|
+ continue; |
|
+ out = strdup(d); |
|
+ break; |
|
+ } |
|
+ fclose(f); |
|
+out: |
|
+ free(uevent_path); |
|
+ return out; |
|
+} |
|
+ |
|
+char *sysfs_get_uevent_devtype(const char *path) |
|
+{ |
|
+ return sysfs_get_uevent_field(path, "DEVTYPE"); |
|
+} |
|
+ |
|
+char *sysfs_get_uevent_devname(const char *path) |
|
+{ |
|
+ return sysfs_get_uevent_field(path, "DEVNAME"); |
|
+} |
|
diff --git a/usr/sysfs.h b/usr/sysfs.h |
|
index 304dbbf..462060e 100644 |
|
--- a/usr/sysfs.h |
|
+++ b/usr/sysfs.h |
|
@@ -66,4 +66,8 @@ extern int sysfs_get_uint16(char *id, char *subsys, char *param, |
|
extern int sysfs_set_param(char *id, char *subsys, char *attr_name, |
|
char *write_buf, ssize_t buf_size); |
|
|
|
+extern char *sysfs_get_uevent_field(const char *path, const char *field); |
|
+extern char *sysfs_get_uevent_devtype(const char *path); |
|
+extern char *sysfs_get_uevent_devname(const char *path); |
|
+ |
|
#endif |
|
-- |
|
2.1.0 |
|
|
|
|