diff options
-rw-r--r-- | etc/iscsid.conf | 3 | ||||
-rw-r--r-- | usr/Makefile | 4 | ||||
-rw-r--r-- | usr/initiator.c | 199 | ||||
-rw-r--r-- | usr/sysfs.c | 40 | ||||
-rw-r--r-- | 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 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 |