summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Leech <cleech@redhat.com>2015-02-03 16:28:15 -0800
committerMike Christie <michaelc@cs.wisc.edu>2015-03-02 00:41:57 -0600
commit63d086c9cb84b881e13ce8a5b4ddb80234948258 (patch)
treed21edfa0f47fea78c6179758af4beeedea6d5e36
parentc02b558f58e6b34f2c3e298e7552ac829a554d11 (diff)
downloadopen-iscsi-63d086c9cb84b881e13ce8a5b4ddb80234948258.tar.gz
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>
-rw-r--r--etc/iscsid.conf3
-rw-r--r--usr/Makefile4
-rw-r--r--usr/initiator.c199
-rw-r--r--usr/sysfs.c40
-rw-r--r--usr/sysfs.h4
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 550fdff..9209d5d 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 -lrt
+ $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lrt -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 $@ -lrt
+ $(CC) $(CFLAGS) $^ -o $@ -lrt -lmount
clean:
rm -f *.o $(PROGRAMS) .depend $(LIBSYS)
diff --git a/usr/initiator.c b/usr/initiator.c
index c6c9c1a..2f17882 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"
@@ -2139,11 +2140,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) {
@@ -2166,6 +2356,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