--- libmultipath/Makefile | 2 libmultipath/alias.c | 153 --------------------------------------- libmultipath/alias.h | 1 libmultipath/configure.c | 3 libmultipath/defaults.h | 1 libmultipath/discovery.c | 2 libmultipath/file.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++ libmultipath/file.h | 11 ++ libmultipath/wwids.c | 139 ++++++++++++++++++++++++++++++++++++ libmultipath/wwids.h | 18 ++++ multipath/main.c | 37 ++++++++- 11 files changed, 389 insertions(+), 158 deletions(-) Index: multipath-tools-120518/libmultipath/alias.c =================================================================== --- multipath-tools-120518.orig/libmultipath/alias.c +++ multipath-tools-120518/libmultipath/alias.c @@ -3,19 +3,16 @@ * Copyright (c) 2005 Benjamin Marzinski, Redhat */ #include -#include -#include -#include #include #include #include #include #include -#include #include "debug.h" #include "uxsock.h" #include "alias.h" +#include "file.h" /* @@ -36,150 +33,6 @@ * See the file COPYING included with this distribution for more details. */ -static int -ensure_directories_exist(char *str, mode_t dir_mode) -{ - char *pathname; - char *end; - int err; - - pathname = strdup(str); - if (!pathname){ - condlog(0, "Cannot copy bindings file pathname : %s", - strerror(errno)); - return -1; - } - end = pathname; - /* skip leading slashes */ - while (end && *end && (*end == '/')) - end++; - - while ((end = strchr(end, '/'))) { - /* if there is another slash, make the dir. */ - *end = '\0'; - err = mkdir(pathname, dir_mode); - if (err && errno != EEXIST) { - condlog(0, "Cannot make directory [%s] : %s", - pathname, strerror(errno)); - free(pathname); - return -1; - } - if (!err) - condlog(3, "Created dir [%s]", pathname); - *end = '/'; - end++; - } - free(pathname); - return 0; -} - -static void -sigalrm(int sig) -{ - /* do nothing */ -} - -static int -lock_bindings_file(int fd) -{ - struct sigaction act, oldact; - sigset_t set, oldset; - struct flock lock; - int err; - - memset(&lock, 0, sizeof(lock)); - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - - act.sa_handler = sigalrm; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - sigemptyset(&set); - sigaddset(&set, SIGALRM); - - sigaction(SIGALRM, &act, &oldact); - sigprocmask(SIG_UNBLOCK, &set, &oldset); - - alarm(BINDINGS_FILE_TIMEOUT); - err = fcntl(fd, F_SETLKW, &lock); - alarm(0); - - if (err) { - if (errno != EINTR) - condlog(0, "Cannot lock bindings file : %s", - strerror(errno)); - else - condlog(0, "Bindings file is locked. Giving up."); - } - - sigprocmask(SIG_SETMASK, &oldset, NULL); - sigaction(SIGALRM, &oldact, NULL); - return err; - -} - - -static int -open_bindings_file(char *file, int *can_write) -{ - int fd; - struct stat s; - - if (ensure_directories_exist(file, 0700)) - return -1; - *can_write = 1; - fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); - if (fd < 0) { - if (errno == EROFS) { - *can_write = 0; - condlog(3, "Cannot open bindings file [%s] read/write. " - " trying readonly", file); - fd = open(file, O_RDONLY); - if (fd < 0) { - condlog(0, "Cannot open bindings file [%s] " - "readonly : %s", file, strerror(errno)); - return -1; - } - } - else { - condlog(0, "Cannot open bindings file [%s] : %s", file, - strerror(errno)); - return -1; - } - } - if (*can_write && lock_bindings_file(fd) < 0) - goto fail; - - memset(&s, 0, sizeof(s)); - if (fstat(fd, &s) < 0){ - condlog(0, "Cannot stat bindings file : %s", strerror(errno)); - goto fail; - } - if (s.st_size == 0) { - if (*can_write == 0) - goto fail; - /* If bindings file is empty, write the header */ - size_t len = strlen(BINDINGS_FILE_HEADER); - if (write_all(fd, BINDINGS_FILE_HEADER, len) != len) { - condlog(0, - "Cannot write header to bindings file : %s", - strerror(errno)); - /* cleanup partially written header */ - if (ftruncate(fd, 0)) - condlog(0, "Cannot truncate the header : %s", - strerror(errno)); - goto fail; - } - fsync(fd); - condlog(3, "Initialized new bindings file [%s]", file); - } - - return fd; - -fail: - close(fd); - return -1; -} static int format_devname(char *name, int id, int len, char *prefix) @@ -370,7 +223,7 @@ get_user_friendly_alias(char *wwid, char return NULL; } - fd = open_bindings_file(file, &can_write); + fd = open_file(file, &can_write, BINDINGS_FILE_HEADER); if (fd < 0) return NULL; @@ -414,7 +267,7 @@ get_user_friendly_wwid(char *alias, char return NULL; } - fd = open_bindings_file(file, &unused); + fd = open_file(file, &unused, BINDINGS_FILE_HEADER); if (fd < 0) return NULL; Index: multipath-tools-120518/libmultipath/alias.h =================================================================== --- multipath-tools-120518.orig/libmultipath/alias.h +++ multipath-tools-120518/libmultipath/alias.h @@ -1,4 +1,3 @@ -#define BINDINGS_FILE_TIMEOUT 30 #define BINDINGS_FILE_HEADER \ "# Multipath bindings, Version : 1.0\n" \ "# NOTE: this file is automatically maintained by the multipath program.\n" \ Index: multipath-tools-120518/libmultipath/configure.c =================================================================== --- multipath-tools-120518.orig/libmultipath/configure.c +++ multipath-tools-120518/libmultipath/configure.c @@ -37,6 +37,7 @@ #include "prio.h" #include "util.h" #include "uxsock.h" +#include "wwids.h" extern int setup_map (struct multipath * mpp, char * params, int params_size) @@ -407,6 +408,8 @@ domap (struct multipath * mpp, char * pa * DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD * succeeded */ + if (mpp->action == ACT_CREATE) + remember_wwid(mpp->wwid); if (!conf->daemon) { /* multipath client mode */ dm_switchgroup(mpp->alias, mpp->bestpg); Index: multipath-tools-120518/libmultipath/defaults.h =================================================================== --- multipath-tools-120518.orig/libmultipath/defaults.h +++ multipath-tools-120518/libmultipath/defaults.h @@ -24,5 +24,6 @@ #define DEFAULT_SOCKET "/var/run/multipathd.sock" #define DEFAULT_CONFIGFILE "/etc/multipath.conf" #define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings" +#define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" char * set_default (char * str); Index: multipath-tools-120518/libmultipath/file.c =================================================================== --- /dev/null +++ multipath-tools-120518/libmultipath/file.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2005 Christophe Varoqui + * Copyright (c) 2005 Benjamin Marzinski, Redhat + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "file.h" +#include "debug.h" +#include "uxsock.h" + + +/* + * significant parts of this file were taken from iscsi-bindings.c of the + * linux-iscsi project. + * Copyright (C) 2002 Cisco Systems, 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 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. + */ + +static int +ensure_directories_exist(char *str, mode_t dir_mode) +{ + char *pathname; + char *end; + int err; + + pathname = strdup(str); + if (!pathname){ + condlog(0, "Cannot copy file pathname %s : %s", + str, strerror(errno)); + return -1; + } + end = pathname; + /* skip leading slashes */ + while (end && *end && (*end == '/')) + end++; + + while ((end = strchr(end, '/'))) { + /* if there is another slash, make the dir. */ + *end = '\0'; + err = mkdir(pathname, dir_mode); + if (err && errno != EEXIST) { + condlog(0, "Cannot make directory [%s] : %s", + pathname, strerror(errno)); + free(pathname); + return -1; + } + if (!err) + condlog(3, "Created dir [%s]", pathname); + *end = '/'; + end++; + } + free(pathname); + return 0; +} + +static void +sigalrm(int sig) +{ + /* do nothing */ +} + +static int +lock_file(int fd, char *file_name) +{ + struct sigaction act, oldact; + sigset_t set, oldset; + struct flock lock; + int err; + + memset(&lock, 0, sizeof(lock)); + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + + act.sa_handler = sigalrm; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + + sigaction(SIGALRM, &act, &oldact); + sigprocmask(SIG_UNBLOCK, &set, &oldset); + + alarm(FILE_TIMEOUT); + err = fcntl(fd, F_SETLKW, &lock); + alarm(0); + + if (err) { + if (errno != EINTR) + condlog(0, "Cannot lock %s : %s", file_name, + strerror(errno)); + else + condlog(0, "%s is locked. Giving up.", file_name); + } + + sigprocmask(SIG_SETMASK, &oldset, NULL); + sigaction(SIGALRM, &oldact, NULL); + return err; +} + +int +open_file(char *file, int *can_write, char *header) +{ + int fd; + struct stat s; + + if (ensure_directories_exist(file, 0700)) + return -1; + *can_write = 1; + fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + if (errno == EROFS) { + *can_write = 0; + condlog(3, "Cannot open file [%s] read/write. " + " trying readonly", file); + fd = open(file, O_RDONLY); + if (fd < 0) { + condlog(0, "Cannot open file [%s] " + "readonly : %s", file, strerror(errno)); + return -1; + } + } + else { + condlog(0, "Cannot open file [%s] : %s", file, + strerror(errno)); + return -1; + } + } + if (*can_write && lock_file(fd, file) < 0) + goto fail; + + memset(&s, 0, sizeof(s)); + if (fstat(fd, &s) < 0){ + condlog(0, "Cannot stat file %s : %s", file, strerror(errno)); + goto fail; + } + if (s.st_size == 0) { + if (*can_write == 0) + goto fail; + /* If file is empty, write the header */ + size_t len = strlen(header); + if (write_all(fd, header, len) != len) { + condlog(0, + "Cannot write header to file %s : %s", file, + strerror(errno)); + /* cleanup partially written header */ + if (ftruncate(fd, 0)) + condlog(0, "Cannot truncate header : %s", + strerror(errno)); + goto fail; + } + fsync(fd); + condlog(3, "Initialized new file [%s]", file); + } + + return fd; + +fail: + close(fd); + return -1; +} Index: multipath-tools-120518/libmultipath/file.h =================================================================== --- /dev/null +++ multipath-tools-120518/libmultipath/file.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2010 Benjamin Marzinski, Redhat + */ + +#ifndef _FILE_H +#define _FILE_H + +#define FILE_TIMEOUT 30 +int open_file(char *file, int *can_write, char *header); + +#endif /* _FILE_H */ Index: multipath-tools-120518/multipath/main.c =================================================================== --- multipath-tools-120518.orig/multipath/main.c +++ multipath-tools-120518/multipath/main.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "dev_t.h" int logsink; @@ -82,7 +83,7 @@ usage (char * progname) { fprintf (stderr, VERSION_STRING); fprintf (stderr, "Usage:\n"); - fprintf (stderr, " %s [-d] [-r] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname); + fprintf (stderr, " %s [-c] [-d] [-r] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname); fprintf (stderr, " %s -l|-ll|-f [-v lvl] [-b fil] [dev]\n", progname); fprintf (stderr, " %s -F [-v lvl]\n", progname); fprintf (stderr, " %s -t\n", progname); @@ -95,6 +96,7 @@ usage (char * progname) " -ll show multipath topology (maximum info)\n" \ " -f flush a multipath device map\n" \ " -F flush all multipath device maps\n" \ + " -c check if a device should be a path in a multipath device\n" \ " -q allow queue_if_no_path when multipathd is not running\n"\ " -d dry run, do not create or update devmaps\n" \ " -t dump internal hardware table\n" \ @@ -209,6 +211,7 @@ get_dm_mpvec (vector curmp, vector pathv if (!conf->dry_run) reinstate_paths(mpp); + remember_wwid(mpp->wwid); } return 0; } @@ -259,9 +262,13 @@ configure (void) * if we have a blacklisted device parameter, exit early */ if (dev && - (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) - goto out; - + (filter_devnode(conf->blist_devnode, + conf->elist_devnode, dev) > 0)) { + if (conf->dry_run == 2) + printf("%s is not a valid multipath device path\n", + conf->dev); + goto out; + } /* * scope limiting must be translated into a wwid * failing the translation is fatal (by policy) @@ -277,6 +284,15 @@ configure (void) if (filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid) > 0) goto out; + if (conf->dry_run == 2) { + if (check_wwids_file(refwwid, 0) == 0){ + printf("%s is a valid multipath device path\n", conf->dev); + r = 0; + } + else + printf("%s is not a valid multipath device path\n", conf->dev); + goto out; + } } /* @@ -412,7 +428,7 @@ main (int argc, char *argv[]) if (load_config(DEFAULT_CONFIGFILE)) exit(1); - while ((arg = getopt(argc, argv, ":dhl::FfM:v:p:b:Brtq")) != EOF ) { + while ((arg = getopt(argc, argv, ":dchl::FfM:v:p:b:Brtq")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); break; @@ -434,8 +450,12 @@ main (int argc, char *argv[]) case 'q': conf->allow_queueing = 1; break; + case 'c': + conf->dry_run = 2; + break; case 'd': - conf->dry_run = 1; + if (!conf->dry_run) + conf->dry_run = 1; break; case 'f': conf->remove = FLUSH_ONE; @@ -517,6 +537,11 @@ main (int argc, char *argv[]) } dm_init(); + if (conf->dry_run == 2 && + (!conf->dev || conf->dev_type == DEV_DEVMAP)) { + condlog(0, "the -c option requires a path to check"); + goto out; + } if (conf->remove == FLUSH_ONE) { if (conf->dev_type == DEV_DEVMAP) r = dm_flush_map(conf->dev); Index: multipath-tools-120518/libmultipath/Makefile =================================================================== --- multipath-tools-120518.orig/libmultipath/Makefile +++ multipath-tools-120518/libmultipath/Makefile @@ -15,7 +15,7 @@ OBJS = memory.o parser.o vector.o devmap pgpolicies.o debug.o regex.o defaults.o uevent.o \ switchgroup.o uxsock.o print.o alias.o log_pthread.o \ log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \ - lock.o waiter.o + lock.o waiter.o file.o wwids.o LIBDM_API_FLUSH = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_no_flush' /usr/include/libdevmapper.h) Index: multipath-tools-120518/libmultipath/wwids.c =================================================================== --- /dev/null +++ multipath-tools-120518/libmultipath/wwids.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include + +#include "checkers.h" +#include "vector.h" +#include "structs.h" +#include "debug.h" +#include "uxsock.h" +#include "file.h" +#include "wwids.h" +#include "defaults.h" + +/* + * Copyright (c) 2010 Benjamin Marzinski, Redhat + */ + +static int +lookup_wwid(FILE *f, char *wwid) { + int c; + char buf[LINE_MAX]; + int count; + + while ((c = fgetc(f)) != EOF){ + if (c != '/') { + if (fgets(buf, LINE_MAX, f) == NULL) + return 0; + else + continue; + } + count = 0; + while ((c = fgetc(f)) != '/') { + if (c == EOF) + return 0; + if (count >= WWID_SIZE - 1) + goto next; + if (wwid[count] == '\0') + goto next; + if (c != wwid[count++]) + goto next; + } + if (wwid[count] == '\0') + return 1; +next: + if (fgets(buf, LINE_MAX, f) == NULL) + return 0; + } + return 0; +} + +static int +write_out_wwid(int fd, char *wwid) { + int ret; + off_t offset; + char buf[WWID_SIZE + 3]; + + ret = snprintf(buf, WWID_SIZE + 3, "/%s/\n", wwid); + if (ret >= (WWID_SIZE + 3) || ret < 0){ + condlog(0, "can't format wwid for writing (%d) : %s", + ret, strerror(errno)); + return -1; + } + offset = lseek(fd, 0, SEEK_END); + if (offset < 0) { + condlog(0, "can't seek to the end of wwids file : %s", + strerror(errno)); + return -1; + } + if (write_all(fd, buf, strlen(buf)) != strlen(buf)) { + condlog(0, "cannot write wwid to wwids file : %s", + strerror(errno)); + if (ftruncate(fd, offset)) + condlog(0, "cannot truncate failed wwid write : %s", + strerror(errno)); + return -1; + } + return 1; +} + +int +check_wwids_file(char *wwid, int write_wwid) +{ + int fd, can_write, found, ret; + FILE *f; + fd = open_file(DEFAULT_WWIDS_FILE, &can_write, WWIDS_FILE_HEADER); + if (fd < 0) + return -1; + + f = fdopen(fd, "r"); + if (!f) { + condlog(0,"can't fdopen wwids file : %s", strerror(errno)); + close(fd); + return -1; + } + found = lookup_wwid(f, wwid); + if (found) { + ret = 0; + goto out; + } + if (!write_wwid) { + ret = -1; + goto out; + } + if (!can_write) { + condlog(0, "wwids file is read-only. Can't write wwid"); + ret = -1; + goto out; + } + + if (fflush(f) != 0) { + condlog(0, "cannot fflush wwids file stream : %s", + strerror(errno)); + ret = -1; + goto out; + } + + ret = write_out_wwid(fd, wwid); +out: + fclose(f); + return ret; +} + +int +remember_wwid(char *wwid) +{ + int ret = check_wwids_file(wwid, 1); + if (ret < 0){ + condlog(3, "failed writing wwid %s to wwids file", wwid); + return -1; + } + if (ret == 1) + condlog(3, "wrote wwid %s to wwids file", wwid); + else + condlog(4, "wwid %s already in wwids file", wwid); + return 0; +} Index: multipath-tools-120518/libmultipath/wwids.h =================================================================== --- /dev/null +++ multipath-tools-120518/libmultipath/wwids.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2010 Benjamin Marzinski, Redhat + */ + +#ifndef _WWIDS_H +#define _WWIDS_H + +#define WWIDS_FILE_HEADER \ +"# Multipath wwids, Version : 1.0\n" \ +"# NOTE: This file is automatically maintained by multipath and multipathd.\n" \ +"# You should not need to edit this file in normal circumstances.\n" \ +"#\n" \ +"# Valid WWIDs:\n" + +int remember_wwid(char *wwid); +int check_wwids_file(char *wwid, int write_wwid); + +#endif /* _WWIDS_H */ Index: multipath-tools-120518/libmultipath/discovery.c =================================================================== --- multipath-tools-120518.orig/libmultipath/discovery.c +++ multipath-tools-120518/libmultipath/discovery.c @@ -810,6 +810,8 @@ get_uid (struct path * pp) memset(pp->wwid, 0, WWID_SIZE); value = udev_device_get_property_value(pp->udev, pp->uid_attribute); + if ((!value || strlen(value) == 0) && conf->dry_run == 2) + value = getenv(pp->uid_attribute); if (value && strlen(value)) { size_t len = WWID_SIZE;