Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
export KRNLOBJ
BUILDDIRS = \
+ libmpathcmd \
libmultipath \
libmultipath/prioritizers \
libmultipath/checkers \
endif
endif
+ifndef RUN
+ ifeq ($(shell test -L /var/run -o ! -d /var/run && echo 1),1)
+ RUN=run
+ else
+ RUN=var/run
+ endif
+endif
+
ifndef SYSTEMD
ifeq ($(shell systemctl --version > /dev/null 2>&1 && echo 1), 1)
SYSTEMD = $(shell systemctl --version 2> /dev/null | sed -n 's/systemd \([0-9]*\)/\1/p')
endif
endif
+ifndef SYSTEMDPATH
+ SYSTEMDPATH=usr/lib
+endif
+
prefix =
exec_prefix = $(prefix)
bindir = $(exec_prefix)/sbin
-libudevdir = ${prefix}/lib/udev
+libudevdir = $(prefix)/$(SYSTEMDPATH)/udev
+udevrulesdir = $(libudevdir)/rules.d
multipathdir = $(TOPDIR)/libmultipath
mandir = $(prefix)/usr/share/man/man8
man5dir = $(prefix)/usr/share/man/man5
man3dir = $(prefix)/usr/share/man/man3
rcdir = $(prefix)/etc/init.d
syslibdir = $(prefix)/$(LIB)
+incdir = $(prefix)/usr/include
libdir = $(prefix)/$(LIB)/multipath
-unitdir = $(prefix)/usr/lib/systemd/system
+unitdir = $(prefix)/$(SYSTEMDPATH)/systemd/system
mpathpersistdir = $(TOPDIR)/libmpathpersist
+mpathcmddir = $(TOPDIR)/libmpathcmd
GZIP = gzip -9 -c
INSTALL_PROGRAM = install
ifndef RPM_OPT_FLAGS
- RPM_OPT_FLAGS = -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4
+ RPM_OPT_FLAGS = -O2 -g -pipe -Wformat-security -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4
endif
OPTFLAGS = $(RPM_OPT_FLAGS) -Wunused -Wstrict-prototypes
-CFLAGS = $(OPTFLAGS) -fPIC -DLIB_STRING=\"${LIB}\"
+CFLAGS = $(OPTFLAGS) -fPIC -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\"
SHARED_FLAGS = -shared
%.o: %.c
#define MAX_PREFIX_LEN 8
#define PARAMS_SIZE 1024
-#ifndef LIBDM_API_COOKIE
-static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a)
-{
- return 1;
-}
-#endif
-
extern int
dm_prereq (char * str, int x, int y, int z)
{
}
extern int
-dm_simplecmd (int task, const char *name, int no_flush, uint32_t *cookie) {
+dm_simplecmd (int task, const char *name, int no_flush, uint16_t udev_flags) {
int r = 0;
int udev_wait_flag = (task == DM_DEVICE_RESUME ||
task == DM_DEVICE_REMOVE);
+#ifdef LIBDM_API_COOKIE
+ uint32_t cookie = 0;
+#endif
struct dm_task *dmt;
if (!(dmt = dm_task_create(task)))
if (no_flush)
dm_task_no_flush(dmt);
- if (udev_wait_flag && !dm_task_set_cookie(dmt, cookie, (udev_sync)? 0 : DM_UDEV_DISABLE_LIBRARY_FALLBACK))
+#ifdef LIBDM_API_COOKIE
+ if (!udev_sync)
+ udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+ if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags)) {
+ dm_udev_complete(cookie);
goto out;
+ }
+#endif
r = dm_task_run(dmt);
-
+#ifdef LIBDM_API_COOKIE
+ if (udev_wait_flag) {
+ if (!r)
+ dm_udev_complete(cookie);
+ else
+ dm_udev_wait(cookie);
+ }
+#endif
out:
dm_task_destroy(dmt);
return r;
extern int
dm_addmap (int task, const char *name, const char *target,
const char *params, uint64_t size, int ro, const char *uuid, int part,
- mode_t mode, uid_t uid, gid_t gid, uint32_t *cookie) {
+ mode_t mode, uid_t uid, gid_t gid) {
int r = 0;
struct dm_task *dmt;
char *prefixed_uuid = NULL;
+#ifdef LIBDM_API_COOKIE
+ uint32_t cookie = 0;
+ uint16_t udev_flags = 0;
+#endif
if (!(dmt = dm_task_create (task)))
return 0;
dm_task_no_open_count(dmt);
- if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, cookie, (udev_sync)? 0 : DM_UDEV_DISABLE_LIBRARY_FALLBACK))
+#ifdef LIBDM_API_COOKIE
+ if (!udev_sync)
+ udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+ if (task == DM_DEVICE_CREATE &&
+ !dm_task_set_cookie(dmt, &cookie, udev_flags)) {
+ dm_udev_complete(cookie);
goto addout;
+ }
+#endif
r = dm_task_run (dmt);
-
- addout:
+#ifdef LIBDM_API_COOKIE
+ if (task == DM_DEVICE_CREATE) {
+ if (!r)
+ dm_udev_complete(cookie);
+ else
+ dm_udev_wait(cookie);
+ }
+#endif
+addout:
dm_task_destroy (dmt);
+ free(prefixed_uuid);
return r;
}
extern int
-dm_map_present (char * str)
+dm_map_present (char * str, char **uuid)
{
int r = 0;
struct dm_task *dmt;
+ const char *uuidtmp;
struct dm_info info;
+ if (uuid)
+ *uuid = NULL;
+
if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
return 0;
if (!dm_task_get_info(dmt, &info))
goto out;
- if (info.exists)
- r = 1;
+ if (!info.exists)
+ goto out;
+
+ r = 1;
+ if (uuid) {
+ uuidtmp = dm_task_get_uuid(dmt);
+ if (uuidtmp && strlen(uuidtmp))
+ *uuid = strdup(uuidtmp);
+ }
out:
dm_task_destroy(dmt);
return r;
-#define MAJOR(dev) ((dev & 0xfff00) >> 8)
-#define MINOR(dev) ((dev & 0xff) | ((dev >> 12) & 0xfff00))
-#define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
+#ifndef _KPARTX_DEVMAPPER_H
+#define _KPARTX_DEVMAPPER_H
+
+#ifdef DM_SUBSYSTEM_UDEV_FLAG0
+#define MPATH_UDEV_RELOAD_FLAG DM_SUBSYSTEM_UDEV_FLAG0
+#else
+#define MPATH_UDEV_RELOAD_FLAG 0
+#endif
extern int udev_sync;
int dm_prereq (char *, int, int, int);
-int dm_simplecmd (int, const char *, int, uint32_t *);
+int dm_simplecmd (int, const char *, int, uint16_t);
int dm_addmap (int, const char *, const char *, const char *, uint64_t,
- int, const char *, int, mode_t, uid_t, gid_t, uint32_t *);
-int dm_map_present (char *);
+ int, const char *, int, mode_t, uid_t, gid_t);
+int dm_map_present (char *, char **);
char * dm_mapname(int major, int minor);
dev_t dm_get_first_dep(char *devname);
char * dm_mapuuid(int major, int minor);
int dm_devn (char * mapname, int *major, int *minor);
int dm_no_partitions(int major, int minor);
+
+#endif /* _KPARTX_DEVMAPPER_H */
break;
}
if (is_extended(p.sys_type)) {
- sp[i].size = sector_size_mul * 2; /* extended partitions only get two
- sectors mapped for LILO to install */
+ /* extended partitions only get one or
+ two sectors mapped for LILO to install,
+ whichever is needed to have 1kb of space */
+ if (sector_size_mul == 1)
+ sp[i].size = 2;
+ else sp[i].size = sector_size_mul;
n += read_extended_partition(fd, &p, i, sp+n, ns-n);
}
}
if (stat(devname, &buf))
return NULL;
- major = (unsigned int)MAJOR(buf.st_rdev);
- minor = (unsigned int)MINOR(buf.st_rdev);
+ major = major(buf.st_rdev);
+ minor = minor(buf.st_rdev);
if (!(mapname = dm_mapname(major, minor))) /* Not dm device. */
return NULL;
return device;
}
+static int
+check_uuid(char *uuid, char *part_uuid, char **err_msg) {
+ char *map_uuid = strchr(part_uuid, '-');
+ if (!map_uuid || strncmp(part_uuid, "part", 4) != 0) {
+ *err_msg = "not a kpartx partition";
+ return -1;
+ }
+ map_uuid++;
+ if (strcmp(uuid, map_uuid) != 0) {
+ *err_msg = "a partition of a different device";
+ return -1;
+ }
+ return 0;
+}
+
int
main(int argc, char **argv){
int i, j, m, n, op, off, arg, c, d, ro=0;
int hotplug = 0;
int loopcreated = 0;
struct stat buf;
- uint32_t cookie = 0;
initpts();
init_crc32();
#ifdef LIBDM_API_COOKIE
if (!udev_sync)
dm_udev_set_sync_support(0);
+ else
+ dm_udev_set_sync_support(1);
#endif
if (dm_prereq(DM_TARGET, 0, 0, 0) && (what == ADD || what == DELETE || what == UPDATE)) {
loopcreated = 1;
}
device = loopdev;
+
+ if (stat(device, &buf)) {
+ printf("failed to stat() %s\n", device);
+ exit (1);
+ }
}
off = find_devname_offset(device);
if (!loopdev) {
- uuid = dm_mapuuid((unsigned int)MAJOR(buf.st_rdev),
- (unsigned int)MINOR(buf.st_rdev));
- mapname = dm_mapname((unsigned int)MAJOR(buf.st_rdev),
- (unsigned int)MINOR(buf.st_rdev));
+ uuid = dm_mapuuid(major(buf.st_rdev), minor(buf.st_rdev));
+ mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev));
}
if (!uuid)
if (!mapname)
mapname = device + off;
else if (!force_devmap &&
- dm_no_partitions((unsigned int)MAJOR(buf.st_rdev),
- (unsigned int)MINOR(buf.st_rdev))) {
+ dm_no_partitions(major(buf.st_rdev), minor(buf.st_rdev))) {
/* Feature 'no_partitions' is set, return */
return 0;
}
break;
case DELETE:
- for (j = n-1; j >= 0; j--) {
+ for (j = MAXSLICES-1; j >= 0; j--) {
+ char *part_uuid, *reason;
+
if (safe_sprintf(partname, "%s%s%d",
mapname, delim, j+1)) {
fprintf(stderr, "partname too small\n");
}
strip_slash(partname);
- if (!slices[j].size || !dm_map_present(partname))
+ if (!dm_map_present(partname, &part_uuid))
continue;
+ if (part_uuid && uuid) {
+ if (check_uuid(uuid, part_uuid, &reason) != 0) {
+ fprintf(stderr, "%s is %s. Not removing\n", partname, reason);
+ free(part_uuid);
+ continue;
+ }
+ free(part_uuid);
+ }
+
if (!dm_simplecmd(DM_DEVICE_REMOVE, partname,
- 0, &cookie)) {
+ 0, 0)) {
r++;
continue;
}
case UPDATE:
/* ADD and UPDATE share the same code that adds new partitions. */
for (j = 0, c = 0; j < n; j++) {
+ char *part_uuid, *reason;
+
if (slices[j].size == 0)
continue;
}
strip_slash(partname);
- if (safe_sprintf(params, "%s %" PRIu64 ,
- device, slices[j].start)) {
+ if (safe_sprintf(params, "%d:%d %" PRIu64 ,
+ major(buf.st_rdev), minor(buf.st_rdev), slices[j].start)) {
fprintf(stderr, "params too small\n");
exit(1);
}
- op = (dm_map_present(partname) ?
+ op = (dm_map_present(partname, &part_uuid) ?
DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
+ if (part_uuid && uuid) {
+ if (check_uuid(uuid, part_uuid, &reason) != 0) {
+ fprintf(stderr, "%s is already in use, and %s\n", partname, reason);
+ r++;
+ free(part_uuid);
+ continue;
+ }
+ free(part_uuid);
+ }
+
if (!dm_addmap(op, partname, DM_TARGET, params,
slices[j].size, ro, uuid, j+1,
buf.st_mode & 0777, buf.st_uid,
- buf.st_gid, &cookie)) {
+ buf.st_gid)) {
fprintf(stderr, "create/reload failed on %s\n",
partname);
r++;
+ continue;
}
if (op == DM_DEVICE_RELOAD &&
!dm_simplecmd(DM_DEVICE_RESUME, partname,
- 1, &cookie)) {
+ 1, MPATH_UDEV_RELOAD_FLAG)) {
fprintf(stderr, "resume failed on %s\n",
partname);
r++;
+ continue;
}
+
dm_devn(partname, &slices[j].major,
&slices[j].minor);
d = c;
while (c) {
for (j = 0; j < n; j++) {
+ char *part_uuid, *reason;
int k = slices[j].container - 1;
if (slices[j].size == 0)
}
strip_slash(partname);
- if (safe_sprintf(params, "%s %" PRIu64,
- device,
+ if (safe_sprintf(params, "%d:%d %" PRIu64,
+ major(buf.st_rdev), minor(buf.st_rdev),
slices[j].start)) {
fprintf(stderr, "params too small\n");
exit(1);
}
- op = (dm_map_present(partname) ?
+ op = (dm_map_present(partname,
+ &part_uuid) ?
DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
+ if (part_uuid && uuid) {
+ if (check_uuid(uuid, part_uuid, &reason) != 0) {
+ fprintf(stderr, "%s is already in use, and %s\n", partname, reason);
+ free(part_uuid);
+ continue;
+ }
+ free(part_uuid);
+ }
+
dm_addmap(op, partname, DM_TARGET, params,
slices[j].size, ro, uuid, j+1,
buf.st_mode & 0777,
- buf.st_uid, buf.st_gid,
- &cookie);
+ buf.st_uid, buf.st_gid);
if (op == DM_DEVICE_RELOAD)
dm_simplecmd(DM_DEVICE_RESUME,
partname, 1,
- &cookie);
-
+ MPATH_UDEV_RELOAD_FLAG);
dm_devn(partname, &slices[j].major,
&slices[j].minor);
if (verbose)
- printf("add map %s : 0 %" PRIu64 " %s %s\n",
- partname, slices[j].size,
+ printf("add map %s (%d:%d): 0 %" PRIu64 " %s %s\n",
+ partname, slices[j].major, slices[j].minor, slices[j].size,
DM_TARGET, params);
c--;
}
}
for (j = MAXSLICES-1; j >= 0; j--) {
+ char *part_uuid, *reason;
if (safe_sprintf(partname, "%s%s%d",
mapname, delim, j+1)) {
fprintf(stderr, "partname too small\n");
}
strip_slash(partname);
- if (slices[j].size || !dm_map_present(partname))
+ if (slices[j].size ||
+ !dm_map_present(partname, &part_uuid))
continue;
+ if (part_uuid && uuid) {
+ if (check_uuid(uuid, part_uuid, &reason) != 0) {
+ fprintf(stderr, "%s is %s. Not removing\n", partname, reason);
+ free(part_uuid);
+ continue;
+ }
+ free(part_uuid);
+ }
+
if (!dm_simplecmd(DM_DEVICE_REMOVE,
- partname, 1, &cookie)) {
+ partname, 1, 0)) {
r++;
continue;
}
}
printf("loop deleted : %s\n", device);
}
-#ifdef LIBDM_API_COOKIE
- dm_udev_wait(cookie);
-#endif
+
dm_lib_release();
dm_lib_exit();
ENV{DM_UUID}=="?*", IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
-ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0", \
- GOTO="blkid_end"
-ENV{DM_UUID}=="mpath-*", IMPORT{program}="/sbin/blkid -o udev -p $tempnode"
-ENV{DM_PART}=="?*", IMPORT{program}="/sbin/blkid -o udev -p $tempnode"
-LABEL="blkid_end"
-
OPTIONS="link_priority=50"
# Create persistent links for multipath tables
SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
# Create dm tables for partitions
-ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0", \
- GOTO="kpartx_end"
+ENV{DM_ACTION}=="PATH_FAILED|PATH_REINSTATED", GOTO="kpartx_end"
+ENV{DM_NR_VALID_PATHS}=="0", GOTO="kpartx_end"
ENV{DM_STATE}!="SUSPENDED", ENV{DM_UUID}=="mpath-*", \
RUN+="/sbin/kpartx -u -p -part /dev/$name"
int i, j, fd;
struct stat statbuf;
struct loop_info loopinfo;
+ dev_t file_dev;
+ ino_t file_ino;
+
+ if (stat (filename, &statbuf) != 0) {
+ return NULL;
+ }
+ file_dev = statbuf.st_dev;
+ file_ino = statbuf.st_ino;
for (j = 0; j < SIZE(loop_formats); j++) {
continue;
}
- if (0 == strcmp(filename, loopinfo.lo_name)) {
+ if (loopinfo.lo_device == file_dev && loopinfo.lo_inode == file_ino) {
close (fd);
return xstrdup(dev); /*found */
}
for(i=0, n=0; i<SUN_DISK_MAXPARTITIONS; i++) {
s = &l->partitions[i];
- if (s->num_sectors == 0)
- continue;
if (n < ns) {
sp[n].start = offset +
be32_to_cpu(s->start_cylinder) * be16_to_cpu(l->nsect) * be16_to_cpu(l->ntrks);
--- /dev/null
+# Makefile
+#
+include ../Makefile.inc
+
+SONAME=0
+DEVLIB = libmpathcmd.so
+LIBS = $(DEVLIB).$(SONAME)
+
+OBJS = mpath_cmd.o
+
+all: $(LIBS)
+
+$(LIBS): $(OBJS)
+ $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ $(CFLAGS) -o $@ $(OBJS) $(LIBDEPS)
+ ln -sf $@ $(DEVLIB)
+
+install: $(LIBS)
+ $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir)
+ $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
+ ln -sf $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
+ $(INSTALL_PROGRAM) -d $(DESTDIR)$(incdir)
+ $(INSTALL_PROGRAM) -m 755 mpath_cmd.h $(DESTDIR)$(incdir)
+
+uninstall:
+ rm -f $(DESTDIR)$(syslibdir)/$(LIBS)
+ rm -f $(DESTDIR)$(syslibdir)/$(DEVLIB)
+ rm -f $(DESTDIR)$(incdir)/mpath_cmd.h
+
+clean:
+ rm -f core *.a *.o *.gz *.so *.so.*
--- /dev/null
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <string.h>
+#include <errno.h>
+
+#include "mpath_cmd.h"
+
+/*
+ * keep reading until its all read
+ */
+static ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout)
+{
+ size_t total = 0;
+ ssize_t n;
+ int ret;
+ struct pollfd pfd;
+
+ while (len) {
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ ret = poll(&pfd, 1, timeout);
+ if (!ret) {
+ errno = ETIMEDOUT;
+ return -1;
+ } else if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ } else if (!pfd.revents & POLLIN)
+ continue;
+ n = read(fd, buf, len);
+ if (n < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ return -1;
+ }
+ if (!n)
+ return total;
+ buf = n + (char *)buf;
+ len -= n;
+ total += n;
+ }
+ return total;
+}
+
+/*
+ * keep writing until it's all sent
+ */
+static size_t write_all(int fd, const void *buf, size_t len)
+{
+ size_t total = 0;
+
+ while (len) {
+ ssize_t n = write(fd, buf, len);
+ if (n < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ return total;
+ }
+ if (!n)
+ return total;
+ buf = n + (char *)buf;
+ len -= n;
+ total += n;
+ }
+ return total;
+}
+
+/*
+ * connect to a unix domain socket
+ */
+int mpath_connect(void)
+{
+ int fd, len;
+ struct sockaddr_un addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_LOCAL;
+ addr.sun_path[0] = '\0';
+ len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t);
+ strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len);
+
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (fd == -1)
+ return -1;
+
+ if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int mpath_disconnect(int fd)
+{
+ return close(fd);
+}
+
+ssize_t mpath_recv_reply_len(int fd, unsigned int timeout)
+{
+ size_t len;
+ ssize_t ret;
+
+ ret = read_all(fd, &len, sizeof(len), timeout);
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(len)) {
+ errno = EIO;
+ return ret;
+ }
+ return len;
+}
+
+int mpath_recv_reply_data(int fd, char *reply, size_t len,
+ unsigned int timeout)
+{
+ ssize_t ret;
+
+ ret = read_all(fd, reply, len, timeout);
+ if (ret < 0)
+ return ret;
+ if (ret != len) {
+ errno = EIO;
+ return -1;
+ }
+ reply[len - 1] = '\0';
+ return 0;
+}
+
+int mpath_recv_reply(int fd, char **reply, unsigned int timeout)
+{
+ int err;
+ ssize_t len;
+
+ *reply = NULL;
+ len = mpath_recv_reply_len(fd, timeout);
+ if (len <= 0)
+ return len;
+ *reply = malloc(len);
+ if (!*reply)
+ return -1;
+ err = mpath_recv_reply_data(fd, *reply, len, timeout);
+ if (err) {
+ free(*reply);
+ *reply = NULL;
+ return err;
+ }
+ return 0;
+}
+
+int mpath_send_cmd(int fd, const char *cmd)
+{
+ size_t len;
+
+ if (cmd != NULL)
+ len = strlen(cmd) + 1;
+ else
+ len = 0;
+ if (write_all(fd, &len, sizeof(len)) != sizeof(len))
+ return -1;
+ if (len && write_all(fd, cmd, len) != len)
+ return -1;
+ return 0;
+}
+
+int mpath_process_cmd(int fd, const char *cmd, char **reply,
+ unsigned int timeout)
+{
+ if (mpath_send_cmd(fd, cmd) != 0)
+ return -1;
+ return mpath_recv_reply(fd, reply, timeout);
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This file is part of the device-mapper multipath userspace tools.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef LIB_MPATH_CMD_H
+#define LIB_MPATH_CMD_H
+
+#ifdef __cpluscplus
+extern "C" {
+#endif
+
+#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
+#define DEFAULT_REPLY_TIMEOUT 1000
+
+
+/*
+ * DESCRIPTION:
+ * Connect to the running multipathd daemon. On systems with the
+ * multipathd.socket systemd unit file installed, this command will
+ * start multipathd if it is not already running. This function
+ * must be run before any of the others in this library
+ *
+ * RETURNS:
+ * A file descriptor on success. -1 on failure (with errno set).
+ */
+int mpath_connect(void);
+
+
+/*
+ * DESCRIPTION:
+ * Disconnect from the multipathd daemon. This function must be
+ * run after after processing all the multipath commands.
+ *
+ * RETURNS:
+ * 0 on success. -1 on failure (with errno set).
+ */
+int mpath_disconnect(int fd);
+
+
+/*
+ * DESCRIPTION
+ * Send multipathd a command and return the reply. This function
+ * does the same as calling mpath_send_cmd() and then
+ * mpath_recv_reply()
+ *
+ * RETURNS:
+ * 0 on successs, and reply will either be NULL (if there was no
+ * reply data), or point to the reply string, which must be freed by
+ * the caller. -1 on failure (with errno set).
+ */
+int mpath_process_cmd(int fd, const char *cmd, char **reply,
+ unsigned int timeout);
+
+
+/*
+ * DESCRIPTION:
+ * Send a command to multipathd
+ *
+ * RETURNS:
+ * 0 on success. -1 on failure (with errno set)
+ */
+int mpath_send_cmd(int fd, const char *cmd);
+
+
+/*
+ * DESCRIPTION:
+ * Return a reply from multipathd for a previously sent command.
+ * This is equivalent to calling mpath_recv_reply_len(), allocating
+ * a buffer of the appropriate size, and then calling
+ * mpath_recv_reply_data() with that buffer.
+ *
+ * RETURNS:
+ * 0 on success, and reply will either be NULL (if there was no
+ * reply data), or point to the reply string, which must be freed by
+ * the caller, -1 on failure (with errno set).
+ */
+int mpath_recv_reply(int fd, char **reply, unsigned int timeout);
+
+
+/*
+ * DESCRIPTION:
+ * Return the size of the upcoming reply data from the sent multipath
+ * command. This must be called before calling mpath_recv_reply_data().
+ *
+ * RETURNS:
+ * The required size of the reply data buffer on success. -1 on
+ * failure (with errno set).
+ */
+ssize_t mpath_recv_reply_len(int fd, unsigned int timeout);
+
+
+/*
+ * DESCRIPTION:
+ * Return the reply data from the sent multipath command.
+ * mpath_recv_reply_len must be called first. reply must point to a
+ * buffer of len size.
+ *
+ * RETURNS:
+ * 0 on success, and reply will contain the reply data string. -1
+ * on failure (with errno set).
+ */
+int mpath_recv_reply_data(int fd, char *reply, size_t len,
+ unsigned int timeout);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LIB_MPATH_CMD_H */
LIBS = $(DEVLIB).$(SONAME)
-CFLAGS += -I$(multipathdir) -I$(mpathpersistdir)
-LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath
+CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir)
+LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath \
+ -L$(mpathcmddir) -lmpathcmd
OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o
$(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
$(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(syslibdir)
$(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(man3dir)
- $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)/usr/include/
+ $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(incdir)
$(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)/usr/share/doc/mpathpersist/
ln -sf $(DESTDIR)$(syslibdir)/$(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
install -m 644 mpath_persistent_reserve_in.3.gz $(DESTDIR)$(man3dir)
install -m 644 mpath_persistent_reserve_out.3.gz $(DESTDIR)$(man3dir)
- install -m 644 mpath_persist.h $(DESTDIR)/usr/include/
+ install -m 644 mpath_persist.h $(DESTDIR)$(incdir)
uninstall:
rm -f $(DESTDIR)$(syslibdir)/$(LIBS)
- rm $(DESTDIR)$(mandir)/mpath_persistent_reserve_in.3.gz
- rm $(DESTDIR)$(mandir)/mpath_persistent_reserve_out.3.gz
+ rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz
+ rm $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3.gz
clean:
rm -f core *.a *.o
#include <libdevmapper.h>
#include <defaults.h>
#include <sys/stat.h>
-#include <linux/kdev_t.h>
+#include <sys/types.h>
#include <fcntl.h>
#include <vector.h>
#include <checkers.h>
#include <dmparser.h>
#include <ctype.h>
#include <propsel.h>
+#include <util.h>
#include "mpath_persist.h"
#include "mpathpr.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+#include <sys/resource.h>
#define __STDC_FORMAT_MACROS 1
return 1;
}
+ if (conf->max_fds) {
+ struct rlimit fd_limit;
+
+ fd_limit.rlim_cur = conf->max_fds;
+ fd_limit.rlim_max = conf->max_fds;
+ if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0)
+ condlog(0, "can't set open fds limit to %d : %s",
+ conf->max_fds, strerror(errno));
+ }
+
return 0;
}
vector_foreach_slot (pgp->paths, pp, j){
if (!strlen(pp->dev)){
- if (devt2devname(pp->dev, pp->dev_t)){
+ if (devt2devname(pp->dev, PATH_SIZE,
+ pp->dev_t)){
/*
* path is not in sysfs anymore
*/
return 0;
}
-int
+int
mpath_prin_activepath (struct multipath *mpp, int rq_servact,
struct prin_resp * resp, int noisy)
{
vector_foreach_slot (mpp->pg, pgp, j){
vector_foreach_slot (pgp->paths, pp, i){
- if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
- condlog(2, "%s: %s not available. Skip.", mpp->wwid, pp->dev);
- condlog(3, "%s: status = %d.", mpp->wwid, pp->state);
+ if (!((pp->state == PATH_UP) ||
+ (pp->state == PATH_GHOST))){
+ condlog(2, "%s: %s not available. Skip.",
+ mpp->wwid, pp->dev);
+ condlog(3, "%s: status = %d.",
+ mpp->wwid, pp->state);
continue;
}
- condlog(3, "%s: sending pr in command to %s ", mpp->wwid, pp->dev);
- ret = mpath_send_prin_activepath(pp->dev, rq_servact, resp, noisy);
+ condlog(3, "%s: sending pr in command to %s ",
+ mpp->wwid, pp->dev);
+ ret = mpath_send_prin_activepath(pp->dev, rq_servact,
+ resp, noisy);
switch(ret)
{
case MPATH_PR_SUCCESS:
}
}
}
- return ret;
+ return ret;
}
-int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose)
+int mpath_persistent_reserve_in (int fd, int rq_servact,
+ struct prin_resp *resp, int noisy, int verbose)
{
struct stat info;
vector curmp = NULL;
if (fstat( fd, &info) != 0){
condlog(0, "stat error %d", fd);
return MPATH_PR_FILE_ERROR;
- }
+ }
if(!S_ISBLK(info.st_mode)){
condlog(0, "Failed to get major:minor. fd = %d", fd);
return MPATH_PR_FILE_ERROR;
}
- major = (int)MAJOR(info.st_rdev);
- minor = (int)MINOR(info.st_rdev);
+ major = major(info.st_rdev);
+ minor = minor(info.st_rdev);
condlog(4, "Device %d:%d: ", major, minor);
/* get alias from major:minor*/
condlog(3, "alias = %s", alias);
map_present = dm_map_present(alias);
- if (map_present && dm_type(alias, TGT_MPATH) <= 0){
+ if (map_present && !dm_is_mpath(alias)){
condlog( 0, "%s: not a multipath device.", alias);
ret = MPATH_PR_DMMP_ERROR;
goto out;
goto out;
}
- if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER)) {
+ if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER) < 0) {
ret = MPATH_PR_DMMP_ERROR;
goto out1;
}
out1:
free_multipathvec(curmp, KEEP_PATHS);
- free_pathvec(pathvec, FREE_PATHS);
+ free_pathvec(pathvec, FREE_PATHS);
out:
FREE(alias);
- return ret;
+ return ret;
}
int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose)
+ unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose)
{
struct stat info;
int ret;
int j;
unsigned char *keyp;
- uint64_t prkey;
+ uint64_t prkey;
conf->verbosity = verbose;
if(!S_ISBLK(info.st_mode)){
condlog(3, "Failed to get major:minor. fd=%d", fd);
- return MPATH_PR_FILE_ERROR;
- }
+ return MPATH_PR_FILE_ERROR;
+ }
- major = (int)MAJOR(info.st_rdev);
- minor = (int)MINOR(info.st_rdev);
+ major = major(info.st_rdev);
+ minor = minor(info.st_rdev);
condlog(4, "Device %d:%d", major, minor);
/* get WWN of the device from major:minor*/
condlog(3, "alias = %s", alias);
map_present = dm_map_present(alias);
- if (map_present && dm_type(alias, TGT_MPATH) <= 0){
+ if (map_present && !dm_is_mpath(alias)){
condlog(3, "%s: not a multipath device.", alias);
ret = MPATH_PR_DMMP_ERROR;
goto out;
curmp = vector_alloc ();
pathvec = vector_alloc ();
- if (!curmp || !pathvec){
- condlog (0, "%s: vector allocation failed.", alias);
- ret = MPATH_PR_DMMP_ERROR;
- goto out;
- }
+ if (!curmp || !pathvec){
+ condlog (0, "%s: vector allocation failed.", alias);
+ ret = MPATH_PR_DMMP_ERROR;
+ goto out;
+ }
- if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER)) {
+ if (path_discovery(pathvec, conf, DI_SYSFS | DI_CHECKER) < 0) {
ret = MPATH_PR_DMMP_ERROR;
goto out1;
}
switch(rq_servact)
{
- case MPATH_PROUT_REG_SA:
- case MPATH_PROUT_REG_IGN_SA:
- ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
- break;
- case MPATH_PROUT_RES_SA :
- case MPATH_PROUT_PREE_SA :
- case MPATH_PROUT_PREE_AB_SA :
- case MPATH_PROUT_CLEAR_SA:
- ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
- break;
- case MPATH_PROUT_REL_SA:
- ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
- break;
- default:
- ret = MPATH_PR_OTHER;
- goto out1;
+ case MPATH_PROUT_REG_SA:
+ case MPATH_PROUT_REG_IGN_SA:
+ ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ break;
+ case MPATH_PROUT_RES_SA :
+ case MPATH_PROUT_PREE_SA :
+ case MPATH_PROUT_PREE_AB_SA :
+ case MPATH_PROUT_CLEAR_SA:
+ ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ break;
+ case MPATH_PROUT_REL_SA:
+ ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ break;
+ default:
+ ret = MPATH_PR_OTHER;
+ goto out1;
}
if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
else
update_prflag(alias, "set", noisy);
} else {
- if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_CLEAR_SA) ||
+ if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_CLEAR_SA) ||
(rq_servact == MPATH_PROUT_PREE_AB_SA ))){
update_prflag(alias, "unset", noisy);
}
out:
FREE(alias);
- return ret;
+ return ret;
}
int
dm_get_map(mpp->alias, &mpp->size, params);
condlog(3, "params = %s", params);
dm_get_status(mpp->alias, status);
- condlog(3, "status = %s", status);
+ condlog(3, "status = %s", status);
disassemble_map (pathvec, params, mpp);
-
+
/*
* disassemble_map() can add new paths to pathvec.
* If not in "fast list mode", we need to fetch information
int ret;
struct prin_param * pparam = (struct prin_param *)p;
- ret = prin_do_scsi_ioctl(pparam->dev, pparam->rq_servact, pparam->resp, pparam->noisy);
- pparam->status = ret;
- pthread_exit(NULL);
+ ret = prin_do_scsi_ioctl(pparam->dev, pparam->rq_servact,
+ pparam->resp, pparam->noisy);
+ pparam->status = ret;
+ pthread_exit(NULL);
}
-int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy)
+int mpath_send_prin_activepath (char * dev, int rq_servact,
+ struct prin_resp * resp, int noisy)
{
int rc;
rc = prin_do_scsi_ioctl(dev, rq_servact, resp, noisy);
-
+
return (rc);
}
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
int rollback = 0;
- int active_pathcount=0;
+ int active_pathcount=0;
int rc;
int count=0;
int status = MPATH_PR_SUCCESS;
uint64_t sa_key = 0;
if (!mpp)
- return MPATH_PR_DMMP_ERROR;
+ return MPATH_PR_DMMP_ERROR;
active_pathcount = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
condlog (3, "THRED ID [%d] INFO]", i);
condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
- condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
- condlog (3, "rq_type=%d ", thread[i].param.rq_type);
- condlog (3, "rkey=");
- condlog (3, "paramp->sa_flags =%02x ", thread[i].param.paramp->sa_flags);
- condlog (3, "noisy=%d ", thread[i].param.noisy);
- condlog (3, "status=%d ", thread[i].param.status);
+ condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
+ condlog (3, "rq_type=%d ", thread[i].param.rq_type);
+ condlog (3, "rkey=");
+ condlog (3, "paramp->sa_flags =%02x ",
+ thread[i].param.paramp->sa_flags);
+ condlog (3, "noisy=%d ", thread[i].param.noisy);
+ condlog (3, "status=%d ", thread[i].param.status);
}
pthread_attr_t attr;
memset(&thread[i].param.paramp->sa_key, 0, 8);
thread[i].param.status = MPATH_PR_SUCCESS;
rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
- (void *)(&thread[count].param));
+ (void *)(&thread[i].param));
if (rc){
condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc);
}
}
int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy)
+ unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy)
{
int i,j, ret;
struct pathgroup *pgp = NULL;
vector_foreach_slot (mpp->pg, pgp, j){
vector_foreach_slot (pgp->paths, pp, i){
if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
- condlog (1, "%s: %s path not up. Skip", mpp->wwid, pp->dev);
+ condlog (1, "%s: %s path not up. Skip",
+ mpp->wwid, pp->dev);
continue;
}
condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
- ret = send_prout_activepath(pp->dev, rq_servact, rq_scope, rq_type,
- paramp, noisy);
+ ret = send_prout_activepath(pp->dev, rq_servact,
+ rq_scope, rq_type,
+ paramp, noisy);
return ret ;
}
}
rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(¶m));
if (rc){
condlog (3, "%s: failed to create thread %d", dev, rc);
- exit(-1);
+ return MPATH_PR_OTHER;
}
/* Free attribute and wait for the other threads */
pthread_attr_destroy(&attr);
}
int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
+ unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
{
int i, j;
int num = 0;
struct prout_param_descriptor *pamp;
struct prin_resp *pr_buff;
int length;
- struct transportid *pptr;
+ struct transportid *pptr;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
condlog (1, "%s: %s path not up.", mpp->wwid, pp->dev);
continue;
}
-
+
strncpy(thread[count].param.dev, pp->dev, FILE_NAME_SIZE);
condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn,
num = resp.prin_descriptor.prin_readresv.additional_length / 8;
if (num == 0){
condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
- return MPATH_PR_SUCCESS;
+ return MPATH_PR_SUCCESS;
}
condlog (2, "%s: Path holding reservation is not avialable.", mpp->wwid);
pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);
if (!pr_buff){
- condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid);
+ condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid);
return MPATH_PR_OTHER;
}
condlog (3, "%s: reservation key set.", mpp->wwid);
}
- mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA, rq_scope, rq_type, pamp,
- noisy);
+ status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA,
+ rq_scope, rq_type, pamp, noisy);
+
+ if (status) {
+ condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
+ goto out1;
+ }
pamp->num_transportid = 1;
pptr=pamp->trnptid_list[0];
for (i = 0; i < num; i++){
- if (mpp->reservation_key &&
+ if (mpp->reservation_key &&
memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
- mpp->reservation_key, 8)){
+ mpp->reservation_key, 8)){
/*register with tarnsport id*/
memset(pamp, 0, length);
pamp->trnptid_list[0] = pptr;
memset (pamp, 0, length);
memcpy (pamp->sa_key, mpp->reservation_key, 8);
memset (pamp->key, 0, 8);
- status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
+ status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
}
memset(ptr, 0, size);
break;
case MPATH_PRIN_RFSTAT_SA:
- size = sizeof(struct print_fulldescr_list) +
+ size = sizeof(struct print_fulldescr_list) +
sizeof(struct prin_fulldescr *)*MPATH_MX_TIDS;
ptr = malloc(size);
memset(ptr, 0, size);
if (!mpp->reservation_key)
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
- condlog(3, "%s: reservation_key not set in multiapth.conf", mpp->alias);
+ condlog(3, "%s: reservation_key not set in multipath.conf", mpp->alias);
return MPATH_PR_SUCCESS;
}
#include <sys/poll.h>
#include <errno.h>
#include <debug.h>
+#include <mpath_cmd.h>
+#include <uxsock.h>
#include "memory.h"
-#include "../libmultipath/uxsock.h"
-#include "../libmultipath/defaults.h"
unsigned long mem_allocated; /* Total memory used in Bytes */
int fd;
char str[64];
char *reply;
- size_t len;
int ret = 0;
- fd = ux_socket_connect(DEFAULT_SOCKET);
+ fd = mpath_connect();
if (fd == -1) {
condlog (0, "ux socket connect error");
return 1 ;
snprintf(str,sizeof(str),"map %s %s", arg1, arg2);
condlog (2, "%s: pr flag message=%s", arg1, str);
- send_packet(fd, str, strlen(str) + 1);
- recv_packet(fd, &reply, &len);
-
- condlog (2, "%s: message=%s reply=%s", arg1, str, reply);
- if (!reply || strncmp(reply,"ok", 2) == 0)
- ret = -1;
- else if (strncmp(reply, "fail", 4) == 0)
+ send_packet(fd, str);
+ ret = recv_packet(fd, &reply, DEFAULT_REPLY_TIMEOUT);
+ if (ret < 0) {
+ condlog(2, "%s: message=%s error=%d", arg1, str, errno);
ret = -2;
- else{
- ret = atoi(reply);
+ } else {
+ condlog (2, "%s: message=%s reply=%s", arg1, str, reply);
+ if (!reply || strncmp(reply,"ok", 2) == 0)
+ ret = -1;
+ else if (strncmp(reply, "fail", 4) == 0)
+ ret = -2;
+ else{
+ ret = atoi(reply);
+ }
}
free(reply);
+ mpath_disconnect(fd);
return ret;
}
int update_prflag(char * arg1, char * arg2, int noisy);
void * mpath_alloc_prin_response(int prin_sa);
int update_map_pr(struct multipath *mpp);
-int devt2devname (char *devname, char *devt);
-#endif
+#endif
SONAME=0
DEVLIB = libmultipath.so
LIBS = $(DEVLIB).$(SONAME)
-LIBDEPS = -lpthread -ldl -ldevmapper -ludev
+CFLAGS += -I$(mpathcmddir)
+LIBDEPS = -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd
ifdef SYSTEMD
- LIBDEPS += -lsystemd-daemon
+ ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1)
+ LIBDEPS += -lsystemd
+ else
+ LIBDEPS += -lsystemd-daemon
+ endif
endif
OBJS = memory.o parser.o vector.o devmapper.o callout.o \
CFLAGS += -DLIBDM_API_COOKIE
endif
-LIBUDEV_API_RECVBUF = $(shell grep -Ecs '^[a-z]*[[:space:]]+udev_monitor_set_resolve_buffer_size' /usr/include/libudev.h)
+LIBUDEV_API_RECVBUF = $(shell grep -Ecs '^[a-z]*[[:space:]]+udev_monitor_set_receive_buffer_size' /usr/include/libudev.h)
ifneq ($(strip $(LIBUDEV_API_RECVBUF)),0)
CFLAGS += -DLIBUDEV_API_RECVBUF
CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
endif
+LIBDM_API_DEFERRED = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_deferred_remove' /usr/include/libdevmapper.h)
+
+ifneq ($(strip $(LIBDM_API_DEFERRED)),0)
+ CFLAGS += -DLIBDM_API_DEFERRED
+endif
+
all: $(LIBS)
$(LIBS): $(OBJS)
return -1;
if (strlen(alias) == strlen(prefix))
- return -1;
+ return -1;
if (strlen(alias) > strlen(prefix) + 7)
/* id of 'aaaaaaaa' overflows int */
}
static int
-rlookup_binding(FILE *f, char *buff, char *map_alias)
+rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix)
{
char line[LINE_MAX];
unsigned int line_nr = 0;
- int id = 0;
buff[0] = '\0';
while (fgets(line, LINE_MAX, f)) {
char *c, *alias, *wwid;
- int curr_id;
line_nr++;
c = strpbrk(line, "#\n\r");
alias = strtok(line, " \t");
if (!alias) /* blank line */
continue;
- curr_id = scan_devname(alias, NULL); /* TBD: Why this call? */
- if (curr_id >= id)
- id = curr_id + 1;
wwid = strtok(NULL, " \t");
if (!wwid){
condlog(3,
"\nSetting wwid to %s", alias, wwid);
strncpy(buff, wwid, WWID_SIZE);
buff[WWID_SIZE - 1] = '\0';
- return id;
+ return 0;
}
}
condlog(3, "No matching alias [%s] in bindings file.", map_alias);
- return id;
+
+ return -1;
}
static char *
return alias;
}
+char *
+use_existing_alias (char *wwid, char *file, char *alias_old,
+ char *prefix, int bindings_read_only)
+{
+ char *alias = NULL;
+ int id = 0;
+ int fd, can_write;
+ char buff[WWID_SIZE];
+ FILE *f;
+
+ fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
+ if (fd < 0)
+ return NULL;
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ condlog(0, "cannot fdopen on bindings file descriptor");
+ close(fd);
+ return NULL;
+ }
+ /* lookup the binding. if it exsists, the wwid will be in buff
+ * either way, id contains the id for the alias
+ */
+ rlookup_binding(f, buff, alias_old, prefix);
+
+ if (strlen(buff) > 0) {
+ /* if buff is our wwid, it's already
+ * allocated correctly
+ */
+ if (strcmp(buff, wwid) == 0)
+ alias = STRDUP(alias_old);
+ else {
+ alias = NULL;
+ condlog(0, "alias %s already bound to wwid %s, cannot reuse",
+ alias_old, buff);
+ }
+ goto out;
+ }
+
+ /* allocate the existing alias in the bindings file */
+ id = scan_devname(alias_old, prefix);
+ if (id <= 0)
+ goto out;
+
+ if (fflush(f) != 0) {
+ condlog(0, "cannot fflush bindings file stream : %s",
+ strerror(errno));
+ goto out;
+ }
+
+ if (can_write && !bindings_read_only) {
+ alias = allocate_binding(fd, wwid, id, prefix);
+ condlog(0, "Allocated existing binding [%s] for WWID [%s]",
+ alias, wwid);
+ }
+
+out:
+ fclose(f);
+ return alias;
+}
+
char *
get_user_friendly_alias(char *wwid, char *file, char *prefix,
int bindings_read_only)
if (fflush(f) != 0) {
condlog(0, "cannot fflush bindings file stream : %s",
strerror(errno));
+ free(alias);
fclose(f);
return NULL;
}
return -1;
}
- rlookup_binding(f, buff, alias);
+ rlookup_binding(f, buff, alias, NULL);
if (!strlen(buff)) {
fclose(f);
return -1;
char *get_user_friendly_alias(char *wwid, char *file, char *prefix,
int bindings_readonly);
int get_user_friendly_wwid(char *alias, char *buff, char *file);
+char *use_existing_alias (char *wwid, char *file, char *alias_old,
+ char *prefix, int bindings_read_only);
if (regcomp(&ble->vendor_reg, vendor,
REG_EXTENDED|REG_NOSUB)) {
FREE(vendor);
+ if (product)
+ FREE(product);
return 1;
}
ble->vendor = vendor;
if (regcomp(&ble->product_reg, product,
REG_EXTENDED|REG_NOSUB)) {
FREE(product);
+ if (vendor) {
+ ble->vendor = NULL;
+ FREE(vendor);
+ }
return 1;
}
ble->product = product;
struct blentry_device * ble;
vector_foreach_slot (elist, ble, i) {
- if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) &&
- !regexec(&ble->product_reg, product, 0, NULL, 0))
+ if (!ble->vendor && !ble->product)
+ continue;
+ if ((!ble->vendor ||
+ !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
+ (!ble->product ||
+ !regexec(&ble->product_reg, product, 0, NULL, 0)))
return 1;
}
return 0;
struct blentry_device * ble;
vector_foreach_slot (blist, ble, i) {
- if (!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) &&
- !regexec(&ble->product_reg, product, 0, NULL, 0))
+ if (!ble->vendor && !ble->product)
+ continue;
+ if ((!ble->vendor ||
+ !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
+ (!ble->product ||
+ !regexec(&ble->product_reg, product, 0, NULL, 0)))
return 1;
}
return 0;
if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
return 1;
- str = STRDUP("^(td|hd)[a-z]");
+ str = STRDUP("^(td|hd|vd)[a-z]");
if (!str)
return 1;
if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
return 1;
- str = STRDUP("(ID_SCSI_VPD|ID_WWN)");
+ str = STRDUP("^nvme.*");
+ if (!str)
+ return 1;
+ if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
+ return 1;
+
+ str = STRDUP("(SCSI_IDENT_.*|ID_WWN)");
if (!str)
return 1;
if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT))
STRDUP(hwe->bl_product),
ORIGIN_DEFAULT)) {
FREE(ble);
+ vector_del_slot(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1);
return 1;
}
}
if (vendor && product) \
condlog(3, "%s: (%s:%s) %s %s", \
dev, vendor, product, (M), (S)); \
+ else if (wwid && !dev) \
+ condlog(3, "%s: %s %s", wwid, (M), (S)); \
else if (wwid) \
condlog(3, "%s: %s %s %s", dev, (M), wwid, (S)); \
else if (env) \
}
int
-filter_wwid (vector blist, vector elist, char * wwid)
+filter_wwid (vector blist, vector elist, char * wwid, char * dev)
{
int r = _filter_wwid(blist, elist, wwid);
- log_filter(NULL, NULL, NULL, wwid, NULL, r);
+ log_filter(dev, NULL, NULL, wwid, NULL, r);
return r;
}
int setup_default_blist (struct config *);
int alloc_ble_device (vector);
int filter_devnode (vector, vector, char *);
-int filter_wwid (vector, vector, char *);
+int filter_wwid (vector, vector, char *, char *);
int filter_device (vector, vector, char *, char *);
int filter_path (struct config *, struct path *);
int filter_property(struct config *, struct udev_device *);
if (status == 0)
retval = 0;
else
- condlog(0, "%s exitted with %d", argv[0], status);
+ condlog(0, "%s exited with %d", argv[0], status);
}
else if (WIFSIGNALED(status))
condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
"pending",
"timeout",
"removed",
+ "delayed",
};
static LIST_HEAD(checkers);
{
struct checker * src;
- if (!dst)
+ if (!dst || !dst->check)
return;
src = checker_lookup(dst->name);
if (dst->free)
* PATH REMOVED:
* - Use: All checkers
* - Description: Device has been removed from the system
+ *
+ * PATH_DELAYED:
+ * - Use: None of the checkers (returned if the path is being delayed before
+ * reintegration.
+ * - Description: If a path fails after being up for less than
+ * delay_watch_checks checks, when it comes back up again, it will not
+ * be marked as up until it has been up for delay_wait_checks checks.
+ * During this time, it is marked as "delayed"
*/
enum path_check_state {
PATH_WILD,
PATH_PENDING,
PATH_TIMEOUT,
PATH_REMOVED,
+ PATH_DELAYED,
PATH_MAX_STATE
};
done:
switch (ret) {
case PATH_DOWN:
- MSG(c, (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") :
+ MSG(c, "%s", (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") :
checker_msg_string(&inq));
break;
case PATH_UP:
#include <stdio.h>
#include <string.h>
#include <libudev.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
#include "checkers.h"
#include "memory.h"
#include "defaults.h"
#include "prio.h"
#include "devmapper.h"
+#include "mpath_cmd.h"
static int
hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2)
merge_num(user_friendly_names);
merge_num(retain_hwhandler);
merge_num(detect_prio);
+ merge_num(deferred_remove);
+ merge_num(delay_watch_checks);
+ merge_num(delay_wait_checks);
/*
* Make sure features is consistent with
hwe->user_friendly_names = dhwe->user_friendly_names;
hwe->retain_hwhandler = dhwe->retain_hwhandler;
hwe->detect_prio = dhwe->detect_prio;
+ conf->deferred_remove = DEFAULT_DEFERRED_REMOVE;
if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product)))
goto out;
if (conf->wwids_file)
FREE(conf->wwids_file);
+
if (conf->prio_name)
FREE(conf->prio_name);
if (conf->alias_prefix)
FREE(conf->alias_prefix);
+ if (conf->partition_delim)
+ FREE(conf->partition_delim);
if (conf->prio_args)
FREE(conf->prio_args);
if (conf->checker_name)
FREE(conf->checker_name);
+
+ if (conf->config_dir)
+ FREE(conf->config_dir);
+
if (conf->reservation_key)
FREE(conf->reservation_key);
free_mptable(conf->mptable);
free_hwtable(conf->hwtable);
+ free_hwe(conf->overrides);
free_keywords(conf->keywords);
FREE(conf);
}
+/* if multipath fails to process the config directory, it should continue,
+ * with just a warning message */
+static void
+process_config_dir(vector keywords, char *dir)
+{
+ struct dirent **namelist;
+ int i, n;
+ char path[LINE_MAX];
+ int old_hwtable_size;
+
+ if (dir[0] != '/') {
+ condlog(1, "config_dir '%s' must be a fully qualified path",
+ dir);
+ return;
+ }
+ n = scandir(dir, &namelist, NULL, alphasort);
+ if (n < 0) {
+ if (errno == ENOENT)
+ condlog(3, "No configuration dir '%s'", dir);
+ else
+ condlog(0, "couldn't open configuration dir '%s': %s",
+ dir, strerror(errno));
+ return;
+ }
+ for (i = 0; i < n; i++) {
+ if (!strstr(namelist[i]->d_name, ".conf"))
+ continue;
+ old_hwtable_size = VECTOR_SIZE(conf->hwtable);
+ snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name);
+ path[LINE_MAX-1] = '\0';
+ process_file(path);
+ if (VECTOR_SIZE(conf->hwtable) > old_hwtable_size)
+ factorize_hwtable(conf->hwtable, old_hwtable_size);
+
+ }
+}
+
int
load_config (char * file, struct udev *udev)
{
get_sys_max_fds(&conf->max_fds);
conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
conf->wwids_file = set_default(DEFAULT_WWIDS_FILE);
- conf->bindings_read_only = 0;
conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR);
conf->features = set_default(DEFAULT_FEATURES);
conf->flush_on_last_del = 0;
conf->attribute_flags = 0;
conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
conf->checkint = DEFAULT_CHECKINT;
- conf->max_checkint = MAX_CHECKINT(conf->checkint);
+ conf->max_checkint = 0;
conf->pgfailback = DEFAULT_FAILBACK;
conf->fast_io_fail = DEFAULT_FAST_IO_FAIL;
conf->retain_hwhandler = DEFAULT_RETAIN_HWHANDLER;
conf->detect_prio = DEFAULT_DETECT_PRIO;
+ conf->force_sync = 0;
+ conf->partition_delim = NULL;
+ conf->processed_main_config = 0;
+ conf->find_multipaths = DEFAULT_FIND_MULTIPATHS;
+ conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT;
+ conf->uid_attribute = set_default(DEFAULT_UID_ATTRIBUTE);
+ conf->retrigger_tries = DEFAULT_RETRIGGER_TRIES;
+ conf->retrigger_delay = DEFAULT_RETRIGGER_DELAY;
+ conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT;
/*
* preload default hwtable
*/
set_current_keywords(&conf->keywords);
alloc_keywords();
+ init_keywords();
if (filepresent(file)) {
int builtin_hwtable_size;
builtin_hwtable_size = VECTOR_SIZE(conf->hwtable);
- if (init_data(file, init_keywords)) {
+ if (process_file(file)) {
condlog(0, "error parsing config file");
goto out;
}
factorize_hwtable(conf->hwtable, builtin_hwtable_size);
}
- } else {
- init_keywords();
}
+ conf->processed_main_config = 1;
+ if (conf->config_dir == NULL)
+ conf->config_dir = set_default(DEFAULT_CONFIG_DIR);
+ if (conf->config_dir && conf->config_dir[0] != '\0')
+ process_config_dir(conf->keywords, conf->config_dir);
+
/*
* fill the voids left in the config file
*/
+ if (conf->max_checkint == 0)
+ conf->max_checkint = MAX_CHECKINT(conf->checkint);
if (conf->blist_devnode == NULL) {
conf->blist_devnode = vector_alloc();
DEV_NONE,
DEV_DEVT,
DEV_DEVNODE,
- DEV_DEVMAP
+ DEV_DEVMAP,
+ DEV_UEVENT
+};
+
+enum mpath_cmds {
+ CMD_CREATE,
+ CMD_DRY_RUN,
+ CMD_LIST_SHORT,
+ CMD_LIST_LONG,
+ CMD_VALID_PATH,
+ CMD_REMOVE_WWID,
+ CMD_RESET_WWIDS,
+ CMD_ADD_WWID,
};
struct hwentry {
int user_friendly_names;
int retain_hwhandler;
int detect_prio;
+ int deferred_remove;
+ int delay_watch_checks;
+ int delay_wait_checks;
char * bl_product;
};
int flush_on_last_del;
int attribute_flags;
int user_friendly_names;
+ int deferred_remove;
+ int delay_watch_checks;
+ int delay_wait_checks;
uid_t uid;
gid_t gid;
mode_t mode;
struct config {
int verbosity;
- int dry_run;
- int list;
+ enum mpath_cmds cmd;
int pgpolicy_flag;
int pgpolicy;
enum devtypes dev_type;
int max_fds;
int force_reload;
int queue_without_daemon;
+ int ignore_wwids;
int checker_timeout;
int daemon;
-#ifdef USE_SYSTEMD
- int watchdog;
-#endif
int flush_on_last_del;
int attribute_flags;
int fast_io_fail;
unsigned int dev_loss;
int log_checker_err;
int allow_queueing;
+ int find_multipaths;
uid_t uid;
gid_t gid;
mode_t mode;
- uint32_t cookie;
int reassign_maps;
int retain_hwhandler;
int detect_prio;
+ int force_sync;
+ int deferred_remove;
+ int processed_main_config;
+ int delay_watch_checks;
+ int delay_wait_checks;
+ int uxsock_timeout;
+ int retrigger_tries;
+ int retrigger_delay;
+ int ignore_new_devs;
+ int delayed_reconfig;
+ int uev_wait_timeout;
unsigned int version[3];
char * dev;
char * prio_args;
char * checker_name;
char * alias_prefix;
+ char * partition_delim;
+ char * config_dir;
unsigned char * reservation_key;
vector keywords;
vector mptable;
vector hwtable;
+ struct hwentry *overrides;
vector blist_devnode;
vector blist_wwid;
#include <errno.h>
#include <libdevmapper.h>
#include <libudev.h>
+#include <mpath_cmd.h>
#include "checkers.h"
#include "vector.h"
#include "uxsock.h"
#include "wwids.h"
+/* group paths in pg by host adapter
+ */
+int group_by_host_adapter(struct pathgroup *pgp, vector adapters)
+{
+ struct adapter_group *agp;
+ struct host_group *hgp;
+ struct path *pp, *pp1;
+ char adapter_name1[SLOT_NAME_SIZE];
+ char adapter_name2[SLOT_NAME_SIZE];
+ int i, j;
+ int found_hostgroup = 0;
+
+ while (VECTOR_SIZE(pgp->paths) > 0) {
+
+ pp = VECTOR_SLOT(pgp->paths, 0);
+
+ if (sysfs_get_host_adapter_name(pp, adapter_name1))
+ goto out;
+ /* create a new host adapter group
+ */
+ agp = alloc_adaptergroup();
+ if (!agp)
+ goto out;
+ agp->pgp = pgp;
+
+ strncpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE);
+ store_adaptergroup(adapters, agp);
+
+ /* create a new host port group
+ */
+ hgp = alloc_hostgroup();
+ if (!hgp)
+ goto out;
+ if (store_hostgroup(agp->host_groups, hgp))
+ goto out;
+
+ hgp->host_no = pp->sg_id.host_no;
+ agp->num_hosts++;
+ if (store_path(hgp->paths, pp))
+ goto out;
+
+ hgp->num_paths++;
+ /* delete path from path group
+ */
+ vector_del_slot(pgp->paths, 0);
+
+ /* add all paths belonging to same host adapter
+ */
+ vector_foreach_slot(pgp->paths, pp1, i) {
+ if (sysfs_get_host_adapter_name(pp1, adapter_name2))
+ goto out;
+ if (strcmp(adapter_name1, adapter_name2) == 0) {
+ found_hostgroup = 0;
+ vector_foreach_slot(agp->host_groups, hgp, j) {
+ if (hgp->host_no == pp1->sg_id.host_no) {
+ if (store_path(hgp->paths, pp1))
+ goto out;
+ hgp->num_paths++;
+ found_hostgroup = 1;
+ break;
+ }
+ }
+ if (!found_hostgroup) {
+ /* this path belongs to new host port
+ * within this adapter
+ */
+ hgp = alloc_hostgroup();
+ if (!hgp)
+ goto out;
+
+ if (store_hostgroup(agp->host_groups, hgp))
+ goto out;
+
+ agp->num_hosts++;
+ if (store_path(hgp->paths, pp1))
+ goto out;
+
+ hgp->host_no = pp1->sg_id.host_no;
+ hgp->num_paths++;
+ }
+ /* delete paths from original path_group
+ * as they are added into adapter group now
+ */
+ vector_del_slot(pgp->paths, i);
+ i--;
+ }
+ }
+ }
+ return 0;
+
+out: /* add back paths into pg as re-ordering failed
+ */
+ vector_foreach_slot(adapters, agp, i) {
+ vector_foreach_slot(agp->host_groups, hgp, j) {
+ while (VECTOR_SIZE(hgp->paths) > 0) {
+ pp = VECTOR_SLOT(hgp->paths, 0);
+ if (store_path(pgp->paths, pp))
+ condlog(3, "failed to restore "
+ "path %s into path group",
+ pp->dev);
+ vector_del_slot(hgp->paths, 0);
+ }
+ }
+ }
+ free_adaptergroup(adapters);
+ return 1;
+}
+
+/* re-order paths in pg by alternating adapters and host ports
+ * for optimized selection
+ */
+int order_paths_in_pg_by_alt_adapters(struct pathgroup *pgp, vector adapters,
+ int total_paths)
+{
+ int next_adapter_index = 0;
+ struct adapter_group *agp;
+ struct host_group *hgp;
+ struct path *pp;
+
+ while (total_paths > 0) {
+ agp = VECTOR_SLOT(adapters, next_adapter_index);
+ if (!agp) {
+ condlog(0, "can't get adapter group %d", next_adapter_index);
+ return 1;
+ }
+
+ hgp = VECTOR_SLOT(agp->host_groups, agp->next_host_index);
+ if (!hgp) {
+ condlog(0, "can't get host group %d of adapter group %d", next_adapter_index, agp->next_host_index);
+ return 1;
+ }
+
+ if (!hgp->num_paths) {
+ agp->next_host_index++;
+ agp->next_host_index %= agp->num_hosts;
+ next_adapter_index++;
+ next_adapter_index %= VECTOR_SIZE(adapters);
+ continue;
+ }
+
+ pp = VECTOR_SLOT(hgp->paths, 0);
+
+ if (store_path(pgp->paths, pp))
+ return 1;
+
+ total_paths--;
+
+ vector_del_slot(hgp->paths, 0);
+
+ hgp->num_paths--;
+
+ agp->next_host_index++;
+ agp->next_host_index %= agp->num_hosts;
+ next_adapter_index++;
+ next_adapter_index %= VECTOR_SIZE(adapters);
+ }
+
+ /* all paths are added into path_group
+ * in crafted child order
+ */
+ return 0;
+}
+
+/* round-robin: order paths in path group to alternate
+ * between all host adapters
+ */
+int rr_optimize_path_order(struct pathgroup *pgp)
+{
+ vector adapters;
+ struct path *pp;
+ int total_paths;
+ int i;
+
+ total_paths = VECTOR_SIZE(pgp->paths);
+ vector_foreach_slot(pgp->paths, pp, i) {
+ if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP &&
+ pp->sg_id.proto_id != SCSI_PROTOCOL_SAS &&
+ pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI &&
+ pp->sg_id.proto_id != SCSI_PROTOCOL_SRP) {
+ /* return success as default path order
+ * is maintained in path group
+ */
+ return 0;
+ }
+ }
+ adapters = vector_alloc();
+ if (!adapters)
+ return 0;
+
+ /* group paths in path group by host adapters
+ */
+ if (group_by_host_adapter(pgp, adapters)) {
+ /* already freed adapters */
+ condlog(3, "Failed to group paths by adapters");
+ return 0;
+ }
+
+ /* re-order paths in pg to alternate between adapters and host ports
+ */
+ if (order_paths_in_pg_by_alt_adapters(pgp, adapters, total_paths)) {
+ condlog(3, "Failed to re-order paths in pg by adapters "
+ "and host ports");
+ free_adaptergroup(adapters);
+ /* return failure as original paths are
+ * removed form pgp
+ */
+ return 1;
+ }
+
+ free_adaptergroup(adapters);
+ return 0;
+}
+
extern int
setup_map (struct multipath * mpp, char * params, int params_size)
{
select_dev_loss(mpp);
select_reservation_key(mpp);
select_retain_hwhandler(mpp);
+ select_deferred_remove(mpp);
+ select_delay_watch_checks(mpp);
+ select_delay_wait_checks(mpp);
sysfs_set_scsi_tmo(mpp);
/*
*/
mpp->bestpg = select_path_group(mpp);
+ /* re-order paths in all path groups in an optimized way
+ * for round-robin path selectors to get maximum throughput.
+ */
+ if (!strncmp(mpp->selector, "round-robin", 11)) {
+ vector_foreach_slot(mpp->pg, pgp, i) {
+ if (VECTOR_SIZE(pgp->paths) <= 2)
+ continue;
+ if (rr_optimize_path_order(pgp)) {
+ condlog(2, "cannot re-order paths for "
+ "optimization: %s",
+ mpp->alias);
+ return 1;
+ }
+ }
+ }
+
/*
* transform the mp->pg vector of vectors of paths
* into a mp->params strings to feed the device-mapper
cmpp->alias, mpp->alias);
strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE);
mpp->action = ACT_RENAME;
+ if (force_reload)
+ mpp->action = ACT_RENAME2;
return;
}
mpp->action = ACT_CREATE;
condlog(2, "%s: unable to rename %s to %s (%s is used by %s)",
mpp->wwid, cmpp->alias, mpp->alias,
mpp->alias, cmpp_by_name->wwid);
+ /* reset alias to existing alias */
+ FREE(mpp->alias);
+ mpp->alias = STRDUP(cmpp->alias);
mpp->action = ACT_NOTHING;
return;
}
extern int
domap (struct multipath * mpp, char * params)
{
- int r = 0;
+ int r = DOMAP_FAIL;
/*
* last chance to quit before touching the devmaps
*/
- if (conf->dry_run && mpp->action != ACT_NOTHING) {
+ if (conf->cmd == CMD_DRY_RUN && mpp->action != ACT_NOTHING) {
print_multipath_topology(mpp, conf->verbosity);
return DOMAP_DRY;
}
+ if (mpp->action == ACT_CREATE &&
+ dm_map_present(mpp->alias)) {
+ condlog(3, "%s: map already present", mpp->alias);
+ mpp->action = ACT_RELOAD;
+ }
+
switch (mpp->action) {
case ACT_REJECT:
case ACT_NOTHING:
return DOMAP_RETRY;
}
- if (dm_map_present(mpp->alias)) {
- condlog(3, "%s: map already present", mpp->alias);
- lock_multipath(mpp, 0);
- break;
- }
-
r = dm_addmap_create(mpp, params);
lock_multipath(mpp, 0);
case ACT_RELOAD:
r = dm_addmap_reload(mpp, params);
if (r)
- r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias);
+ r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias,
+ 0, MPATH_UDEV_RELOAD_FLAG);
break;
case ACT_RESIZE:
r = dm_addmap_reload(mpp, params);
if (r)
- r = dm_simplecmd_flush(DM_DEVICE_RESUME, mpp->alias, 1);
+ r = dm_simplecmd_flush(DM_DEVICE_RESUME, mpp->alias, 1, 0);
break;
case ACT_RENAME:
r = dm_rename(mpp->alias_old, mpp->alias);
break;
+ case ACT_RENAME2:
+ r = dm_rename(mpp->alias_old, mpp->alias);
+ if (r) {
+ r = dm_addmap_reload(mpp, params);
+ if (r)
+ r = dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, 0, MPATH_UDEV_RELOAD_FLAG);
+ }
+ break;
+
default:
break;
}
- if (r) {
+ if (r == DOMAP_OK) {
/*
* DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD
* succeeded
*/
if (mpp->action != ACT_CREATE)
mpp->action = ACT_NOTHING;
+ else {
+ mpp->wait_for_udev = 1;
+ mpp->uev_wait_tick = conf->uev_wait_timeout;
+ }
}
dm_setgeometry(mpp);
return DOMAP_OK;
{
int fd;
char *reply;
- size_t len;
int ret = 0;
- fd = ux_socket_connect(DEFAULT_SOCKET);
+ fd = mpath_connect();
if (fd == -1)
return 0;
- if (send_packet(fd, "show daemon", 12) != 0)
+ if (send_packet(fd, "show daemon") != 0)
goto out;
- if (recv_packet(fd, &reply, &len) != 0)
+ if (recv_packet(fd, &reply, conf->uxsock_timeout) != 0)
goto out;
if (strstr(reply, "shutdown"))
out_free:
FREE(reply);
out:
- close(fd);
+ mpath_disconnect(fd);
return ret;
}
{
int r = 1;
int k, i;
- char empty_buff[WWID_SIZE];
char params[PARAMS_SIZE];
struct multipath * mpp;
struct path * pp1;
vector curmp = vecs->mpvec;
vector pathvec = vecs->pathvec;
- memset(empty_buff, 0, WWID_SIZE);
+ /* ignore refwwid if it's empty */
+ if (refwwid && !strlen(refwwid))
+ refwwid = NULL;
if (force_reload) {
vector_foreach_slot (pathvec, pp1, k) {
/* skip this path for some reason */
/* 1. if path has no unique id or wwid blacklisted */
- if (memcmp(empty_buff, pp1->wwid, WWID_SIZE) == 0 ||
+ if (strlen(pp1->wwid) == 0 ||
filter_path(conf, pp1) > 0) {
orphan_path(pp1, "wwid blacklisted");
continue;
continue;
/* 3. if path has disappeared */
- if (!pp1->size) {
- orphan_path(pp1, "invalid size");
+ if (pp1->state == PATH_REMOVED) {
+ orphan_path(pp1, "path removed");
continue;
}
if (refwwid && strncmp(pp1->wwid, refwwid, WWID_SIZE))
continue;
+ /* If find_multipaths was selected check if the path is valid */
+ if (!refwwid && !should_multipath(pp1, pathvec)) {
+ orphan_path(pp1, "only one path");
+ continue;
+ }
+
/*
* at this point, we know we really got a new mp
*/
if (strcmp(pp1->wwid, pp2->wwid))
continue;
- if (!pp2->size)
- continue;
+ if (!mpp->size && pp2->size)
+ mpp->size = pp2->size;
- if (pp2->size != mpp->size) {
+ if (mpp->size && pp2->size &&
+ pp2->size != mpp->size) {
/*
* ouch, avoid feeding that to the DM
*/
if (pp2->priority == PRIO_UNDEF)
mpp->action = ACT_REJECT;
}
- verify_paths(mpp, vecs, NULL);
+ verify_paths(mpp, vecs);
params[0] = '\0';
if (setup_map(mpp, params, PARAMS_SIZE)) {
udev_device_unref(udevice);
if (!pp) {
if (ret == 1)
- condlog(0, "%s can't store path info",
- buff);
+ condlog(0, "%s: can't store path info",
+ dev);
return ret;
}
}
+ if (pp->udev && filter_property(conf, pp->udev) > 0)
+ return 2;
+
refwwid = pp->wwid;
goto out;
}
return ret;
}
}
+ if (pp->udev && filter_property(conf, pp->udev) > 0)
+ return 2;
+
+ refwwid = pp->wwid;
+ goto out;
+ }
+
+ if (dev_type == DEV_UEVENT) {
+ struct udev_device *udevice = udev_device_new_from_environment(conf->udev);
+
+ if (!udevice) {
+ condlog(2, "%s: can't get udev device", dev);
+ return 1;
+ }
+ ret = store_pathinfo(pathvec, conf->hwtable, udevice,
+ DI_SYSFS | DI_WWID, &pp);
+ udev_device_unref(udevice);
+ if (!pp) {
+ if (ret == 1)
+ condlog(0, "%s: can't store path info",
+ dev);
+ return ret;
+ }
+ if (pp->udev && filter_property(conf, pp->udev) > 0)
+ return 2;
+
refwwid = pp->wwid;
goto out;
}
+
if (dev_type == DEV_DEVMAP) {
if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) {
check:
if (refwwid && strlen(refwwid)) {
if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
- refwwid) > 0)
+ refwwid, NULL) > 0)
return 2;
}
}
ACT_RENAME,
ACT_CREATE,
ACT_RESIZE,
+ ACT_RENAME2,
};
#define FLUSH_ONE 1
int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload);
int get_refwwid (char * dev, enum devtypes dev_type, vector pathvec, char **wwid);
int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh);
-
+int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name);
#define DEFAULT_FAILBACK -FAILBACK_MANUAL
#define DEFAULT_RR_WEIGHT RR_WEIGHT_NONE
#define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF
-#define DEFAULT_PGTIMEOUT -PGTIMEOUT_NONE
-#define DEFAULT_USER_FRIENDLY_NAMES 0
#define DEFAULT_VERBOSITY 2
-#define DEFAULT_REASSIGN_MAPS 1
+#define DEFAULT_REASSIGN_MAPS 0
+#define DEFAULT_FIND_MULTIPATHS 0
#define DEFAULT_FAST_IO_FAIL 5
#define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF
#define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF
+#define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF
+#define DEFAULT_DELAY_CHECKS DELAY_CHECKS_OFF
+#define DEFAULT_UEVENT_STACKSIZE 256
+#define DEFAULT_RETRIGGER_DELAY 10
+#define DEFAULT_RETRIGGER_TRIES 3
+#define DEFAULT_UEV_WAIT_TIMEOUT 30
#define DEFAULT_CHECKINT 5
#define MAX_CHECKINT(a) (a << 2)
#define MAX_DEV_LOSS_TMO 0x7FFFFFFF
-#define DEFAULT_PIDFILE "/var/run/multipathd.pid"
+#define DEFAULT_PIDFILE "/" RUN_DIR "/multipathd.pid"
#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
#define DEFAULT_CONFIGFILE "/etc/multipath.conf"
#define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings"
#define DEFAULT_WWIDS_FILE "/etc/multipath/wwids"
+#define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d"
char * set_default (char * str);
#define UUID_PREFIX "mpath-"
#define UUID_PREFIX_LEN 6
+#ifdef LIBDM_API_DEFERRED
+static int dm_cancel_remove_partmaps(const char * mapname);
+#endif
+
+static int do_foreach_partmaps(const char * mapname,
+ int (*partmap_func)(const char *, void *),
+ void *data);
+
#ifndef LIBDM_API_COOKIE
static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a)
{
return 1;
}
-void udev_wait(unsigned int c)
+void dm_udev_wait(unsigned int c)
{
}
-void udev_set_sync_support(int c)
-{
-}
-#else
-void udev_wait(unsigned int c)
+void dm_udev_set_sync_support(int c)
{
- dm_udev_wait(c);
}
-void udev_set_sync_support(int c)
-{
- dm_udev_set_sync_support(c);
-}
#endif
static void
{
char version[64];
int v[3];
-#ifdef LIBDM_API_COOKIE
+#if defined(LIBDM_API_DEFERRED)
+ int minv[3] = {1, 2, 89};
+#elif defined(DM_SUBSYSTEM_UDEV_FLAG0)
+ int minv[3] = {1, 2, 82};
+#elif defined(LIBDM_API_COOKIE)
int minv[3] = {1, 2, 38};
#else
int minv[3] = {1, 2, 8};
return dm_drv_prereq();
}
+#define do_deferred(x) ((x) == DEFERRED_REMOVE_ON || (x) == DEFERRED_REMOVE_IN_PROGRESS)
+
static int
-dm_simplecmd (int task, const char *name, int no_flush, int need_sync) {
+dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t udev_flags, int deferred_remove) {
int r = 0;
int udev_wait_flag = (need_sync && (task == DM_DEVICE_RESUME ||
task == DM_DEVICE_REMOVE));
+ uint32_t cookie = 0;
struct dm_task *dmt;
if (!(dmt = dm_task_create (task)))
if (no_flush)
dm_task_no_flush(dmt); /* for DM_DEVICE_SUSPEND/RESUME */
#endif
-
- if (udev_wait_flag && !dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0))
+#ifdef LIBDM_API_DEFERRED
+ if (do_deferred(deferred_remove))
+ dm_task_deferred_remove(dmt);
+#endif
+ if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, ((conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0) | udev_flags)) {
+ dm_udev_complete(cookie);
goto out;
+ }
r = dm_task_run (dmt);
+ if (udev_wait_flag) {
+ if (!r)
+ dm_udev_complete(cookie);
+ else
+ dm_udev_wait(cookie);
+ }
out:
dm_task_destroy (dmt);
return r;
}
extern int
-dm_simplecmd_flush (int task, const char *name, int needsync) {
- return dm_simplecmd(task, name, 0, needsync);
+dm_simplecmd_flush (int task, const char *name, int needsync, uint16_t udev_flags) {
+ return dm_simplecmd(task, name, 0, needsync, udev_flags, 0);
}
extern int
-dm_simplecmd_noflush (int task, const char *name) {
- return dm_simplecmd(task, name, 1, 1);
+dm_simplecmd_noflush (int task, const char *name, int needsync, uint16_t udev_flags) {
+ return dm_simplecmd(task, name, 1, needsync, udev_flags, 0);
+}
+
+static int
+dm_device_remove (const char *name, int needsync, int deferred_remove) {
+ return dm_simplecmd(DM_DEVICE_REMOVE, name, 0, needsync, 0,
+ deferred_remove);
}
extern int
int r = 0;
struct dm_task *dmt;
char *prefixed_uuid = NULL;
+ uint32_t cookie = 0;
if (!(dmt = dm_task_create (task)))
return 0;
dm_task_no_open_count(dmt);
if (task == DM_DEVICE_CREATE &&
- !dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0))
+ !dm_task_set_cookie(dmt, &cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0)) {
+ dm_udev_complete(cookie);
goto freeout;
+ }
r = dm_task_run (dmt);
+ if (task == DM_DEVICE_CREATE) {
+ if (!r)
+ dm_udev_complete(cookie);
+ else
+ dm_udev_wait(cookie);
+ }
freeout:
if (prefixed_uuid)
FREE(prefixed_uuid);
for (ro = 0; ro <= 1; ro++) {
int err;
- if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, 1, ro))
+ if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH,
+ mpp, params, 1, ro))
return 1;
/*
* DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD.
condlog(3, "%s: failed to load map (a path might be in use)", mpp->alias);
dm_flush_map_nosync(mpp->alias);
}
- if (err != EROFS)
+ if (err != EROFS) {
+ condlog(3, "%s: failed to load map, error %d",
+ mpp->alias, err);
break;
+ }
}
return 0;
}
return r;
}
+extern int
+dm_is_mpath(const char * name)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct dm_info info;
+ uint64_t start, length;
+ char *target_type = NULL;
+ char *params;
+ const char *uuid;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ dm_task_no_open_count(dmt);
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ if (!dm_task_get_info(dmt, &info) || !info.exists)
+ goto out;
+
+ uuid = dm_task_get_uuid(dmt);
+
+ if (!uuid || strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN) != 0)
+ goto out;
+
+ /* Fetch 1st target */
+ dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms);
+
+ if (!target_type || strcmp(target_type, TGT_MPATH) != 0)
+ goto out;
+
+ r = 1;
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
static int
dm_dev_t (const char * mapname, char * dev_t, int len)
{
if (!dm_task_run(dmt))
goto out;
- if (!dm_task_get_info(dmt, &info))
+ if (!dm_task_get_info(dmt, &info) || !info.exists)
goto out;
- r = info.open_count;
if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len)
goto out;
if (!dm_task_get_info(dmt, &info))
goto out;
+ if (!info.exists)
+ goto out;
+
r = info.open_count;
out:
dm_task_destroy(dmt);
if (!dm_task_get_info(dmt, &info))
goto out;
+ if (!info.exists)
+ goto out;
+
r = info.major;
out:
dm_task_destroy(dmt);
if (!dm_task_get_info(dmt, &info))
goto out;
+ if (!info.exists)
+ goto out;
+
r = info.minor;
out:
dm_task_destroy(dmt);
return r;
}
+static int
+partmap_in_use(const char *name, void *data)
+{
+ int part_count, *ret_count = (int *)data;
+ int open_count = dm_get_opencount(name);
+
+ if (ret_count)
+ (*ret_count)++;
+ part_count = 0;
+ if (open_count) {
+ if (do_foreach_partmaps(name, partmap_in_use, &part_count))
+ return 1;
+ if (open_count != part_count) {
+ condlog(2, "%s: map in use", name);
+ return 1;
+ }
+ }
+ return 0;
+}
+
extern int
-_dm_flush_map (const char * mapname, int need_sync)
+_dm_flush_map (const char * mapname, int need_sync, int deferred_remove)
{
int r;
- if (!dm_map_present(mapname))
- return 0;
-
- if (dm_type(mapname, TGT_MPATH) <= 0)
+ if (!dm_is_mpath(mapname))
return 0; /* nothing to do */
- if (dm_remove_partmaps(mapname, need_sync))
+ /* If you aren't doing a deferred remove, make sure that no
+ * devices are in use */
+ if (!do_deferred(deferred_remove) && partmap_in_use(mapname, NULL))
+ return 1;
+
+ if (dm_remove_partmaps(mapname, need_sync, deferred_remove))
return 1;
- if (dm_get_opencount(mapname)) {
+ if (!do_deferred(deferred_remove) && dm_get_opencount(mapname)) {
condlog(2, "%s: map in use", mapname);
return 1;
}
- r = dm_simplecmd_flush(DM_DEVICE_REMOVE, mapname, need_sync);
+ r = dm_device_remove(mapname, need_sync, deferred_remove);
if (r) {
+ if (do_deferred(deferred_remove) && dm_map_present(mapname)) {
+ condlog(4, "multipath map %s remove deferred",
+ mapname);
+ return 2;
+ }
condlog(4, "multipath map %s removed", mapname);
return 0;
}
return 1;
}
+#ifdef LIBDM_API_DEFERRED
+
+int
+dm_flush_map_nopaths(const char * mapname, int deferred_remove)
+{
+ return _dm_flush_map(mapname, 1, deferred_remove);
+}
+
+#else
+
+int
+dm_flush_map_nopaths(const char * mapname, int deferred_remove)
+{
+ return _dm_flush_map(mapname, 1, 0);
+}
+
+#endif
+
extern int
dm_suspend_and_flush_map (const char * mapname)
{
unsigned long long mapsize;
char params[PARAMS_SIZE] = {0};
- if (!dm_map_present(mapname))
- return 0;
-
- if (dm_type(mapname, TGT_MPATH) <= 0)
+ if (!dm_is_mpath(mapname))
return 0; /* nothing to do */
if (!dm_get_map(mapname, &mapsize, params)) {
if (s)
queue_if_no_path = 0;
else
- s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0);
+ s = dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 1, 0);
if (!dm_flush_map(mapname)) {
condlog(4, "multipath map %s removed", mapname);
return 0;
}
condlog(2, "failed to remove multipath map %s", mapname);
- dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname);
+ dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, 1, 0);
if (queue_if_no_path)
s = dm_queue_if_no_path((char *)mapname, 1);
return 1;
}
int
-dm_message(char * mapname, char * message)
+dm_message(const char * mapname, char * message)
{
int r = 1;
struct dm_task *dmt;
{
struct multipath * mpp;
int r = 1;
- int info;
struct dm_task *dmt;
struct dm_names *names;
unsigned next = 0;
}
do {
- info = dm_type(names->name, TGT_MPATH);
-
- if (info <= 0)
+ if (!dm_is_mpath(names->name))
goto next;
mpp = alloc_multipath();
if (!mpp->alias)
goto out1;
- if (info > 0) {
- if (dm_get_map(names->name, &mpp->size, NULL))
- goto out1;
+ if (dm_get_map(names->name, &mpp->size, NULL))
+ goto out1;
- dm_get_uuid(names->name, mpp->wwid);
- dm_get_info(names->name, &mpp->dmi);
- }
+ dm_get_uuid(names->name, mpp->wwid);
+ dm_get_info(names->name, &mpp->dmi);
if (!vector_alloc_slot(mp))
goto out1;
return NULL;
}
-int
-dm_remove_partmaps (const char * mapname, int need_sync)
+static int
+do_foreach_partmaps (const char * mapname,
+ int (*partmap_func)(const char *, void *),
+ void *data)
{
struct dm_task *dmt;
struct dm_names *names;
*/
strstr(params, dev_t)
) {
- /*
- * then it's a kpartx generated partition.
- * remove it.
- */
- /*
- * if the opencount is 0 maybe some other
- * partitions depend on it.
- */
- if (dm_get_opencount(names->name)) {
- dm_remove_partmaps(names->name, need_sync);
- if (dm_get_opencount(names->name)) {
- condlog(2, "%s: map in use",
- names->name);
- goto out;
- }
- }
- condlog(4, "partition map %s removed",
- names->name);
- dm_simplecmd_flush(DM_DEVICE_REMOVE, names->name,
- need_sync);
+ if (partmap_func(names->name, data) != 0)
+ goto out;
}
next = names->next;
return r;
}
+struct remove_data {
+ int need_sync;
+ int deferred_remove;
+};
+
+static int
+remove_partmap(const char *name, void *data)
+{
+ struct remove_data *rd = (struct remove_data *)data;
+
+ if (dm_get_opencount(name)) {
+ dm_remove_partmaps(name, rd->need_sync, rd->deferred_remove);
+ if (!do_deferred(rd->deferred_remove) &&
+ dm_get_opencount(name)) {
+ condlog(2, "%s: map in use", name);
+ return 1;
+ }
+ }
+ condlog(4, "partition map %s removed", name);
+ dm_device_remove(name, rd->need_sync, rd->deferred_remove);
+ return 0;
+}
+
+int
+dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove)
+{
+ struct remove_data rd = { need_sync, deferred_remove };
+ return do_foreach_partmaps(mapname, remove_partmap, &rd);
+}
+
+#ifdef LIBDM_API_DEFERRED
+
+static int
+cancel_remove_partmap (const char *name, void *unused)
+{
+ if (dm_get_opencount(name))
+ dm_cancel_remove_partmaps(name);
+ if (dm_message(name, "@cancel_deferred_remove") != 0)
+ condlog(0, "%s: can't cancel deferred remove: %s", name,
+ strerror(errno));
+ return 0;
+}
+
+static int
+dm_get_deferred_remove (char * mapname)
+{
+ int r = -1;
+ struct dm_task *dmt;
+ struct dm_info info;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+ return -1;
+
+ if (!dm_task_set_name(dmt, mapname))
+ goto out;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ if (!dm_task_get_info(dmt, &info))
+ goto out;
+
+ r = info.deferred_remove;
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int
+dm_cancel_remove_partmaps(const char * mapname) {
+ return do_foreach_partmaps(mapname, cancel_remove_partmap, NULL);
+}
+
+int
+dm_cancel_deferred_remove (struct multipath *mpp)
+{
+ int r = 0;
+
+ if (!dm_get_deferred_remove(mpp->alias))
+ return 0;
+ if (mpp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS)
+ mpp->deferred_remove = DEFERRED_REMOVE_ON;
+
+ dm_cancel_remove_partmaps(mpp->alias);
+ r = dm_message(mpp->alias, "@cancel_deferred_remove");
+ if (r)
+ condlog(0, "%s: can't cancel deferred remove: %s", mpp->alias,
+ strerror(errno));
+ else
+ condlog(2, "%s: canceled deferred remove", mpp->alias);
+ return r;
+}
+
+#else
+
+int
+dm_cancel_deferred_remove (struct multipath *mpp)
+{
+ return 0;
+}
+
+#endif
+
static struct dm_info *
alloc_dminfo (void)
{
return r;
}
-int
-dm_rename_partmaps (char * old, char * new)
+struct rename_data {
+ const char *old;
+ char *new;
+ char *delim;
+};
+
+static int
+rename_partmap (const char *name, void *data)
{
- struct dm_task *dmt;
- struct dm_names *names;
- unsigned next = 0;
char buff[PARAMS_SIZE];
- unsigned long long size;
- char dev_t[32];
- int r = 1;
-
- if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
- return 1;
-
- dm_task_no_open_count(dmt);
+ int offset;
+ struct rename_data *rd = (struct rename_data *)data;
- if (!dm_task_run(dmt))
- goto out;
-
- if (!(names = dm_task_get_names(dmt)))
- goto out;
-
- if (!names->dev) {
- r = 0; /* this is perfectly valid */
- goto out;
- }
-
- if (dm_dev_t(old, &dev_t[0], 32))
- goto out;
-
- do {
- if (
- /*
- * if devmap target is "linear"
- */
- (dm_type(names->name, TGT_PART) > 0) &&
-
- /*
- * and the multipath mapname and the part mapname start
- * the same
- */
- !strncmp(names->name, old, strlen(old)) &&
-
- /*
- * and we can fetch the map table from the kernel
- */
- !dm_get_map(names->name, &size, &buff[0]) &&
+ if (strncmp(name, rd->old, strlen(rd->old)) != 0)
+ return 0;
+ for (offset = strlen(rd->old); name[offset] && !(isdigit(name[offset])); offset++); /* do nothing */
+ snprintf(buff, PARAMS_SIZE, "%s%s%s", rd->new, rd->delim,
+ name + offset);
+ dm_rename(name, buff);
+ condlog(4, "partition map %s renamed", name);
+ return 0;
+}
- /*
- * and the table maps over the multipath map
- */
- strstr(buff, dev_t)
- ) {
- /*
- * then it's a kpartx generated partition.
- * Rename it.
- */
- snprintf(buff, PARAMS_SIZE, "%s%s",
- new, names->name + strlen(old));
- dm_rename(names->name, buff);
- condlog(4, "partition map %s renamed",
- names->name);
- }
+int
+dm_rename_partmaps (const char * old, char * new)
+{
+ struct rename_data rd;
- next = names->next;
- names = (void *) names + next;
- } while (next);
+ rd.old = old;
+ rd.new = new;
- r = 0;
-out:
- dm_task_destroy (dmt);
- return r;
+ if (conf->partition_delim)
+ rd.delim = conf->partition_delim;
+ if (isdigit(new[strlen(new)-1]))
+ rd.delim = "p";
+ else
+ rd.delim = "";
+ return do_foreach_partmaps(old, rename_partmap, &rd);
}
int
-dm_rename (char * old, char * new)
+dm_rename (const char * old, char * new)
{
int r = 0;
struct dm_task *dmt;
+ uint32_t cookie;
if (dm_rename_partmaps(old, new))
return r;
dm_task_no_open_count(dmt);
- if (!dm_task_set_cookie(dmt, &conf->cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0))
- goto out;
- if (!dm_task_run(dmt))
+ if (!dm_task_set_cookie(dmt, &cookie, (conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0))
goto out;
+ r = dm_task_run(dmt);
+
+ if (!r)
+ dm_udev_complete(cookie);
+ else
+ dm_udev_wait(cookie);
- r = 1;
out:
dm_task_destroy(dmt);
+
return r;
}
condlog(3, "%s: failed to reassign targets", name);
goto out_reload;
}
- dm_simplecmd_noflush(DM_DEVICE_RESUME, name);
+ dm_simplecmd_noflush(DM_DEVICE_RESUME, name, 1, MPATH_UDEV_RELOAD_FLAG);
}
r = 1;
#define TGT_MPATH "multipath"
#define TGT_PART "linear"
+#ifdef DM_SUBSYSTEM_UDEV_FLAG0
+#define MPATH_UDEV_RELOAD_FLAG DM_SUBSYSTEM_UDEV_FLAG0
+#else
+#define MPATH_UDEV_RELOAD_FLAG 0
+#endif
+
void dm_init(void);
int dm_prereq (void);
int dm_drv_version (unsigned int * version, char * str);
-int dm_simplecmd_flush (int, const char *, int);
-int dm_simplecmd_noflush (int, const char *);
+int dm_simplecmd_flush (int, const char *, int, uint16_t);
+int dm_simplecmd_noflush (int, const char *, int, uint16_t);
int dm_addmap_create (struct multipath *mpp, char *params);
int dm_addmap_reload (struct multipath *mpp, char *params);
int dm_map_present (const char *);
int dm_get_map(const char *, unsigned long long *, char *);
int dm_get_status(char *, char *);
int dm_type(const char *, char *);
-int _dm_flush_map (const char *, int);
-#define dm_flush_map(mapname) _dm_flush_map(mapname, 1)
-#define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0)
+int dm_is_mpath(const char *);
+int _dm_flush_map (const char *, int, int);
+int dm_flush_map_nopaths(const char * mapname, int deferred_remove);
+#define dm_flush_map(mapname) _dm_flush_map(mapname, 1, 0)
+#define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0, 0)
+int dm_cancel_deferred_remove(struct multipath *mpp);
int dm_suspend_and_flush_map(const char * mapname);
int dm_flush_maps (void);
int dm_fail_path(char * mapname, char * path);
int dm_get_major (char *name);
int dm_get_minor (char *name);
char * dm_mapname(int major, int minor);
-int dm_remove_partmaps (const char * mapname, int need_sync);
+int dm_remove_partmaps (const char * mapname, int need_sync,
+ int deferred_remove);
int dm_get_uuid(char *name, char *uuid);
int dm_get_info (char * mapname, struct dm_info ** dmi);
-int dm_rename (char * old, char * new);
+int dm_rename (const char * old, char * new);
int dm_reassign(const char * mapname);
int dm_reassign_table(const char *name, char *old, char *new);
int dm_setgeometry(struct multipath *mpp);
-void udev_wait(unsigned int c);
-void udev_set_sync_support(int c);
#define VERSION_GE(v, minv) ( \
(v[0] > minv[0]) || \
#include "prio.h"
#include "errno.h"
#include <inttypes.h>
+#include <mpath_cmd.h>
-/*
- * default block handlers
- */
static int
-polling_interval_handler(vector strvec)
+set_int(vector strvec, void *ptr)
{
+ int *int_ptr = (int *)ptr;
char * buff;
buff = VECTOR_SLOT(strvec, 1);
- conf->checkint = atoi(buff);
- conf->max_checkint = MAX_CHECKINT(conf->checkint);
+ *int_ptr = atoi(buff);
return 0;
}
static int
-def_fast_io_fail_handler(vector strvec)
+set_str(vector strvec, void *ptr)
{
- char * buff;
+ char **str_ptr = (char **)ptr;
- buff = set_value(strvec);
- if (strlen(buff) == 3 && !strcmp(buff, "off"))
- conf->fast_io_fail = MP_FAST_IO_FAIL_OFF;
- else if (sscanf(buff, "%d", &conf->fast_io_fail) != 1 ||
- conf->fast_io_fail < MP_FAST_IO_FAIL_ZERO)
- conf->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
- else if (conf->fast_io_fail == 0)
- conf->fast_io_fail = MP_FAST_IO_FAIL_ZERO;
+ if (*str_ptr)
+ FREE(*str_ptr);
+ *str_ptr = set_value(strvec);
+
+ if (!*str_ptr)
+ return 1;
- FREE(buff);
return 0;
}
static int
-def_dev_loss_handler(vector strvec)
+set_yes_no(vector strvec, void *ptr)
{
char * buff;
+ int *int_ptr = (int *)ptr;
buff = set_value(strvec);
if (!buff)
return 1;
- if (strlen(buff) == 8 && !strcmp(buff, "infinity"))
- conf->dev_loss = MAX_DEV_LOSS_TMO;
- else if (sscanf(buff, "%u", &conf->dev_loss) != 1)
- conf->dev_loss = 0;
+ if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0)
+ *int_ptr = YN_YES;
+ else
+ *int_ptr = YN_NO;
FREE(buff);
return 0;
}
static int
-verbosity_handler(vector strvec)
+set_yes_no_undef(vector strvec, void *ptr)
{
char * buff;
+ int *int_ptr = (int *)ptr;
- buff = VECTOR_SLOT(strvec, 1);
- conf->verbosity = atoi(buff);
+ buff = set_value(strvec);
+ if (!buff)
+ return 1;
+ if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0)
+ *int_ptr = YNU_NO;
+ else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0)
+ *int_ptr = YNU_YES;
+ else
+ *int_ptr = YNU_UNDEF;
+
+ FREE(buff);
return 0;
}
static int
-max_polling_interval_handler(vector strvec)
+print_int (char *buff, int len, void *ptr)
{
- char *buff;
-
- buff = VECTOR_SLOT(strvec, 1);
- conf->max_checkint = atoi(buff);
-
- return 0;
+ int *int_ptr = (int *)ptr;
+ return snprintf(buff, len, "%i", *int_ptr);
}
static int
-reassign_maps_handler(vector strvec)
+print_nonzero (char *buff, int len, void *ptr)
{
- char * buff;
+ int *int_ptr = (int *)ptr;
+ if (!*int_ptr)
+ return 0;
+ return snprintf(buff, len, "%i", *int_ptr);
+}
- buff = set_value(strvec);
- if (!strcmp(buff, "yes"))
- conf->reassign_maps = 1;
- else if (!strcmp(buff, "no"))
- conf->reassign_maps = 0;
- else
- return 1;
+static int
+print_str (char *buff, int len, void *ptr)
+{
+ char **str_ptr = (char **)ptr;
+ if (!*str_ptr)
+ return 0;
+ return snprintf(buff, len, "\"%s\"", *str_ptr);
+}
- return 0;
+static int
+print_yes_no (char *buff, int len, void *ptr)
+{
+ int *int_ptr = (int *)ptr;
+ return snprintf(buff, len, "\"%s\"",
+ (*int_ptr == YN_NO)? "no" : "yes");
}
static int
-multipath_dir_handler(vector strvec)
+print_yes_no_undef (char *buff, int len, void *ptr)
{
- conf->multipath_dir = set_value(strvec);
+ int *int_ptr = (int *)ptr;
+ if (!*int_ptr)
+ return 0;
+ return snprintf(buff, len, "\"%s\"",
+ (*int_ptr == YNU_NO)? "no" : "yes");
+}
- if (!conf->multipath_dir)
- return 1;
+#define declare_def_handler(option, function) \
+static int \
+def_ ## option ## _handler (vector strvec) \
+{ \
+ return function (strvec, &conf->option); \
+}
+
+#define declare_def_snprint(option, function) \
+static int \
+snprint_def_ ## option (char * buff, int len, void * data) \
+{ \
+ return function (buff, len, &conf->option); \
+}
+
+#define declare_def_snprint_defint(option, function, value) \
+static int \
+snprint_def_ ## option (char * buff, int len, void * data) \
+{ \
+ int i = value; \
+ if (!conf->option) \
+ return function (buff, len, &i); \
+ return function (buff, len, &conf->option); \
+}
+#define declare_def_snprint_defstr(option, function, value) \
+static int \
+snprint_def_ ## option (char * buff, int len, void * data) \
+{ \
+ char *s = value; \
+ if (!conf->option) \
+ return function (buff, len, &s); \
+ return function (buff, len, &conf->option); \
+}
+
+#define declare_hw_handler(option, function) \
+static int \
+hw_ ## option ## _handler (vector strvec) \
+{ \
+ struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \
+ if (!hwe) \
+ return 1; \
+ return function (strvec, &hwe->option); \
+}
+
+#define declare_hw_snprint(option, function) \
+static int \
+snprint_hw_ ## option (char * buff, int len, void * data) \
+{ \
+ struct hwentry * hwe = (struct hwentry *)data; \
+ return function (buff, len, &hwe->option); \
+}
+
+#define declare_ovr_handler(option, function) \
+static int \
+ovr_ ## option ## _handler (vector strvec) \
+{ \
+ if (!conf->overrides) \
+ return 1; \
+ return function (strvec, &conf->overrides->option); \
+}
+
+#define declare_ovr_snprint(option, function) \
+static int \
+snprint_ovr_ ## option (char * buff, int len, void * data) \
+{ \
+ return function (buff, len, &conf->overrides->option); \
+}
+
+#define declare_mp_handler(option, function) \
+static int \
+mp_ ## option ## _handler (vector strvec) \
+{ \
+ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \
+ if (!mpe) \
+ return 1; \
+ return function (strvec, &mpe->option); \
+}
+
+#define declare_mp_snprint(option, function) \
+static int \
+snprint_mp_ ## option (char * buff, int len, void * data) \
+{ \
+ struct mpentry * mpe = (struct mpentry *)data; \
+ return function (buff, len, &mpe->option); \
+}
+
+declare_def_handler(checkint, set_int)
+declare_def_snprint(checkint, print_int)
+
+declare_def_handler(max_checkint, set_int)
+declare_def_snprint(max_checkint, print_int)
+
+declare_def_handler(verbosity, set_int)
+declare_def_snprint(verbosity, print_int)
+
+declare_def_handler(reassign_maps, set_yes_no)
+declare_def_snprint(reassign_maps, print_yes_no)
+
+declare_def_handler(multipath_dir, set_str)
+declare_def_snprint(multipath_dir, print_str)
+
+declare_def_handler(partition_delim, set_str)
+declare_def_snprint(partition_delim, print_str)
+
+declare_def_handler(find_multipaths, set_yes_no)
+declare_def_snprint(find_multipaths, print_yes_no)
+
+declare_def_handler(selector, set_str)
+declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR)
+declare_hw_handler(selector, set_str)
+declare_hw_snprint(selector, print_str)
+declare_ovr_handler(selector, set_str)
+declare_ovr_snprint(selector, print_str)
+declare_mp_handler(selector, set_str)
+declare_mp_snprint(selector, print_str)
+
+declare_def_handler(uid_attribute, set_str)
+declare_def_snprint_defstr(uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE)
+declare_ovr_handler(uid_attribute, set_str)
+declare_ovr_snprint(uid_attribute, print_str)
+declare_hw_handler(uid_attribute, set_str)
+declare_hw_snprint(uid_attribute, print_str)
+
+declare_def_handler(getuid, set_str)
+declare_def_snprint(getuid, print_str)
+declare_ovr_handler(getuid, set_str)
+declare_ovr_snprint(getuid, print_str)
+declare_hw_handler(getuid, set_str)
+declare_hw_snprint(getuid, print_str)
+
+declare_def_handler(prio_name, set_str)
+declare_def_snprint_defstr(prio_name, print_str, DEFAULT_PRIO)
+declare_ovr_handler(prio_name, set_str)
+declare_ovr_snprint(prio_name, print_str)
+declare_hw_handler(prio_name, set_str)
+declare_hw_snprint(prio_name, print_str)
+declare_mp_handler(prio_name, set_str)
+declare_mp_snprint(prio_name, print_str)
+
+declare_def_handler(alias_prefix, set_str)
+declare_def_snprint_defstr(alias_prefix, print_str, DEFAULT_ALIAS_PREFIX)
+declare_ovr_handler(alias_prefix, set_str)
+declare_ovr_snprint(alias_prefix, print_str)
+declare_hw_handler(alias_prefix, set_str)
+declare_hw_snprint(alias_prefix, print_str)
+
+declare_def_handler(prio_args, set_str)
+declare_def_snprint_defstr(prio_args, print_str, DEFAULT_PRIO_ARGS)
+declare_ovr_handler(prio_args, set_str)
+declare_ovr_snprint(prio_args, print_str)
+declare_hw_handler(prio_args, set_str)
+declare_hw_snprint(prio_args, print_str)
+declare_mp_handler(prio_args, set_str)
+declare_mp_snprint(prio_args, print_str)
+
+declare_def_handler(features, set_str)
+declare_def_snprint_defstr(features, print_str, DEFAULT_FEATURES)
+declare_ovr_handler(features, set_str)
+declare_ovr_snprint(features, print_str)
+declare_hw_handler(features, set_str)
+declare_hw_snprint(features, print_str)
+declare_mp_handler(features, set_str)
+declare_mp_snprint(features, print_str)
+
+declare_def_handler(checker_name, set_str)
+declare_def_snprint_defstr(checker_name, print_str, DEFAULT_CHECKER)
+declare_ovr_handler(checker_name, set_str)
+declare_ovr_snprint(checker_name, print_str)
+declare_hw_handler(checker_name, set_str)
+declare_hw_snprint(checker_name, print_str)
+
+declare_def_handler(minio, set_int)
+declare_def_snprint_defint(minio, print_int, DEFAULT_MINIO)
+declare_ovr_handler(minio, set_int)
+declare_ovr_snprint(minio, print_nonzero)
+declare_hw_handler(minio, set_int)
+declare_hw_snprint(minio, print_nonzero)
+declare_mp_handler(minio, set_int)
+declare_mp_snprint(minio, print_nonzero)
+
+declare_def_handler(minio_rq, set_int)
+declare_def_snprint_defint(minio_rq, print_int, DEFAULT_MINIO_RQ)
+declare_ovr_handler(minio_rq, set_int)
+declare_ovr_snprint(minio_rq, print_nonzero)
+declare_hw_handler(minio_rq, set_int)
+declare_hw_snprint(minio_rq, print_nonzero)
+declare_mp_handler(minio_rq, set_int)
+declare_mp_snprint(minio_rq, print_nonzero)
+
+declare_def_handler(queue_without_daemon, set_yes_no)
+static int
+snprint_def_queue_without_daemon (char * buff, int len, void * data)
+{
+ switch (conf->queue_without_daemon) {
+ case QUE_NO_DAEMON_OFF:
+ return snprintf(buff, len, "\"no\"");
+ case QUE_NO_DAEMON_ON:
+ return snprintf(buff, len, "\"yes\"");
+ case QUE_NO_DAEMON_FORCE:
+ return snprintf(buff, len, "\"forced\"");
+ }
return 0;
}
+declare_def_handler(checker_timeout, set_int)
+declare_def_snprint(checker_timeout, print_nonzero)
+
+declare_def_handler(flush_on_last_del, set_yes_no_undef)
+declare_def_snprint_defint(flush_on_last_del, print_yes_no_undef, YNU_NO)
+declare_ovr_handler(flush_on_last_del, set_yes_no_undef)
+declare_ovr_snprint(flush_on_last_del, print_yes_no_undef)
+declare_hw_handler(flush_on_last_del, set_yes_no_undef)
+declare_hw_snprint(flush_on_last_del, print_yes_no_undef)
+declare_mp_handler(flush_on_last_del, set_yes_no_undef)
+declare_mp_snprint(flush_on_last_del, print_yes_no_undef)
+
+declare_def_handler(user_friendly_names, set_yes_no_undef)
+declare_def_snprint_defint(user_friendly_names, print_yes_no_undef, YNU_NO)
+declare_ovr_handler(user_friendly_names, set_yes_no_undef)
+declare_ovr_snprint(user_friendly_names, print_yes_no_undef)
+declare_hw_handler(user_friendly_names, set_yes_no_undef)
+declare_hw_snprint(user_friendly_names, print_yes_no_undef)
+declare_mp_handler(user_friendly_names, set_yes_no_undef)
+declare_mp_snprint(user_friendly_names, print_yes_no_undef)
+
+declare_def_handler(bindings_file, set_str)
+declare_def_snprint(bindings_file, print_str)
+
+declare_def_handler(wwids_file, set_str)
+declare_def_snprint(wwids_file, print_str)
+
+declare_def_handler(retain_hwhandler, set_yes_no_undef)
+declare_def_snprint_defint(retain_hwhandler, print_yes_no_undef, YNU_NO)
+declare_ovr_handler(retain_hwhandler, set_yes_no_undef)
+declare_ovr_snprint(retain_hwhandler, print_yes_no_undef)
+declare_hw_handler(retain_hwhandler, set_yes_no_undef)
+declare_hw_snprint(retain_hwhandler, print_yes_no_undef)
+
+declare_def_handler(detect_prio, set_yes_no_undef)
+declare_def_snprint_defint(detect_prio, print_yes_no_undef, YNU_NO)
+declare_ovr_handler(detect_prio, set_yes_no_undef)
+declare_ovr_snprint(detect_prio, print_yes_no_undef)
+declare_hw_handler(detect_prio, set_yes_no_undef)
+declare_hw_snprint(detect_prio, print_yes_no_undef)
+
+declare_def_handler(force_sync, set_yes_no)
+declare_def_snprint(force_sync, print_yes_no)
+
+declare_def_handler(deferred_remove, set_yes_no_undef)
+declare_def_snprint_defint(deferred_remove, print_yes_no_undef, YNU_NO)
+declare_ovr_handler(deferred_remove, set_yes_no_undef)
+declare_ovr_snprint(deferred_remove, print_yes_no_undef)
+declare_hw_handler(deferred_remove, set_yes_no_undef)
+declare_hw_snprint(deferred_remove, print_yes_no_undef)
+declare_mp_handler(deferred_remove, set_yes_no_undef)
+declare_mp_snprint(deferred_remove, print_yes_no_undef)
+
+declare_def_handler(retrigger_tries, set_int)
+declare_def_snprint(retrigger_tries, print_int)
+
+declare_def_handler(retrigger_delay, set_int)
+declare_def_snprint(retrigger_delay, print_int)
+
+declare_def_handler(uev_wait_timeout, set_int)
+declare_def_snprint(uev_wait_timeout, print_int)
+
static int
-def_selector_handler(vector strvec)
+def_config_dir_handler(vector strvec)
{
- conf->selector = set_value(strvec);
+ /* this is only valid in the main config file */
+ if (conf->processed_main_config)
+ return 0;
+ return set_str(strvec, &conf->config_dir);
+}
+declare_def_snprint(config_dir, print_str)
- if (!conf->selector)
- return 1;
+#define declare_def_attr_handler(option, function) \
+static int \
+def_ ## option ## _handler (vector strvec) \
+{ \
+ return function (strvec, &conf->option, &conf->attribute_flags);\
+}
- return 0;
+#define declare_def_attr_snprint(option, function) \
+static int \
+snprint_def_ ## option (char * buff, int len, void * data) \
+{ \
+ return function (buff, len, &conf->option, \
+ &conf->attribute_flags); \
+}
+
+#define declare_mp_attr_handler(option, function) \
+static int \
+mp_ ## option ## _handler (vector strvec) \
+{ \
+ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \
+ if (!mpe) \
+ return 1; \
+ return function (strvec, &mpe->option, &mpe->attribute_flags); \
+}
+
+#define declare_mp_attr_snprint(option, function) \
+static int \
+snprint_mp_ ## option (char * buff, int len, void * data) \
+{ \
+ struct mpentry * mpe = (struct mpentry *)data; \
+ return function (buff, len, &mpe->option, \
+ &mpe->attribute_flags); \
}
static int
-def_pgpolicy_handler(vector strvec)
+set_mode(vector strvec, void *ptr, int *flags)
{
- char * buff;
+ mode_t mode;
+ mode_t *mode_ptr = (mode_t *)ptr;
+ char *buff;
buff = set_value(strvec);
if (!buff)
return 1;
- conf->pgpolicy = get_pgpolicy_id(buff);
- FREE(buff);
+ if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) {
+ *flags |= (1 << ATTR_MODE);
+ *mode_ptr = mode;
+ }
+ FREE(buff);
return 0;
}
static int
-def_uid_attribute_handler(vector strvec)
+set_uid(vector strvec, void *ptr, int *flags)
{
- conf->uid_attribute = set_value(strvec);
+ uid_t uid;
+ uid_t *uid_ptr = (uid_t *)ptr;
+ char *buff;
+ char passwd_buf[1024];
+ struct passwd info, *found;
- if (!conf->uid_attribute)
+ buff = set_value(strvec);
+ if (!buff)
return 1;
+ if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) {
+ *flags |= (1 << ATTR_UID);
+ *uid_ptr = info.pw_uid;
+ }
+ else if (sscanf(buff, "%u", &uid) == 1){
+ *flags |= (1 << ATTR_UID);
+ *uid_ptr = uid;
+ }
+ FREE(buff);
return 0;
}
static int
-def_getuid_callout_handler(vector strvec)
+set_gid(vector strvec, void *ptr, int *flags)
{
- conf->getuid = set_value(strvec);
+ gid_t gid;
+ gid_t *gid_ptr = (gid_t *)ptr;
+ char *buff;
+ char passwd_buf[1024];
+ struct passwd info, *found;
- if (!conf->getuid)
+ buff = set_value(strvec);
+ if (!buff)
return 1;
+ if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) {
+ *flags |= (1 << ATTR_GID);
+ *gid_ptr = info.pw_gid;
+ }
+ else if (sscanf(buff, "%u", &gid) == 1){
+ *flags |= (1 << ATTR_GID);
+ *gid_ptr = gid;
+ }
+ FREE(buff);
return 0;
}
static int
-def_prio_handler(vector strvec)
+print_mode(char * buff, int len, void *ptr, int *flags)
{
- conf->prio_name = set_value(strvec);
-
- if (!conf->prio_name)
- return 1;
-
- return 0;
+ mode_t *mode_ptr = (mode_t *)ptr;
+ if ((*flags & (1 << ATTR_MODE)) == 0)
+ return 0;
+ return snprintf(buff, len, "0%o", *mode_ptr);
}
static int
-def_alias_prefix_handler(vector strvec)
+print_uid(char * buff, int len, void *ptr, int *flags)
{
- conf->alias_prefix = set_value(strvec);
-
- if (!conf->alias_prefix)
- return 1;
-
- return 0;
+ uid_t *uid_ptr = (uid_t *)ptr;
+ if ((*flags & (1 << ATTR_UID)) == 0)
+ return 0;
+ return snprintf(buff, len, "0%o", *uid_ptr);
}
static int
-def_prio_args_handler(vector strvec)
+print_gid(char * buff, int len, void *ptr, int *flags)
{
- conf->prio_args = set_value(strvec);
+ gid_t *gid_ptr = (gid_t *)ptr;
+ if ((*flags & (1 << ATTR_GID)) == 0)
+ return 0;
+ return snprintf(buff, len, "0%o", *gid_ptr);
+}
- if (!conf->prio_args)
- return 1;
+declare_def_attr_handler(mode, set_mode)
+declare_def_attr_snprint(mode, print_mode)
+declare_mp_attr_handler(mode, set_mode)
+declare_mp_attr_snprint(mode, print_mode)
- return 0;
-}
+declare_def_attr_handler(uid, set_uid)
+declare_def_attr_snprint(uid, print_uid)
+declare_mp_attr_handler(uid, set_uid)
+declare_mp_attr_snprint(uid, print_uid)
+
+declare_def_attr_handler(gid, set_gid)
+declare_def_attr_snprint(gid, print_gid)
+declare_mp_attr_handler(gid, set_gid)
+declare_mp_attr_snprint(gid, print_gid)
static int
-def_features_handler(vector strvec)
+set_fast_io_fail(vector strvec, void *ptr)
{
- conf->features = set_value(strvec);
+ char * buff;
+ int *int_ptr = (int *)ptr;
- if (!conf->features)
+ buff = set_value(strvec);
+ if (!buff)
return 1;
+ if (strcmp(buff, "off") == 0)
+ *int_ptr = MP_FAST_IO_FAIL_OFF;
+ else if (sscanf(buff, "%d", int_ptr) != 1 ||
+ *int_ptr < MP_FAST_IO_FAIL_ZERO)
+ *int_ptr = MP_FAST_IO_FAIL_UNSET;
+ else if (*int_ptr == 0)
+ *int_ptr = MP_FAST_IO_FAIL_ZERO;
+
+ FREE(buff);
return 0;
}
-static int
-def_path_checker_handler(vector strvec)
+int
+print_fast_io_fail(char * buff, int len, void *ptr)
{
- conf->checker_name = set_value(strvec);
-
- if (!conf->checker_name)
- return 1;
+ int *int_ptr = (int *)ptr;
- return 0;
+ if (*int_ptr == MP_FAST_IO_FAIL_UNSET)
+ return 0;
+ if (*int_ptr == MP_FAST_IO_FAIL_OFF)
+ return snprintf(buff, len, "\"off\"");
+ if (*int_ptr == MP_FAST_IO_FAIL_ZERO)
+ return snprintf(buff, len, "0");
+ return snprintf(buff, len, "%d", *int_ptr);
}
+declare_def_handler(fast_io_fail, set_fast_io_fail)
+declare_def_snprint(fast_io_fail, print_fast_io_fail)
+declare_ovr_handler(fast_io_fail, set_fast_io_fail)
+declare_ovr_snprint(fast_io_fail, print_fast_io_fail)
+declare_hw_handler(fast_io_fail, set_fast_io_fail)
+declare_hw_snprint(fast_io_fail, print_fast_io_fail)
+
static int
-def_minio_handler(vector strvec)
+set_dev_loss(vector strvec, void *ptr)
{
char * buff;
+ unsigned int *uint_ptr = (unsigned int *)ptr;
buff = set_value(strvec);
-
if (!buff)
return 1;
- conf->minio = atoi(buff);
- FREE(buff);
+ if (!strcmp(buff, "infinity"))
+ *uint_ptr = MAX_DEV_LOSS_TMO;
+ else if (sscanf(buff, "%u", uint_ptr) != 1)
+ *uint_ptr = 0;
+ FREE(buff);
return 0;
}
+int
+print_dev_loss(char * buff, int len, void *ptr)
+{
+ unsigned int *uint_ptr = (unsigned int *)ptr;
+
+ if (!*uint_ptr)
+ return 0;
+ if (*uint_ptr >= MAX_DEV_LOSS_TMO)
+ return snprintf(buff, len, "\"infinity\"");
+ return snprintf(buff, len, "%u", *uint_ptr);
+}
+
+declare_def_handler(dev_loss, set_dev_loss)
+declare_def_snprint(dev_loss, print_dev_loss)
+declare_ovr_handler(dev_loss, set_dev_loss)
+declare_ovr_snprint(dev_loss, print_dev_loss)
+declare_hw_handler(dev_loss, set_dev_loss)
+declare_hw_snprint(dev_loss, print_dev_loss)
+
static int
-def_minio_rq_handler(vector strvec)
+set_pgpolicy(vector strvec, void *ptr)
{
char * buff;
+ int *int_ptr = (int *)ptr;
buff = set_value(strvec);
-
if (!buff)
return 1;
- conf->minio_rq = atoi(buff);
+ *int_ptr = get_pgpolicy_id(buff);
FREE(buff);
return 0;
}
+int
+print_pgpolicy(char * buff, int len, void *ptr)
+{
+ char str[POLICY_NAME_SIZE];
+ int pgpolicy = *(int *)ptr;
+
+ if (!pgpolicy)
+ return 0;
+
+ get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy);
+
+ return snprintf(buff, len, "\"%s\"", str);
+}
+
+declare_def_handler(pgpolicy, set_pgpolicy)
+declare_def_snprint_defint(pgpolicy, print_pgpolicy, DEFAULT_PGPOLICY)
+declare_ovr_handler(pgpolicy, set_pgpolicy)
+declare_ovr_snprint(pgpolicy, print_pgpolicy)
+declare_hw_handler(pgpolicy, set_pgpolicy)
+declare_hw_snprint(pgpolicy, print_pgpolicy)
+declare_mp_handler(pgpolicy, set_pgpolicy)
+declare_mp_snprint(pgpolicy, print_pgpolicy)
+
int
get_sys_max_fds(int *max_fds)
{
}
static int
-def_mode_handler(vector strvec)
+snprint_max_fds (char * buff, int len, void * data)
{
- mode_t mode;
- char *buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
+ int r = 0, max_fds;
- if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) {
- conf->attribute_flags |= (1 << ATTR_MODE);
- conf->mode = mode;
- }
+ if (!conf->max_fds)
+ return 0;
- FREE(buff);
- return 0;
+ r = get_sys_max_fds(&max_fds);
+ if (!r && conf->max_fds >= max_fds)
+ return snprintf(buff, len, "\"max\"");
+ else
+ return snprintf(buff, len, "%d", conf->max_fds);
}
static int
-def_uid_handler(vector strvec)
+set_rr_weight(vector strvec, void *ptr)
{
- uid_t uid;
- char *buff;
- char passwd_buf[1024];
- struct passwd info, *found;
+ int *int_ptr = (int *)ptr;
+ char * buff;
buff = set_value(strvec);
+
if (!buff)
return 1;
- if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) {
- conf->attribute_flags |= (1 << ATTR_UID);
- conf->uid = info.pw_uid;
- }
- else if (sscanf(buff, "%u", &uid) == 1){
- conf->attribute_flags |= (1 << ATTR_UID);
- conf->uid = uid;
- }
-
- FREE(buff);
- return 0;
-}
-static int
-def_gid_handler(vector strvec)
-{
- gid_t gid;
- char *buff;
- char passwd_buf[1024];
- struct passwd info, *found;
+ if (!strcmp(buff, "priorities"))
+ *int_ptr = RR_WEIGHT_PRIO;
- buff = set_value(strvec);
- if (!buff)
- return 1;
+ if (!strcmp(buff, "uniform"))
+ *int_ptr = RR_WEIGHT_NONE;
- if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) {
- conf->attribute_flags |= (1 << ATTR_GID);
- conf->gid = info.pw_gid;
- }
- else if (sscanf(buff, "%u", &gid) == 1){
- conf->attribute_flags |= (1 << ATTR_GID);
- conf->gid = gid;
- }
FREE(buff);
+
return 0;
}
-static int
-def_weight_handler(vector strvec)
+int
+print_rr_weight (char * buff, int len, void *ptr)
{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- if (strlen(buff) == 10 &&
- !strcmp(buff, "priorities"))
- conf->rr_weight = RR_WEIGHT_PRIO;
+ int *int_ptr = (int *)ptr;
- if (strlen(buff) == strlen("uniform") &&
- !strcmp(buff, "uniform"))
- conf->rr_weight = RR_WEIGHT_NONE;
-
- FREE(buff);
+ if (!*int_ptr)
+ return 0;
+ if (*int_ptr == RR_WEIGHT_PRIO)
+ return snprintf(buff, len, "\"priorities\"");
+ if (*int_ptr == RR_WEIGHT_NONE)
+ return snprintf(buff, len, "\"uniform\"");
return 0;
}
+declare_def_handler(rr_weight, set_rr_weight)
+declare_def_snprint_defint(rr_weight, print_rr_weight, RR_WEIGHT_NONE)
+declare_ovr_handler(rr_weight, set_rr_weight)
+declare_ovr_snprint(rr_weight, print_rr_weight)
+declare_hw_handler(rr_weight, set_rr_weight)
+declare_hw_snprint(rr_weight, print_rr_weight)
+declare_mp_handler(rr_weight, set_rr_weight)
+declare_mp_snprint(rr_weight, print_rr_weight)
+
static int
-default_failback_handler(vector strvec)
+set_pgfailback(vector strvec, void *ptr)
{
+ int *int_ptr = (int *)ptr;
char * buff;
buff = set_value(strvec);
if (strlen(buff) == 6 && !strcmp(buff, "manual"))
- conf->pgfailback = -FAILBACK_MANUAL;
+ *int_ptr = -FAILBACK_MANUAL;
else if (strlen(buff) == 9 && !strcmp(buff, "immediate"))
- conf->pgfailback = -FAILBACK_IMMEDIATE;
+ *int_ptr = -FAILBACK_IMMEDIATE;
else if (strlen(buff) == 10 && !strcmp(buff, "followover"))
- conf->pgfailback = -FAILBACK_FOLLOWOVER;
+ *int_ptr = -FAILBACK_FOLLOWOVER;
else
- conf->pgfailback = atoi(buff);
+ *int_ptr = atoi(buff);
FREE(buff);
return 0;
}
+int
+print_pgfailback (char * buff, int len, void *ptr)
+{
+ int *int_ptr = (int *)ptr;
+
+ switch(*int_ptr) {
+ case FAILBACK_UNDEF:
+ return 0;
+ case -FAILBACK_MANUAL:
+ return snprintf(buff, len, "\"manual\"");
+ case -FAILBACK_IMMEDIATE:
+ return snprintf(buff, len, "\"immediate\"");
+ case -FAILBACK_FOLLOWOVER:
+ return snprintf(buff, len, "\"followover\"");
+ default:
+ return snprintf(buff, len, "%i", *int_ptr);
+ }
+}
+
+declare_def_handler(pgfailback, set_pgfailback)
+declare_def_snprint_defint(pgfailback, print_pgfailback, DEFAULT_FAILBACK)
+declare_ovr_handler(pgfailback, set_pgfailback)
+declare_ovr_snprint(pgfailback, print_pgfailback)
+declare_hw_handler(pgfailback, set_pgfailback)
+declare_hw_snprint(pgfailback, print_pgfailback)
+declare_mp_handler(pgfailback, set_pgfailback)
+declare_mp_snprint(pgfailback, print_pgfailback)
+
static int
-def_no_path_retry_handler(vector strvec)
+set_no_path_retry(vector strvec, void *ptr)
{
+ int *int_ptr = (int *)ptr;
char * buff;
buff = set_value(strvec);
if (!buff)
return 1;
- if ((strlen(buff) == 4 && !strcmp(buff, "fail")) ||
- (strlen(buff) == 1 && !strcmp(buff, "0")))
- conf->no_path_retry = NO_PATH_RETRY_FAIL;
- else if (strlen(buff) == 5 && !strcmp(buff, "queue"))
- conf->no_path_retry = NO_PATH_RETRY_QUEUE;
- else if ((conf->no_path_retry = atoi(buff)) < 1)
- conf->no_path_retry = NO_PATH_RETRY_UNDEF;
+ if (!strcmp(buff, "fail") || !strcmp(buff, "0"))
+ *int_ptr = NO_PATH_RETRY_FAIL;
+ else if (!strcmp(buff, "queue"))
+ *int_ptr = NO_PATH_RETRY_QUEUE;
+ else if ((*int_ptr = atoi(buff)) < 1)
+ *int_ptr = NO_PATH_RETRY_UNDEF;
FREE(buff);
return 0;
}
+int
+print_no_path_retry(char * buff, int len, void *ptr)
+{
+ int *int_ptr = (int *)ptr;
+
+ switch(*int_ptr) {
+ case NO_PATH_RETRY_UNDEF:
+ return 0;
+ case NO_PATH_RETRY_FAIL:
+ return snprintf(buff, len, "\"fail\"");
+ case NO_PATH_RETRY_QUEUE:
+ return snprintf(buff, len, "\"queue\"");
+ default:
+ return snprintf(buff, len, "%i", *int_ptr);
+ }
+}
+
+declare_def_handler(no_path_retry, set_no_path_retry)
+declare_def_snprint(no_path_retry, print_no_path_retry)
+declare_ovr_handler(no_path_retry, set_no_path_retry)
+declare_ovr_snprint(no_path_retry, print_no_path_retry)
+declare_hw_handler(no_path_retry, set_no_path_retry)
+declare_hw_snprint(no_path_retry, print_no_path_retry)
+declare_mp_handler(no_path_retry, set_no_path_retry)
+declare_mp_snprint(no_path_retry, print_no_path_retry)
+
static int
-def_queue_without_daemon(vector strvec)
+def_log_checker_err_handler(vector strvec)
{
char * buff;
buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if (!strncmp(buff, "on", 2) || !strncmp(buff, "yes", 3) ||
- !strncmp(buff, "1", 1))
- conf->queue_without_daemon = QUE_NO_DAEMON_ON;
- else
- conf->queue_without_daemon = QUE_NO_DAEMON_OFF;
-
- free(buff);
- return 0;
-}
-
-static int
-def_checker_timeout_handler(vector strvec)
-{
- unsigned int checker_timeout;
- char *buff;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if (sscanf(buff, "%u", &checker_timeout) == 1)
- conf->checker_timeout = checker_timeout;
- else
- conf->checker_timeout = 0;
-
- free(buff);
- return 0;
-}
-
-static int
-def_pg_timeout_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- /* Deprecated; device-mapper support has been removed */
-
- FREE(buff);
- return 0;
-}
-
-static int
-def_flush_on_last_del_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "0") == 0))
- conf->flush_on_last_del = FLUSH_DISABLED;
- else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "1") == 0))
- conf->flush_on_last_del = FLUSH_ENABLED;
- else
- conf->flush_on_last_del = FLUSH_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-static int
-def_log_checker_err_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
+
if (!buff)
return 1;
}
static int
-def_reservation_key_handler(vector strvec)
+snprint_def_log_checker_err (char * buff, int len, void * data)
+{
+ if (conf->log_checker_err == LOG_CHKR_ERR_ONCE)
+ return snprintf(buff, len, "once");
+ return snprintf(buff, len, "always");
+}
+
+static int
+set_reservation_key(vector strvec, void *ptr)
{
+ unsigned char **uchar_ptr = (unsigned char **)ptr;
char *buff;
char *tbuff;
int j, k;
return 1;
}
- if (!conf->reservation_key)
- conf->reservation_key = (unsigned char *) malloc(8);
+ if (!*uchar_ptr)
+ *uchar_ptr = (unsigned char *) malloc(8);
- memset(conf->reservation_key, 0, 8);
+ memset(*uchar_ptr, 0, 8);
for (j = 7; j >= 0; --j) {
- conf->reservation_key[j] = (prkey & 0xff);
+ (*uchar_ptr)[j] = (prkey & 0xff);
prkey >>= 8;
}
return 0;
}
+int
+print_reservation_key(char * buff, int len, void * ptr)
+{
+ unsigned char **uchar_ptr = (unsigned char **)ptr;
+ int i;
+ unsigned char *keyp;
+ uint64_t prkey = 0;
+
+ if (!*uchar_ptr)
+ return 0;
+ keyp = (unsigned char *)(*uchar_ptr);
+ for (i = 0; i < 8; i++) {
+ if (i > 0)
+ prkey <<= 8;
+ prkey |= *keyp;
+ keyp++;
+ }
+ return snprintf(buff, len, "0x%" PRIx64, prkey);
+}
+
+declare_def_handler(reservation_key, set_reservation_key)
+declare_def_snprint(reservation_key, print_reservation_key)
+declare_mp_handler(reservation_key, set_reservation_key)
+declare_mp_snprint(reservation_key, print_reservation_key)
+
static int
-def_names_handler(vector strvec)
+set_delay_checks(vector strvec, void *ptr)
{
+ int *int_ptr = (int *)ptr;
char * buff;
buff = set_value(strvec);
-
if (!buff)
return 1;
- if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
- (strlen(buff) == 1 && !strcmp(buff, "0")))
- conf->user_friendly_names = USER_FRIENDLY_NAMES_OFF;
- else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
- (strlen(buff) == 1 && !strcmp(buff, "1")))
- conf->user_friendly_names = USER_FRIENDLY_NAMES_ON;
- else
- conf->user_friendly_names = USER_FRIENDLY_NAMES_UNDEF;
+ if (!strcmp(buff, "no") || !strcmp(buff, "0"))
+ *int_ptr = DELAY_CHECKS_OFF;
+ else if ((*int_ptr = atoi(buff)) < 1)
+ *int_ptr = DELAY_CHECKS_UNDEF;
FREE(buff);
return 0;
}
-static int
-bindings_file_handler(vector strvec)
-{
- conf->bindings_file = set_value(strvec);
-
- if (!conf->bindings_file)
- return 1;
-
- return 0;
-}
-
-static int
-wwids_file_handler(vector strvec)
+int
+print_delay_checks(char * buff, int len, void *ptr)
{
- conf->wwids_file = set_value(strvec);
-
- if (!conf->wwids_file)
- return 1;
+ int *int_ptr = (int *)ptr;
- return 0;
+ switch(*int_ptr) {
+ case DELAY_CHECKS_UNDEF:
+ return 0;
+ case DELAY_CHECKS_OFF:
+ return snprintf(buff, len, "\"off\"");
+ default:
+ return snprintf(buff, len, "%i", *int_ptr);
+ }
}
-static int
-def_retain_hwhandler_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
+declare_def_handler(delay_watch_checks, set_delay_checks)
+declare_def_snprint(delay_watch_checks, print_delay_checks)
+declare_ovr_handler(delay_watch_checks, set_delay_checks)
+declare_ovr_snprint(delay_watch_checks, print_delay_checks)
+declare_hw_handler(delay_watch_checks, set_delay_checks)
+declare_hw_snprint(delay_watch_checks, print_delay_checks)
+declare_mp_handler(delay_watch_checks, set_delay_checks)
+declare_mp_snprint(delay_watch_checks, print_delay_checks)
- if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
- (strlen(buff) == 1 && !strcmp(buff, "0")))
- conf->retain_hwhandler = RETAIN_HWHANDLER_OFF;
- else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
- (strlen(buff) == 1 && !strcmp(buff, "1")))
- conf->retain_hwhandler = RETAIN_HWHANDLER_ON;
- else
- conf->retain_hwhandler = RETAIN_HWHANDLER_UNDEF;
-
- FREE(buff);
- return 0;
-}
+declare_def_handler(delay_wait_checks, set_delay_checks)
+declare_def_snprint(delay_wait_checks, print_delay_checks)
+declare_ovr_handler(delay_wait_checks, set_delay_checks)
+declare_ovr_snprint(delay_wait_checks, print_delay_checks)
+declare_hw_handler(delay_wait_checks, set_delay_checks)
+declare_hw_snprint(delay_wait_checks, print_delay_checks)
+declare_mp_handler(delay_wait_checks, set_delay_checks)
+declare_mp_snprint(delay_wait_checks, print_delay_checks)
static int
-def_detect_prio_handler(vector strvec)
+def_uxsock_timeout_handler(vector strvec)
{
- char * buff;
+ unsigned int uxsock_timeout;
+ char *buff;
buff = set_value(strvec);
-
if (!buff)
return 1;
- if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
- (strlen(buff) == 1 && !strcmp(buff, "0")))
- conf->detect_prio = DETECT_PRIO_OFF;
- else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
- (strlen(buff) == 1 && !strcmp(buff, "1")))
- conf->detect_prio = DETECT_PRIO_ON;
+ if (sscanf(buff, "%u", &uxsock_timeout) == 1 &&
+ uxsock_timeout > DEFAULT_REPLY_TIMEOUT)
+ conf->uxsock_timeout = uxsock_timeout;
else
- conf->detect_prio = DETECT_PRIO_UNDEF;
+ conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT;
- FREE(buff);
+ free(buff);
return 0;
}
static int
blacklist_handler(vector strvec)
{
- conf->blist_devnode = vector_alloc();
- conf->blist_wwid = vector_alloc();
- conf->blist_device = vector_alloc();
- conf->blist_property = vector_alloc();
+ if (!conf->blist_devnode)
+ conf->blist_devnode = vector_alloc();
+ if (!conf->blist_wwid)
+ conf->blist_wwid = vector_alloc();
+ if (!conf->blist_device)
+ conf->blist_device = vector_alloc();
+ if (!conf->blist_property)
+ conf->blist_property = vector_alloc();
if (!conf->blist_devnode || !conf->blist_wwid ||
!conf->blist_device || !conf->blist_property)
static int
blacklist_exceptions_handler(vector strvec)
{
- conf->elist_devnode = vector_alloc();
- conf->elist_wwid = vector_alloc();
- conf->elist_device = vector_alloc();
- conf->elist_property = vector_alloc();
+ if (!conf->elist_devnode)
+ conf->elist_devnode = vector_alloc();
+ if (!conf->elist_wwid)
+ conf->elist_wwid = vector_alloc();
+ if (!conf->elist_device)
+ conf->elist_device = vector_alloc();
+ if (!conf->elist_property)
+ conf->elist_property = vector_alloc();
if (!conf->elist_devnode || !conf->elist_wwid ||
!conf->elist_device || !conf->elist_property)
return 0;
}
-static int
-ble_devnode_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- return store_ble(conf->blist_devnode, buff, ORIGIN_CONFIG);
-}
-
-static int
-ble_except_devnode_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- return store_ble(conf->elist_devnode, buff, ORIGIN_CONFIG);
+#define declare_ble_handler(option) \
+static int \
+ble_ ## option ## _handler (vector strvec) \
+{ \
+ char * buff; \
+ \
+ if (!conf->option) \
+ return 1; \
+ \
+ buff = set_value(strvec); \
+ if (!buff) \
+ return 1; \
+ \
+ return store_ble(conf->option, buff, ORIGIN_CONFIG); \
}
-static int
-ble_wwid_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- return store_ble(conf->blist_wwid, buff, ORIGIN_CONFIG);
+#define declare_ble_device_handler(name, option, vend, prod) \
+static int \
+ble_ ## option ## _ ## name ## _handler (vector strvec) \
+{ \
+ char * buff; \
+ \
+ if (!conf->option) \
+ return 1; \
+ \
+ buff = set_value(strvec); \
+ if (!buff) \
+ return 1; \
+ \
+ return set_ble_device(conf->option, vend, prod, ORIGIN_CONFIG); \
}
-static int
-ble_except_wwid_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- return store_ble(conf->elist_wwid, buff, ORIGIN_CONFIG);
-}
+declare_ble_handler(blist_devnode)
+declare_ble_handler(elist_devnode)
+declare_ble_handler(blist_wwid)
+declare_ble_handler(elist_wwid)
+declare_ble_handler(blist_property)
+declare_ble_handler(elist_property)
static int
-ble_property_handler(vector strvec)
+snprint_def_uxsock_timeout(char * buff, int len, void * data)
{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- return store_ble(conf->blist_property, buff, ORIGIN_CONFIG);
+ return snprintf(buff, len, "%u", conf->uxsock_timeout);
}
static int
-ble_except_property_handler(vector strvec)
+snprint_ble_simple (char * buff, int len, void * data)
{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
+ struct blentry * ble = (struct blentry *)data;
- return store_ble(conf->elist_property, buff, ORIGIN_CONFIG);
+ return snprintf(buff, len, "\"%s\"", ble->str);
}
static int
return alloc_ble_device(conf->elist_device);
}
-static int
-ble_vendor_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- return set_ble_device(conf->blist_device, buff, NULL, ORIGIN_CONFIG);
-}
-
-static int
-ble_except_vendor_handler(vector strvec)
-{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- return set_ble_device(conf->elist_device, buff, NULL, ORIGIN_CONFIG);
-}
+declare_ble_device_handler(vendor, blist_device, buff, NULL)
+declare_ble_device_handler(vendor, elist_device, buff, NULL)
+declare_ble_device_handler(product, blist_device, NULL, buff)
+declare_ble_device_handler(product, elist_device, NULL, buff)
static int
-ble_product_handler(vector strvec)
+snprint_bled_vendor (char * buff, int len, void * data)
{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
+ struct blentry_device * bled = (struct blentry_device *)data;
- return set_ble_device(conf->blist_device, NULL, buff, ORIGIN_CONFIG);
+ return snprintf(buff, len, "\"%s\"", bled->vendor);
}
static int
-ble_except_product_handler(vector strvec)
+snprint_bled_product (char * buff, int len, void * data)
{
- char * buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
+ struct blentry_device * bled = (struct blentry_device *)data;
- return set_ble_device(conf->elist_device, NULL, buff, ORIGIN_CONFIG);
+ return snprintf(buff, len, "\"%s\"", bled->product);
}
/*
return 0;
}
-static int
-vendor_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+declare_hw_handler(vendor, set_str)
+declare_hw_snprint(vendor, print_str)
- if (!hwe)
- return 1;
+declare_hw_handler(product, set_str)
+declare_hw_snprint(product, print_str)
- hwe->vendor = set_value(strvec);
+declare_hw_handler(revision, set_str)
+declare_hw_snprint(revision, print_str)
- if (!hwe->vendor)
- return 1;
+declare_hw_handler(bl_product, set_str)
+declare_hw_snprint(bl_product, print_str)
- return 0;
-}
+declare_hw_handler(hwhandler, set_str)
+declare_hw_snprint(hwhandler, print_str)
+/*
+ * overrides handlers
+ */
static int
-product_handler(vector strvec)
+overrides_handler(vector strvec)
{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- if (!hwe)
- return 1;
+ if (!conf->overrides)
+ conf->overrides = alloc_hwe();
- hwe->product = set_value(strvec);
-
- if (!hwe->product)
+ if (!conf->overrides)
return 1;
return 0;
}
-static int
-revision_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
- if (!hwe)
- return 1;
- hwe->revision = set_value(strvec);
+/*
+ * multipaths block handlers
+ */
+static int
+multipaths_handler(vector strvec)
+{
+ if (!conf->mptable)
+ conf->mptable = vector_alloc();
- if (!hwe->revision)
+ if (!conf->mptable)
return 1;
return 0;
}
static int
-bl_product_handler(vector strvec)
+multipath_handler(vector strvec)
{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+ struct mpentry * mpe;
- if (!hwe)
+ mpe = alloc_mpe();
+
+ if (!mpe)
return 1;
- hwe->bl_product = set_value(strvec);
- if (!hwe->bl_product)
+ if (!vector_alloc_slot(conf->mptable)) {
+ free_mpe(mpe);
return 1;
+ }
+ vector_set_slot(conf->mptable, mpe);
return 0;
}
+declare_mp_handler(wwid, set_str)
+declare_mp_snprint(wwid, print_str)
+
+declare_mp_handler(alias, set_str)
+declare_mp_snprint(alias, print_str)
+
+/*
+ * deprecated handlers
+ */
+
static int
-hw_fast_io_fail_handler(vector strvec)
+deprecated_handler(vector strvec)
{
char * buff;
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
buff = set_value(strvec);
- if (strlen(buff) == 3 && !strcmp(buff, "off"))
- hwe->fast_io_fail = MP_FAST_IO_FAIL_OFF;
- else if (sscanf(buff, "%d", &hwe->fast_io_fail) != 1 ||
- hwe->fast_io_fail < MP_FAST_IO_FAIL_ZERO)
- hwe->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
- else if (hwe->fast_io_fail == 0)
- hwe->fast_io_fail = MP_FAST_IO_FAIL_ZERO;
+
+ if (!buff)
+ return 1;
FREE(buff);
return 0;
}
static int
-hw_dev_loss_handler(vector strvec)
+snprint_deprecated (char * buff, int len, void * data)
{
- char * buff;
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if (strlen(buff) == 8 && !strcmp(buff, "infinity"))
- hwe->dev_loss = MAX_DEV_LOSS_TMO;
- else if (sscanf(buff, "%u", &hwe->dev_loss) != 1)
- hwe->dev_loss = 0;
-
- FREE(buff);
- return 0;
-}
-
-static int
-hw_pgpolicy_handler(vector strvec)
-{
- char * buff;
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- hwe->pgpolicy = get_pgpolicy_id(buff);
- FREE(buff);
-
- return 0;
-}
-
-static int
-hw_uid_attribute_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- hwe->uid_attribute = set_value(strvec);
-
- if (!hwe->uid_attribute)
- return 1;
-
- return 0;
-}
-
-static int
-hw_getuid_callout_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- hwe->getuid = set_value(strvec);
-
- if (!hwe->getuid)
- return 1;
-
- return 0;
-}
-
-static int
-hw_selector_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- if (!hwe)
- return 1;
-
- hwe->selector = set_value(strvec);
-
- if (!hwe->selector)
- return 1;
-
- return 0;
-}
-
-static int
-hw_path_checker_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- if (!hwe)
- return 1;
-
- hwe->checker_name = set_value(strvec);
-
- if (!hwe->checker_name)
- return 1;
-
- return 0;
-}
-
-static int
-hw_features_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- if (!hwe)
- return 1;
-
- hwe->features = set_value(strvec);
-
- if (!hwe->features)
- return 1;
-
- return 0;
-}
-
-static int
-hw_handler_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- if (!hwe)
- return 1;
-
- hwe->hwhandler = set_value(strvec);
-
- if (!hwe->hwhandler)
- return 1;
-
- return 0;
-}
-
-static int
-hw_prio_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- if (!hwe)
- return 1;
-
- hwe->prio_name = set_value(strvec);
-
- if (!hwe->prio_name)
- return 1;
-
- return 0;
-}
-
-static int
-hw_alias_prefix_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- if (!hwe)
- return 1;
-
- hwe->alias_prefix = set_value(strvec);
-
- if (!hwe->alias_prefix)
- return 1;
-
- return 0;
-}
-
-static int
-hw_prio_args_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
-
- if (!hwe)
- return 1;
-
- hwe->prio_args = set_value(strvec);
-
- if (!hwe->prio_args)
- return 1;
-
- return 0;
-}
-
-static int
-hw_failback_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char * buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
-
- if (strlen(buff) == 6 && !strcmp(buff, "manual"))
- hwe->pgfailback = -FAILBACK_MANUAL;
- else if (strlen(buff) == 9 && !strcmp(buff, "immediate"))
- hwe->pgfailback = -FAILBACK_IMMEDIATE;
- else if (strlen(buff) == 10 && !strcmp(buff, "followover"))
- hwe->pgfailback = -FAILBACK_FOLLOWOVER;
- else
- hwe->pgfailback = atoi(buff);
-
- FREE(buff);
-
- return 0;
-}
-
-static int
-hw_weight_handler(vector strvec)
-{
- struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char * buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- if (strlen(buff) == 10 &&
- !strcmp(buff, "priorities"))
- hwe->rr_weight = RR_WEIGHT_PRIO;
-
- if (strlen(buff) == strlen("uniform") &&
- !strcmp(buff, "uniform"))
- hwe->rr_weight = RR_WEIGHT_NONE;
-
- FREE(buff);
-
- return 0;
-}
-
-static int
-hw_no_path_retry_handler(vector strvec)
-{
- struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char *buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 4 && !strcmp(buff, "fail")) ||
- (strlen(buff) == 1 && !strcmp(buff, "0")))
- hwe->no_path_retry = NO_PATH_RETRY_FAIL;
- else if (strlen(buff) == 5 && !strcmp(buff, "queue"))
- hwe->no_path_retry = NO_PATH_RETRY_QUEUE;
- else if ((hwe->no_path_retry = atoi(buff)) < 1)
- hwe->no_path_retry = NO_PATH_RETRY_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-static int
-hw_minio_handler(vector strvec)
-{
- struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char * buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- hwe->minio = atoi(buff);
- FREE(buff);
-
- return 0;
-}
-
-static int
-hw_minio_rq_handler(vector strvec)
-{
- struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char * buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- hwe->minio_rq = atoi(buff);
- FREE(buff);
-
- return 0;
-}
-
-static int
-hw_pg_timeout_handler(vector strvec)
-{
- char *buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- /* Deprecated; device-mapper support has been removed */
-
- FREE(buff);
- return 0;
-}
-
-static int
-hw_flush_on_last_del_handler(vector strvec)
-{
- struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char * buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "0") == 0))
- hwe->flush_on_last_del = FLUSH_DISABLED;
- else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "1") == 0))
- hwe->flush_on_last_del = FLUSH_ENABLED;
- else
- hwe->flush_on_last_del = FLUSH_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-static int
-hw_names_handler(vector strvec)
-{
- struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char * buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "0") == 0))
- hwe->user_friendly_names = USER_FRIENDLY_NAMES_OFF;
- else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "1") == 0))
- hwe->user_friendly_names = USER_FRIENDLY_NAMES_ON;
- else
- hwe->user_friendly_names = USER_FRIENDLY_NAMES_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-static int
-hw_retain_hwhandler_handler(vector strvec)
-{
- struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char * buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
- (strlen(buff) == 1 && !strcmp(buff, "0")))
- hwe->retain_hwhandler = RETAIN_HWHANDLER_OFF;
- else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
- (strlen(buff) == 1 && !strcmp(buff, "1")))
- hwe->retain_hwhandler = RETAIN_HWHANDLER_ON;
- else
- hwe->user_friendly_names = RETAIN_HWHANDLER_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-static int
-hw_detect_prio_handler(vector strvec)
-{
- struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
- char * buff;
-
- if (!hwe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
- (strlen(buff) == 1 && !strcmp(buff, "0")))
- hwe->detect_prio = DETECT_PRIO_OFF;
- else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
- (strlen(buff) == 1 && !strcmp(buff, "1")))
- hwe->detect_prio = DETECT_PRIO_ON;
- else
- hwe->detect_prio = DETECT_PRIO_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-/*
- * multipaths block handlers
- */
-static int
-multipaths_handler(vector strvec)
-{
- conf->mptable = vector_alloc();
-
- if (!conf->mptable)
- return 1;
-
- return 0;
-}
-
-static int
-multipath_handler(vector strvec)
-{
- struct mpentry * mpe;
-
- mpe = alloc_mpe();
-
- if (!mpe)
- return 1;
-
- if (!vector_alloc_slot(conf->mptable)) {
- free_mpe(mpe);
- return 1;
- }
- vector_set_slot(conf->mptable, mpe);
-
- return 0;
-}
-
-static int
-wwid_handler(vector strvec)
-{
- struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- mpe->wwid = set_value(strvec);
-
- if (!mpe->wwid)
- return 1;
-
- return 0;
-}
-
-static int
-alias_handler(vector strvec)
-{
- struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- mpe->alias = set_value(strvec);
-
- if (!mpe->alias)
- return 1;
-
- return 0;
-}
-
-static int
-mp_pgpolicy_handler(vector strvec)
-{
- char * buff;
- struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- mpe->pgpolicy = get_pgpolicy_id(buff);
- FREE(buff);
-
- return 0;
-}
-
-static int
-mp_selector_handler(vector strvec)
-{
- struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- mpe->selector = set_value(strvec);
-
- if (!mpe->selector)
- return 1;
-
- return 0;
-}
-
-static int
-mp_failback_handler(vector strvec)
-{
- struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
- char * buff;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
-
- if (strlen(buff) == 6 && !strcmp(buff, "manual"))
- mpe->pgfailback = -FAILBACK_MANUAL;
- else if (strlen(buff) == 9 && !strcmp(buff, "immediate"))
- mpe->pgfailback = -FAILBACK_IMMEDIATE;
- else if (strlen(buff) == 10 && !strcmp(buff, "followover"))
- mpe->pgfailback = -FAILBACK_FOLLOWOVER;
- else
- mpe->pgfailback = atoi(buff);
-
- FREE(buff);
-
- return 0;
-}
-
-static int
-mp_mode_handler(vector strvec)
-{
- mode_t mode;
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
- char *buff;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
- if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) {
- mpe->attribute_flags |= (1 << ATTR_MODE);
- mpe->mode = mode;
- }
-
- FREE(buff);
- return 0;
-}
-
-static int
-mp_uid_handler(vector strvec)
-{
- uid_t uid;
- char *buff;
- char passwd_buf[1024];
- struct passwd info, *found;
-
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) {
- mpe->attribute_flags |= (1 << ATTR_UID);
- mpe->uid = info.pw_uid;
- }
- else if (sscanf(buff, "%u", &uid) == 1){
- mpe->attribute_flags |= (1 << ATTR_UID);
- mpe->uid = uid;
- }
- FREE(buff);
- return 0;
-}
-
-static int
-mp_gid_handler(vector strvec)
-{
- gid_t gid;
- char *buff;
- char passwd_buf[1024];
- struct passwd info, *found;
-
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) {
- mpe->attribute_flags |= (1 << ATTR_GID);
- mpe->gid = info.pw_gid;
- }
- else if (sscanf(buff, "%u", &gid) == 1) {
- mpe->attribute_flags |= (1 << ATTR_GID);
- mpe->gid = gid;
- }
- FREE(buff);
- return 0;
-}
-
-static int
-mp_weight_handler(vector strvec)
-{
- struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
- char * buff;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- if (strlen(buff) == 10 &&
- !strcmp(buff, "priorities"))
- mpe->rr_weight = RR_WEIGHT_PRIO;
-
- if (strlen(buff) == strlen("uniform") &&
- !strcmp(buff, "uniform"))
- mpe->rr_weight = RR_WEIGHT_NONE;
-
- FREE(buff);
-
- return 0;
-}
-
-static int
-mp_no_path_retry_handler(vector strvec)
-{
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
- char *buff;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 4 && !strcmp(buff, "fail")) ||
- (strlen(buff) == 1 && !strcmp(buff, "0")))
- mpe->no_path_retry = NO_PATH_RETRY_FAIL;
- else if (strlen(buff) == 5 && !strcmp(buff, "queue"))
- mpe->no_path_retry = NO_PATH_RETRY_QUEUE;
- else if ((mpe->no_path_retry = atoi(buff)) < 1)
- mpe->no_path_retry = NO_PATH_RETRY_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-static int
-mp_minio_handler(vector strvec)
-{
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
- char * buff;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- mpe->minio = atoi(buff);
- FREE(buff);
-
- return 0;
-}
-
-static int
-mp_minio_rq_handler(vector strvec)
-{
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
- char * buff;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- mpe->minio_rq = atoi(buff);
- FREE(buff);
-
- return 0;
-}
-
-static int
-mp_pg_timeout_handler(vector strvec)
-{
- char *buff;
-
- buff = set_value(strvec);
-
- if (!buff)
- return 1;
-
- /* Deprecated; device-mapper support has been removed */
-
- FREE(buff);
- return 0;
-}
-
-static int
-mp_features_handler(vector strvec)
-{
- struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- mpe->features = set_value(strvec);
-
- if (!mpe->features)
- return 1;
-
- return 0;
-}
-
-static int
-mp_flush_on_last_del_handler(vector strvec)
-{
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
- char * buff;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "0") == 0))
- mpe->flush_on_last_del = FLUSH_DISABLED;
- else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "1") == 0))
- mpe->flush_on_last_del = FLUSH_ENABLED;
- else
- mpe->flush_on_last_del = FLUSH_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-static int
-mp_prio_handler(vector strvec)
-{
- struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- mpe->prio_name = set_value(strvec);
-
- if (!mpe->prio_name)
- return 1;
-
- return 0;
-}
-
-static int
-mp_prio_args_handler (vector strvec)
-{
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- if (!mpe)
- return 1;
-
- mpe->prio_args = set_value(strvec);
- if (!mpe->prio_args)
- return 1;
-
- return 0;
-}
-
-static int
-mp_reservation_key_handler (vector strvec)
-{
- char *buff;
- char *tbuff;
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
-
- int j, k, len;
- uint64_t prkey;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- tbuff = buff;
- if (!memcmp(buff, "0x", 2))
- buff = buff + 2;
-
- len = strlen(buff);
-
- k = strspn(buff, "0123456789aAbBcCdDeEfF");
- if (len != k) {
- FREE(tbuff);
- return 1;
- }
-
- if (1 != sscanf (buff, "%" SCNx64 "", &prkey))
- {
- FREE(tbuff);
- return 1;
- }
-
- if (!mpe->reservation_key)
- mpe->reservation_key = (unsigned char *) malloc(8);
-
- memset(mpe->reservation_key, 0, 8);
-
- for (j = 7; j >= 0; --j) {
- mpe->reservation_key[j] = (prkey & 0xff);
- prkey >>= 8;
- }
-
- FREE(tbuff);
- return 0;
-}
-
-static int
-mp_names_handler(vector strvec)
-{
- struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
- char * buff;
-
- if (!mpe)
- return 1;
-
- buff = set_value(strvec);
- if (!buff)
- return 1;
-
- if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "0") == 0))
- mpe->user_friendly_names = USER_FRIENDLY_NAMES_OFF;
- else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
- (strlen(buff) == 1 && strcmp(buff, "1") == 0))
- mpe->user_friendly_names = USER_FRIENDLY_NAMES_ON;
- else
- mpe->user_friendly_names = USER_FRIENDLY_NAMES_UNDEF;
-
- FREE(buff);
- return 0;
-}
-
-/*
- * config file keywords printing
- */
-static int
-snprint_mp_wwid (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- return snprintf(buff, len, "%s", mpe->wwid);
-}
-
-static int
-snprint_mp_alias (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->alias)
- return 0;
-
- return snprintf(buff, len, "%s", mpe->alias);
-}
-
-static int
-snprint_mp_path_grouping_policy (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
- char str[POLICY_NAME_SIZE];
-
- if (!mpe->pgpolicy)
- return 0;
- get_pgpolicy_name(str, POLICY_NAME_SIZE, mpe->pgpolicy);
-
- return snprintf(buff, len, "\"%s\"", str);
-}
-
-static int
-snprint_mp_selector (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->selector)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", mpe->selector);
-}
-
-static int
-snprint_mp_failback (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (mpe->pgfailback == FAILBACK_UNDEF ||
- mpe->pgfailback == DEFAULT_FAILBACK)
- return 0;
-
- switch(mpe->pgfailback) {
- case -FAILBACK_MANUAL:
- return snprintf(buff, len, "\"manual\"");
- case -FAILBACK_IMMEDIATE:
- return snprintf(buff, len, "\"immediate\"");
- case -FAILBACK_FOLLOWOVER:
- return snprintf(buff, len, "\"followover\"");
- default:
- return snprintf(buff, len, "%i", mpe->pgfailback);
- }
- return 0;
-}
-
-static int
-snprint_mp_mode(char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if ((mpe->attribute_flags & (1 << ATTR_MODE)) == 0)
- return 0;
- return snprintf(buff, len, "0%o", mpe->mode);
-}
-
-static int
-snprint_mp_uid(char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if ((mpe->attribute_flags & (1 << ATTR_UID)) == 0)
- return 0;
- return snprintf(buff, len, "0%o", mpe->uid);
-}
-
-static int
-snprint_mp_gid(char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if ((mpe->attribute_flags & (1 << ATTR_GID)) == 0)
- return 0;
- return snprintf(buff, len, "0%o", mpe->gid);
-}
-
-static int
-snprint_mp_rr_weight (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->rr_weight)
- return 0;
- if (mpe->rr_weight == RR_WEIGHT_PRIO)
- return snprintf(buff, len, "\"priorities\"");
- if (mpe->rr_weight == RR_WEIGHT_NONE)
- return snprintf(buff, len, "\"uniform\"");
-
- return 0;
-}
-
-static int
-snprint_mp_no_path_retry (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->no_path_retry)
- return 0;
-
- switch(mpe->no_path_retry) {
- case NO_PATH_RETRY_UNDEF:
- break;
- case NO_PATH_RETRY_FAIL:
- return snprintf(buff, len, "\"fail\"");
- case NO_PATH_RETRY_QUEUE:
- return snprintf(buff, len, "\"queue\"");
- default:
- return snprintf(buff, len, "%i",
- mpe->no_path_retry);
- }
- return 0;
-}
-
-static int
-snprint_mp_rr_min_io (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->minio)
- return 0;
-
- return snprintf(buff, len, "%u", mpe->minio);
-}
-
-static int
-snprint_mp_rr_min_io_rq (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->minio_rq)
- return 0;
-
- return snprintf(buff, len, "%u", mpe->minio_rq);
-}
-
-static int
-snprint_mp_pg_timeout (char * buff, int len, void * data)
-{
- return 0;
-}
-
-static int
-snprint_mp_features (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->features)
- return 0;
- if (strlen(mpe->features) == strlen(conf->features) &&
- !strcmp(mpe->features, conf->features))
- return 0;
-
- return snprintf(buff, len, "\"%s\"", mpe->features);
-}
-
-static int
-snprint_mp_flush_on_last_del (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- switch (mpe->flush_on_last_del) {
- case FLUSH_DISABLED:
- return snprintf(buff, len, "\"no\"");
- case FLUSH_ENABLED:
- return snprintf(buff, len, "\"yes\"");
- }
- return 0;
-}
-
-static int
-snprint_mp_prio(char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->prio_name)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", mpe->prio_name);
-}
-
-static int
-snprint_mp_prio_args(char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->prio_args)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", mpe->prio_args);
-}
-
-static int
-snprint_mp_reservation_key (char * buff, int len, void * data)
-{
- int i;
- unsigned char *keyp;
- uint64_t prkey = 0;
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (!mpe->reservation_key)
- return 0;
- keyp = (unsigned char *)mpe->reservation_key;
- for (i = 0; i < 8; i++) {
- if (i > 0)
- prkey <<= 8;
- prkey |= *keyp;
- keyp++;
- }
-
- return snprintf(buff, len, "0x%" PRIx64, prkey);
-}
-
- static int
-snprint_mp_user_friendly_names (char * buff, int len, void * data)
-{
- struct mpentry * mpe = (struct mpentry *)data;
-
- if (mpe->user_friendly_names == USER_FRIENDLY_NAMES_UNDEF)
- return 0;
- else if (mpe->user_friendly_names == USER_FRIENDLY_NAMES_OFF)
- return snprintf(buff, len, "\"no\"");
- else
- return snprintf(buff, len, "\"yes\"");
-}
-
-static int
-snprint_hw_fast_io_fail(char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
- if (hwe->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
- return 0;
- if (hwe->fast_io_fail == conf->fast_io_fail)
- return 0;
- if (hwe->fast_io_fail == MP_FAST_IO_FAIL_OFF)
- return snprintf(buff, len, "\"off\"");
- if (hwe->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
- return snprintf(buff, len, "0");
- return snprintf(buff, len, "%d", hwe->fast_io_fail);
-}
-
-static int
-snprint_hw_dev_loss(char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
- if (!hwe->dev_loss)
- return 0;
- if (hwe->dev_loss == conf->dev_loss)
- return 0;
- if (hwe->dev_loss >= MAX_DEV_LOSS_TMO)
- return snprintf(buff, len, "\"infinity\"");
-
- return snprintf(buff, len, "%u", hwe->dev_loss);
-}
-
-static int
-snprint_hw_vendor (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->vendor)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->vendor);
-}
-
-static int
-snprint_hw_product (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->product)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->product);
-}
-
-static int
-snprint_hw_revision (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->revision)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->revision);
-}
-
-static int
-snprint_hw_bl_product (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->bl_product)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->bl_product);
-}
-
-static int
-snprint_hw_uid_attribute (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->uid_attribute)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->uid_attribute);
-}
-
-static int
-snprint_hw_getuid_callout (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->getuid)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->getuid);
-}
-
-static int
-snprint_hw_prio (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->prio_name)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->prio_name);
-}
-
-static int
-snprint_hw_alias_prefix (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->alias_prefix)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->alias_prefix);
-}
-
-static int
-snprint_hw_prio_args (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->prio_args)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->prio_args);
-}
-
-static int
-snprint_hw_features (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->features)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->features);
-}
-
-static int
-snprint_hw_hardware_handler (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->hwhandler)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->hwhandler);
-}
-
-static int
-snprint_hw_selector (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->selector)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->selector);
-}
-
-static int
-snprint_hw_path_grouping_policy (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- char str[POLICY_NAME_SIZE];
-
- if (!hwe->pgpolicy)
- return 0;
-
- get_pgpolicy_name(str, POLICY_NAME_SIZE, hwe->pgpolicy);
-
- return snprintf(buff, len, "\"%s\"", str);
-}
-
-static int
-snprint_hw_failback (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (hwe->pgfailback == FAILBACK_UNDEF ||
- hwe->pgfailback == DEFAULT_FAILBACK)
- return 0;
-
- switch(hwe->pgfailback) {
- case -FAILBACK_MANUAL:
- return snprintf(buff, len, "\"manual\"");
- case -FAILBACK_IMMEDIATE:
- return snprintf(buff, len, "\"immediate\"");
- case -FAILBACK_FOLLOWOVER:
- return snprintf(buff, len, "\"followover\"");
- default:
- return snprintf(buff, len, "%i", hwe->pgfailback);
- }
- return 0;
-}
-
-static int
-snprint_hw_rr_weight (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->rr_weight)
- return 0;
- if (hwe->rr_weight == RR_WEIGHT_PRIO)
- return snprintf(buff, len, "\"priorities\"");
- if (hwe->rr_weight == RR_WEIGHT_NONE)
- return snprintf(buff, len, "\"uniform\"");
-
- return 0;
-}
-
-static int
-snprint_hw_no_path_retry (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->no_path_retry)
- return 0;
-
- switch(hwe->no_path_retry) {
- case NO_PATH_RETRY_UNDEF:
- break;
- case NO_PATH_RETRY_FAIL:
- return snprintf(buff, len, "\"fail\"");
- case NO_PATH_RETRY_QUEUE:
- return snprintf(buff, len, "\"queue\"");
- default:
- return snprintf(buff, len, "%i",
- hwe->no_path_retry);
- }
- return 0;
-}
-
-static int
-snprint_hw_rr_min_io (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->minio)
- return 0;
-
- return snprintf(buff, len, "%u", hwe->minio);
-}
-
-static int
-snprint_hw_rr_min_io_rq (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->minio_rq)
- return 0;
-
- return snprintf(buff, len, "%u", hwe->minio_rq);
-}
-
-static int
-snprint_hw_pg_timeout (char * buff, int len, void * data)
-{
- return 0;
-}
-
-static int
-snprint_hw_flush_on_last_del (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- switch (hwe->flush_on_last_del) {
- case FLUSH_DISABLED:
- return snprintf(buff, len, "\"no\"");
- case FLUSH_ENABLED:
- return snprintf(buff, len, "\"yes\"");
- }
- return 0;
-}
-
-static int
-snprint_hw_path_checker (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (!hwe->checker_name)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", hwe->checker_name);
-}
-
- static int
-snprint_hw_user_friendly_names (char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (hwe->user_friendly_names == USER_FRIENDLY_NAMES_UNDEF)
- return 0;
- else if (hwe->user_friendly_names == USER_FRIENDLY_NAMES_OFF)
- return snprintf(buff, len, "\"no\"");
- else
- return snprintf(buff, len, "\"yes\"");
-}
-
-static int
-snprint_hw_retain_hwhandler_handler(char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (hwe->retain_hwhandler == RETAIN_HWHANDLER_ON)
- return snprintf(buff, len, "\"yes\"");
- else if (hwe->retain_hwhandler == RETAIN_HWHANDLER_OFF)
- return snprintf(buff, len, "\"no\"");
- else
- return 0;
-}
-
-static int
-snprint_detect_prio(char * buff, int len, void * data)
-{
- struct hwentry * hwe = (struct hwentry *)data;
-
- if (hwe->detect_prio == DETECT_PRIO_ON)
- return snprintf(buff, len, "\"yes\"");
- else if (hwe->detect_prio == DETECT_PRIO_OFF)
- return snprintf(buff, len, "\"no\"");
- else
- return 0;
-}
-
-static int
-snprint_def_polling_interval (char * buff, int len, void * data)
-{
- return snprintf(buff, len, "%i", conf->checkint);
-}
-
-static int
-snprint_def_fast_io_fail(char * buff, int len, void * data)
-{
- if (conf->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
- return 0;
- if (conf->fast_io_fail == MP_FAST_IO_FAIL_OFF)
- return snprintf(buff, len, "\"off\"");
- if (conf->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
- return snprintf(buff, len, "0");
- return snprintf(buff, len, "%d", conf->fast_io_fail);
-}
-
-static int
-snprint_def_dev_loss(char * buff, int len, void * data)
-{
- if (!conf->dev_loss)
- return 0;
- if (conf->dev_loss >= MAX_DEV_LOSS_TMO)
- return snprintf(buff, len, "\"infinity\"");
- return snprintf(buff, len, "%u", conf->dev_loss);
-}
-
-static int
-snprint_def_verbosity (char * buff, int len, void * data)
-{
- return snprintf(buff, len, "%i", conf->verbosity);
-}
-
-static int
-snprint_def_max_polling_interval (char * buff, int len, void * data)
-{
- return snprintf(buff, len, "%i", conf->max_checkint);
-}
-
-static int
-snprint_reassign_maps (char * buff, int len, void * data)
-{
- return snprintf(buff, len, "\"%s\"",
- conf->reassign_maps?"yes":"no");
-}
-
-static int
-snprint_def_multipath_dir (char * buff, int len, void * data)
-{
- if (!conf->multipath_dir)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", conf->multipath_dir);
-}
-
-static int
-snprint_def_selector (char * buff, int len, void * data)
-{
- if (!conf->selector)
- return snprintf(buff, len, "\"%s\"", DEFAULT_SELECTOR);
-
- return snprintf(buff, len, "\"%s\"", conf->selector);
-}
-
-static int
-snprint_def_path_grouping_policy (char * buff, int len, void * data)
-{
- char str[POLICY_NAME_SIZE];
- int pgpolicy = conf->pgpolicy;
-
- if (!pgpolicy)
- pgpolicy = DEFAULT_PGPOLICY;
-
- get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy);
-
- return snprintf(buff, len, "\"%s\"", str);
-}
-
-static int
-snprint_def_uid_attribute (char * buff, int len, void * data)
-{
- if (!conf->uid_attribute)
- return snprintf(buff, len, "\"%s\"", DEFAULT_UID_ATTRIBUTE);
-
- return snprintf(buff, len, "\"%s\"", conf->uid_attribute);
-}
-
-static int
-snprint_def_getuid_callout (char * buff, int len, void * data)
-{
- if (!conf->getuid)
- return 0;
-
- return snprintf(buff, len, "\"%s\"", conf->getuid);
-}
-
-static int
-snprint_def_prio (char * buff, int len, void * data)
-{
- if (!conf->prio_name)
- return snprintf(buff, len, "\"%s\"", DEFAULT_PRIO);
-
- return snprintf(buff, len, "\"%s\"", conf->prio_name);
-}
-
-static int
-snprint_def_prio_args (char * buff, int len, void * data)
-{
- if (!conf->prio_args)
- return snprintf(buff, len, "\"%s\"", DEFAULT_PRIO_ARGS);
-
- return snprintf(buff, len, "\"%s\"", conf->prio_args);
-}
-
-static int
-snprint_def_features (char * buff, int len, void * data)
-{
- if (!conf->features)
- return snprintf(buff, len, "\"%s\"", DEFAULT_FEATURES);
-
- return snprintf(buff, len, "\"%s\"", conf->features);
-}
-
-static int
-snprint_def_path_checker (char * buff, int len, void * data)
-{
- if (!conf->checker_name)
- return snprintf(buff, len, "\"%s\"", DEFAULT_CHECKER);
-
- return snprintf(buff, len, "\"%s\"", conf->checker_name);
-}
-
-static int
-snprint_def_failback (char * buff, int len, void * data)
-{
- switch(conf->pgfailback) {
- case FAILBACK_UNDEF:
- return snprintf(buff, len, "\"undef\"");
- case -FAILBACK_MANUAL:
- return snprintf(buff, len, "\"manual\"");
- case -FAILBACK_IMMEDIATE:
- return snprintf(buff, len, "\"immediate\"");
- case -FAILBACK_FOLLOWOVER:
- return snprintf(buff, len, "\"followover\"");
- default:
- return snprintf(buff, len, "%i", conf->pgfailback);
- }
- return 0;
-}
-
-static int
-snprint_def_rr_min_io (char * buff, int len, void * data)
-{
- if (!conf->minio)
- return 0;
-
- return snprintf(buff, len, "%u", conf->minio);
-}
-
-static int
-snprint_def_rr_min_io_rq (char * buff, int len, void * data)
-{
- if (!conf->minio_rq)
- return 0;
-
- return snprintf(buff, len, "%u", conf->minio_rq);
-}
-
-static int
-snprint_max_fds (char * buff, int len, void * data)
-{
- int r = 0, max_fds;
-
- if (!conf->max_fds)
- return 0;
-
- r = get_sys_max_fds(&max_fds);
- if (!r && conf->max_fds >= max_fds)
- return snprintf(buff, len, "\"max\"");
- else
- return snprintf(buff, len, "%d", conf->max_fds);
-}
-
-static int
-snprint_def_mode(char * buff, int len, void * data)
-{
- if ((conf->attribute_flags & (1 << ATTR_MODE)) == 0)
- return 0;
- return snprintf(buff, len, "0%o", conf->mode);
-}
-
-static int
-snprint_def_uid(char * buff, int len, void * data)
-{
- if ((conf->attribute_flags & (1 << ATTR_UID)) == 0)
- return 0;
- return snprintf(buff, len, "0%o", conf->uid);
-}
-
-static int
-snprint_def_gid(char * buff, int len, void * data)
-{
- if ((conf->attribute_flags & (1 << ATTR_GID)) == 0)
- return 0;
- return snprintf(buff, len, "0%o", conf->gid);
-}
-
-static int
-snprint_def_rr_weight (char * buff, int len, void * data)
-{
- if (!conf->rr_weight || conf->rr_weight == RR_WEIGHT_NONE)
- return snprintf(buff, len, "\"uniform\"");
- if (conf->rr_weight == RR_WEIGHT_PRIO)
- return snprintf(buff, len, "\"priorities\"");
-
- return 0;
-}
-
-static int
-snprint_def_no_path_retry (char * buff, int len, void * data)
-{
- switch(conf->no_path_retry) {
- case NO_PATH_RETRY_UNDEF:
- break;
- case NO_PATH_RETRY_FAIL:
- return snprintf(buff, len, "\"fail\"");
- case NO_PATH_RETRY_QUEUE:
- return snprintf(buff, len, "\"queue\"");
- default:
- return snprintf(buff, len, "%i",
- conf->no_path_retry);
- }
- return 0;
-}
-
-static int
-snprint_def_queue_without_daemon (char * buff, int len, void * data)
-{
- switch (conf->queue_without_daemon) {
- case QUE_NO_DAEMON_OFF:
- return snprintf(buff, len, "\"no\"");
- case QUE_NO_DAEMON_ON:
- return snprintf(buff, len, "\"yes\"");
- case QUE_NO_DAEMON_FORCE:
- return snprintf(buff, len, "\"forced\"");
- }
- return 0;
-}
-
-static int
-snprint_def_checker_timeout (char *buff, int len, void *data)
-{
- if (!conf->checker_timeout)
- return 0;
-
- return snprintf(buff, len, "%u", conf->checker_timeout);
-}
-
-static int
-snprint_def_pg_timeout (char * buff, int len, void * data)
-{
- return 0;
-}
-
-static int
-snprint_def_flush_on_last_del (char * buff, int len, void * data)
-{
- switch (conf->flush_on_last_del) {
- case FLUSH_UNDEF:
- case FLUSH_DISABLED:
- return snprintf(buff, len, "\"no\"");
- case FLUSH_ENABLED:
- case FLUSH_IN_PROGRESS:
- return snprintf(buff, len, "\"yes\"");
- }
return 0;
}
-static int
-snprint_def_log_checker_err (char * buff, int len, void * data)
-{
- if (conf->log_checker_err == LOG_CHKR_ERR_ONCE)
- return snprintf(buff, len, "once");
- return snprintf(buff, len, "always");
-}
-
-static int
-snprint_def_user_friendly_names (char * buff, int len, void * data)
-{
- if (conf->user_friendly_names == USER_FRIENDLY_NAMES_ON)
- return snprintf(buff, len, "\"yes\"");
- else
- return snprintf(buff, len, "\"no\"");
-}
-
-static int
-snprint_def_alias_prefix (char * buff, int len, void * data)
-{
- if (!conf->alias_prefix)
- return snprintf(buff, len, "\"%s\"", DEFAULT_ALIAS_PREFIX);
- return snprintf(buff, len, "\"%s\"", conf->alias_prefix);
-}
-
-static int
-snprint_def_bindings_file (char * buff, int len, void * data)
-{
- if (conf->bindings_file == NULL)
- return 0;
- return snprintf(buff, len, "\"%s\"", conf->bindings_file);
-}
-
-static int
-snprint_def_wwids_file (char * buff, int len, void * data)
-{
- if (conf->wwids_file == NULL)
- return 0;
- return snprintf(buff, len, "%s", conf->wwids_file);
-}
-
-static int
-snprint_def_reservation_key(char * buff, int len, void * data)
-{
- int i;
- unsigned char *keyp;
- uint64_t prkey = 0;
-
- if (!conf->reservation_key)
- return 0;
- keyp = (unsigned char *)conf->reservation_key;
- for (i = 0; i < 8; i++) {
- if (i > 0)
- prkey <<= 8;
- prkey |= *keyp;
- keyp++;
- }
- return snprintf(buff, len, "0x%" PRIx64, prkey);
-}
-
-static int
-snprint_def_retain_hwhandler_handler(char * buff, int len, void * data)
-{
- if (conf->retain_hwhandler == RETAIN_HWHANDLER_ON)
- return snprintf(buff, len, "yes");
- else
- return snprintf(buff, len, "no");
-}
-
-static int
-snprint_def_detect_prio(char * buff, int len, void * data)
-{
- if (conf->detect_prio == DETECT_PRIO_ON)
- return snprintf(buff, len, "yes");
- else
- return snprintf(buff, len, "no");
-}
-
-static int
-snprint_ble_simple (char * buff, int len, void * data)
-{
- struct blentry * ble = (struct blentry *)data;
-
- return snprintf(buff, len, "\"%s\"", ble->str);
-}
-
-static int
-snprint_bled_vendor (char * buff, int len, void * data)
-{
- struct blentry_device * bled = (struct blentry_device *)data;
-
- return snprintf(buff, len, "\"%s\"", bled->vendor);
-}
-
-static int
-snprint_bled_product (char * buff, int len, void * data)
-{
- struct blentry_device * bled = (struct blentry_device *)data;
-
- return snprintf(buff, len, "\"%s\"", bled->product);
-}
-
#define __deprecated
void
init_keywords(void)
{
install_keyword_root("defaults", NULL);
- install_keyword("verbosity", &verbosity_handler, &snprint_def_verbosity);
- install_keyword("polling_interval", &polling_interval_handler, &snprint_def_polling_interval);
- install_keyword("max_polling_interval", &max_polling_interval_handler, &snprint_def_max_polling_interval);
- install_keyword("reassign_maps", &reassign_maps_handler, &snprint_reassign_maps);
- install_keyword("multipath_dir", &multipath_dir_handler, &snprint_def_multipath_dir);
+ install_keyword("verbosity", &def_verbosity_handler, &snprint_def_verbosity);
+ install_keyword("polling_interval", &def_checkint_handler, &snprint_def_checkint);
+ install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint);
+ install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps);
+ install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);
install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
- install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_path_grouping_policy);
+ install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy);
install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);
- install_keyword("getuid_callout", &def_getuid_callout_handler, &snprint_def_getuid_callout);
- install_keyword("prio", &def_prio_handler, &snprint_def_prio);
+ install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid);
+ install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name);
install_keyword("prio_args", &def_prio_args_handler, &snprint_def_prio_args);
install_keyword("features", &def_features_handler, &snprint_def_features);
- install_keyword("path_checker", &def_path_checker_handler, &snprint_def_path_checker);
- install_keyword("checker", &def_path_checker_handler, NULL);
+ install_keyword("path_checker", &def_checker_name_handler, &snprint_def_checker_name);
+ install_keyword("checker", &def_checker_name_handler, NULL);
install_keyword("alias_prefix", &def_alias_prefix_handler, &snprint_def_alias_prefix);
- install_keyword("failback", &default_failback_handler, &snprint_def_failback);
- install_keyword("rr_min_io", &def_minio_handler, &snprint_def_rr_min_io);
- install_keyword("rr_min_io_rq", &def_minio_rq_handler, &snprint_def_rr_min_io_rq);
+ install_keyword("failback", &def_pgfailback_handler, &snprint_def_pgfailback);
+ install_keyword("rr_min_io", &def_minio_handler, &snprint_def_minio);
+ install_keyword("rr_min_io_rq", &def_minio_rq_handler, &snprint_def_minio_rq);
install_keyword("max_fds", &max_fds_handler, &snprint_max_fds);
- install_keyword("rr_weight", &def_weight_handler, &snprint_def_rr_weight);
+ install_keyword("rr_weight", &def_rr_weight_handler, &snprint_def_rr_weight);
install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry);
- install_keyword("queue_without_daemon", &def_queue_without_daemon, &snprint_def_queue_without_daemon);
+ install_keyword("queue_without_daemon", &def_queue_without_daemon_handler, &snprint_def_queue_without_daemon);
install_keyword("checker_timeout", &def_checker_timeout_handler, &snprint_def_checker_timeout);
- install_keyword("pg_timeout", &def_pg_timeout_handler, &snprint_def_pg_timeout);
+ install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated);
install_keyword("flush_on_last_del", &def_flush_on_last_del_handler, &snprint_def_flush_on_last_del);
- install_keyword("user_friendly_names", &def_names_handler, &snprint_def_user_friendly_names);
+ install_keyword("user_friendly_names", &def_user_friendly_names_handler, &snprint_def_user_friendly_names);
install_keyword("mode", &def_mode_handler, &snprint_def_mode);
install_keyword("uid", &def_uid_handler, &snprint_def_uid);
install_keyword("gid", &def_gid_handler, &snprint_def_gid);
install_keyword("fast_io_fail_tmo", &def_fast_io_fail_handler, &snprint_def_fast_io_fail);
install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss);
- install_keyword("bindings_file", &bindings_file_handler, &snprint_def_bindings_file);
- install_keyword("wwids_file", &wwids_file_handler, &snprint_def_wwids_file);
+ install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file);
+ install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file);
install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err);
install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key);
- install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler_handler);
+ install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler);
install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio);
+ install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync);
+ install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove);
+ install_keyword("partition_delimiter", &def_partition_delim_handler, &snprint_def_partition_delim);
+ install_keyword("config_dir", &def_config_dir_handler, &snprint_def_config_dir);
+ install_keyword("delay_watch_checks", &def_delay_watch_checks_handler, &snprint_def_delay_watch_checks);
+ install_keyword("delay_wait_checks", &def_delay_wait_checks_handler, &snprint_def_delay_wait_checks);
+ install_keyword("find_multipaths", &def_find_multipaths_handler, &snprint_def_find_multipaths);
+ install_keyword("uxsock_timeout", &def_uxsock_timeout_handler, &snprint_def_uxsock_timeout);
+ install_keyword("retrigger_tries", &def_retrigger_tries_handler, &snprint_def_retrigger_tries);
+ install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay);
+ install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout);
__deprecated install_keyword("default_selector", &def_selector_handler, NULL);
__deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
__deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
- __deprecated install_keyword("default_getuid_callout", &def_getuid_callout_handler, NULL);
+ __deprecated install_keyword("default_getuid_callout", &def_getuid_handler, NULL);
__deprecated install_keyword("default_features", &def_features_handler, NULL);
- __deprecated install_keyword("default_path_checker", &def_path_checker_handler, NULL);
+ __deprecated install_keyword("default_path_checker", &def_checker_name_handler, NULL);
install_keyword_root("blacklist", &blacklist_handler);
- install_keyword_multi("devnode", &ble_devnode_handler, &snprint_ble_simple);
- install_keyword_multi("wwid", &ble_wwid_handler, &snprint_ble_simple);
- install_keyword_multi("property", &ble_property_handler, &snprint_ble_simple);
+ install_keyword_multi("devnode", &ble_blist_devnode_handler, &snprint_ble_simple);
+ install_keyword_multi("wwid", &ble_blist_wwid_handler, &snprint_ble_simple);
+ install_keyword_multi("property", &ble_blist_property_handler, &snprint_ble_simple);
install_keyword_multi("device", &ble_device_handler, NULL);
install_sublevel();
- install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor);
- install_keyword("product", &ble_product_handler, &snprint_bled_product);
+ install_keyword("vendor", &ble_blist_device_vendor_handler, &snprint_bled_vendor);
+ install_keyword("product", &ble_blist_device_product_handler, &snprint_bled_product);
install_sublevel_end();
install_keyword_root("blacklist_exceptions", &blacklist_exceptions_handler);
- install_keyword_multi("devnode", &ble_except_devnode_handler, &snprint_ble_simple);
- install_keyword_multi("wwid", &ble_except_wwid_handler, &snprint_ble_simple);
- install_keyword_multi("property", &ble_except_property_handler, &snprint_ble_simple);
+ install_keyword_multi("devnode", &ble_elist_devnode_handler, &snprint_ble_simple);
+ install_keyword_multi("wwid", &ble_elist_wwid_handler, &snprint_ble_simple);
+ install_keyword_multi("property", &ble_elist_property_handler, &snprint_ble_simple);
install_keyword_multi("device", &ble_except_device_handler, NULL);
install_sublevel();
- install_keyword("vendor", &ble_except_vendor_handler, &snprint_bled_vendor);
- install_keyword("product", &ble_except_product_handler, &snprint_bled_product);
+ install_keyword("vendor", &ble_elist_device_vendor_handler, &snprint_bled_vendor);
+ install_keyword("product", &ble_elist_device_product_handler, &snprint_bled_product);
install_sublevel_end();
#if 0
install_keyword_root("devices", &devices_handler);
install_keyword_multi("device", &device_handler, NULL);
install_sublevel();
- install_keyword("vendor", &vendor_handler, &snprint_hw_vendor);
- install_keyword("product", &product_handler, &snprint_hw_product);
- install_keyword("revision", &revision_handler, &snprint_hw_revision);
- install_keyword("product_blacklist", &bl_product_handler, &snprint_hw_bl_product);
- install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_path_grouping_policy);
+ install_keyword("vendor", &hw_vendor_handler, &snprint_hw_vendor);
+ install_keyword("product", &hw_product_handler, &snprint_hw_product);
+ install_keyword("revision", &hw_revision_handler, &snprint_hw_revision);
+ install_keyword("product_blacklist", &hw_bl_product_handler, &snprint_hw_bl_product);
+ install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_pgpolicy);
install_keyword("uid_attribute", &hw_uid_attribute_handler, &snprint_hw_uid_attribute);
- install_keyword("getuid_callout", &hw_getuid_callout_handler, &snprint_hw_getuid_callout);
+ install_keyword("getuid_callout", &hw_getuid_handler, &snprint_hw_getuid);
install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector);
- install_keyword("path_checker", &hw_path_checker_handler, &snprint_hw_path_checker);
- install_keyword("checker", &hw_path_checker_handler, NULL);
+ install_keyword("path_checker", &hw_checker_name_handler, &snprint_hw_checker_name);
+ install_keyword("checker", &hw_checker_name_handler, NULL);
install_keyword("alias_prefix", &hw_alias_prefix_handler, &snprint_hw_alias_prefix);
install_keyword("features", &hw_features_handler, &snprint_hw_features);
- install_keyword("hardware_handler", &hw_handler_handler, &snprint_hw_hardware_handler);
- install_keyword("prio", &hw_prio_handler, &snprint_hw_prio);
+ install_keyword("hardware_handler", &hw_hwhandler_handler, &snprint_hw_hwhandler);
+ install_keyword("prio", &hw_prio_name_handler, &snprint_hw_prio_name);
install_keyword("prio_args", &hw_prio_args_handler, &snprint_hw_prio_args);
- install_keyword("failback", &hw_failback_handler, &snprint_hw_failback);
- install_keyword("rr_weight", &hw_weight_handler, &snprint_hw_rr_weight);
+ install_keyword("failback", &hw_pgfailback_handler, &snprint_hw_pgfailback);
+ install_keyword("rr_weight", &hw_rr_weight_handler, &snprint_hw_rr_weight);
install_keyword("no_path_retry", &hw_no_path_retry_handler, &snprint_hw_no_path_retry);
- install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_rr_min_io);
- install_keyword("rr_min_io_rq", &hw_minio_rq_handler, &snprint_hw_rr_min_io_rq);
- install_keyword("pg_timeout", &hw_pg_timeout_handler, &snprint_hw_pg_timeout);
+ install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_minio);
+ install_keyword("rr_min_io_rq", &hw_minio_rq_handler, &snprint_hw_minio_rq);
+ install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated);
install_keyword("flush_on_last_del", &hw_flush_on_last_del_handler, &snprint_hw_flush_on_last_del);
install_keyword("fast_io_fail_tmo", &hw_fast_io_fail_handler, &snprint_hw_fast_io_fail);
install_keyword("dev_loss_tmo", &hw_dev_loss_handler, &snprint_hw_dev_loss);
- install_keyword("user_friendly_names", &hw_names_handler, &snprint_hw_user_friendly_names);
- install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler_handler);
- install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_detect_prio);
+ install_keyword("user_friendly_names", &hw_user_friendly_names_handler, &snprint_hw_user_friendly_names);
+ install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler);
+ install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_hw_detect_prio);
+ install_keyword("deferred_remove", &hw_deferred_remove_handler, &snprint_hw_deferred_remove);
+ install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks);
+ install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks);
install_sublevel_end();
+ install_keyword_root("overrides", &overrides_handler);
+ install_keyword("path_grouping_policy", &ovr_pgpolicy_handler, &snprint_ovr_pgpolicy);
+ install_keyword("uid_attribute", &ovr_uid_attribute_handler, &snprint_ovr_uid_attribute);
+ install_keyword("getuid_callout", &ovr_getuid_handler, &snprint_ovr_getuid);
+ install_keyword("path_selector", &ovr_selector_handler, &snprint_ovr_selector);
+ install_keyword("path_checker", &ovr_checker_name_handler, &snprint_ovr_checker_name);
+ install_keyword("checker", &ovr_checker_name_handler, NULL);
+ install_keyword("alias_prefix", &ovr_alias_prefix_handler, &snprint_ovr_alias_prefix);
+ install_keyword("features", &ovr_features_handler, &snprint_ovr_features);
+ install_keyword("prio", &ovr_prio_name_handler, &snprint_ovr_prio_name);
+ install_keyword("prio_args", &ovr_prio_args_handler, &snprint_ovr_prio_args);
+ install_keyword("failback", &ovr_pgfailback_handler, &snprint_ovr_pgfailback);
+ install_keyword("rr_weight", &ovr_rr_weight_handler, &snprint_ovr_rr_weight);
+ install_keyword("no_path_retry", &ovr_no_path_retry_handler, &snprint_ovr_no_path_retry);
+ install_keyword("rr_min_io", &ovr_minio_handler, &snprint_ovr_minio);
+ install_keyword("rr_min_io_rq", &ovr_minio_rq_handler, &snprint_ovr_minio_rq);
+ install_keyword("flush_on_last_del", &ovr_flush_on_last_del_handler, &snprint_ovr_flush_on_last_del);
+ install_keyword("fast_io_fail_tmo", &ovr_fast_io_fail_handler, &snprint_ovr_fast_io_fail);
+ install_keyword("dev_loss_tmo", &ovr_dev_loss_handler, &snprint_ovr_dev_loss);
+ install_keyword("user_friendly_names", &ovr_user_friendly_names_handler, &snprint_ovr_user_friendly_names);
+ install_keyword("retain_attached_hw_handler", &ovr_retain_hwhandler_handler, &snprint_ovr_retain_hwhandler);
+ install_keyword("detect_prio", &ovr_detect_prio_handler, &snprint_ovr_detect_prio);
+ install_keyword("deferred_remove", &ovr_deferred_remove_handler, &snprint_ovr_deferred_remove);
+ install_keyword("delay_watch_checks", &ovr_delay_watch_checks_handler, &snprint_ovr_delay_watch_checks);
+ install_keyword("delay_wait_checks", &ovr_delay_wait_checks_handler, &snprint_ovr_delay_wait_checks);
+
install_keyword_root("multipaths", &multipaths_handler);
install_keyword_multi("multipath", &multipath_handler, NULL);
install_sublevel();
- install_keyword("wwid", &wwid_handler, &snprint_mp_wwid);
- install_keyword("alias", &alias_handler, &snprint_mp_alias);
- install_keyword("path_grouping_policy", &mp_pgpolicy_handler, &snprint_mp_path_grouping_policy);
+ install_keyword("wwid", &mp_wwid_handler, &snprint_mp_wwid);
+ install_keyword("alias", &mp_alias_handler, &snprint_mp_alias);
+ install_keyword("path_grouping_policy", &mp_pgpolicy_handler, &snprint_mp_pgpolicy);
install_keyword("path_selector", &mp_selector_handler, &snprint_mp_selector);
- install_keyword("prio", &mp_prio_handler, &snprint_mp_prio);
+ install_keyword("prio", &mp_prio_name_handler, &snprint_mp_prio_name);
install_keyword("prio_args", &mp_prio_args_handler, &snprint_mp_prio_args);
- install_keyword("failback", &mp_failback_handler, &snprint_mp_failback);
- install_keyword("rr_weight", &mp_weight_handler, &snprint_mp_rr_weight);
+ install_keyword("failback", &mp_pgfailback_handler, &snprint_mp_pgfailback);
+ install_keyword("rr_weight", &mp_rr_weight_handler, &snprint_mp_rr_weight);
install_keyword("no_path_retry", &mp_no_path_retry_handler, &snprint_mp_no_path_retry);
- install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_rr_min_io);
- install_keyword("rr_min_io_rq", &mp_minio_rq_handler, &snprint_mp_rr_min_io_rq);
- install_keyword("pg_timeout", &mp_pg_timeout_handler, &snprint_mp_pg_timeout);
+ install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_minio);
+ install_keyword("rr_min_io_rq", &mp_minio_rq_handler, &snprint_mp_minio_rq);
+ install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated);
install_keyword("flush_on_last_del", &mp_flush_on_last_del_handler, &snprint_mp_flush_on_last_del);
install_keyword("features", &mp_features_handler, &snprint_mp_features);
install_keyword("mode", &mp_mode_handler, &snprint_mp_mode);
install_keyword("uid", &mp_uid_handler, &snprint_mp_uid);
install_keyword("gid", &mp_gid_handler, &snprint_mp_gid);
install_keyword("reservation_key", &mp_reservation_key_handler, &snprint_mp_reservation_key);
- install_keyword("user_friendly_names", &mp_names_handler, &snprint_mp_user_friendly_names);
+ install_keyword("user_friendly_names", &mp_user_friendly_names_handler, &snprint_mp_user_friendly_names);
+ install_keyword("deferred_remove", &mp_deferred_remove_handler, &snprint_mp_deferred_remove);
+ install_keyword("delay_watch_checks", &mp_delay_watch_checks_handler, &snprint_mp_delay_watch_checks);
+ install_keyword("delay_wait_checks", &mp_delay_wait_checks_handler, &snprint_mp_delay_wait_checks);
install_sublevel_end();
}
void init_keywords(void);
int get_sys_max_fds(int *);
+int print_rr_weight (char * buff, int len, void *ptr);
+int print_pgfailback (char * buff, int len, void *ptr);
+int print_pgpolicy(char * buff, int len, void *ptr);
+int print_no_path_retry(char * buff, int len, void *ptr);
+int print_fast_io_fail(char * buff, int len, void *ptr);
+int print_dev_loss(char * buff, int len, void *ptr);
+int print_reservation_key(char * buff, int len, void * ptr);
+int print_delay_checks(char * buff, int len, void *ptr);
#endif /* _DICT_H */
* Copyright (c) 2005 Mike Anderson
*/
#include <stdio.h>
+#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "prio.h"
#include "defaults.h"
+int
+alloc_path_with_pathinfo (vector hwtable, struct udev_device *udevice,
+ int flag, struct path **pp_ptr)
+{
+ int err = PATHINFO_FAILED;
+ struct path * pp;
+ const char * devname;
+
+ if (pp_ptr)
+ *pp_ptr = NULL;
+
+ devname = udev_device_get_sysname(udevice);
+ if (!devname)
+ return PATHINFO_FAILED;
+
+ pp = alloc_path();
+
+ if (!pp)
+ return PATHINFO_FAILED;
+
+ if (safe_sprintf(pp->dev, "%s", devname)) {
+ condlog(0, "pp->dev too small");
+ } else {
+ pp->udev = udev_device_ref(udevice);
+ err = pathinfo(pp, hwtable, flag | DI_BLACKLIST);
+ }
+
+ if (err)
+ free_path(pp);
+ else if (pp_ptr)
+ *pp_ptr = pp;
+ return err;
+}
+
int
store_pathinfo (vector pathvec, vector hwtable, struct udev_device *udevice,
int flag, struct path **pp_ptr)
{
- int err = 1;
+ int err = PATHINFO_FAILED;
struct path * pp;
const char * devname;
devname = udev_device_get_sysname(udevice);
if (!devname)
- return 1;
+ return PATHINFO_FAILED;
pp = alloc_path();
if (!pp)
- return 1;
+ return PATHINFO_FAILED;
if(safe_sprintf(pp->dev, "%s", devname)) {
condlog(0, "pp->dev too small");
}
pp->udev = udev_device_ref(udevice);
err = pathinfo(pp, hwtable,
- (conf->dry_run == 3)? flag : (flag | DI_BLACKLIST));
+ (conf->cmd == CMD_REMOVE_WWID)? flag :
+ (flag | DI_BLACKLIST));
if (err)
goto out;
devname = udev_device_get_sysname(udevice);
if (!devname)
- return 0;
+ return PATHINFO_FAILED;
if (filter_property(conf, udevice) > 0)
- return 0;
+ return PATHINFO_SKIPPED;
if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
(char *)devname) > 0)
- return 0;
+ return PATHINFO_SKIPPED;
pp = find_path_by_dev(pathvec, (char *)devname);
if (!pp) {
- if (store_pathinfo(pathvec, conf->hwtable,
- udevice, flag, NULL) != 1)
- return 0;
- else
- return 1;
+ return store_pathinfo(pathvec, conf->hwtable,
+ udevice, flag, NULL);
}
return pathinfo(pp, conf->hwtable, flag);
}
struct udev_list_entry *entry;
struct udev_device *udevice;
const char *devpath;
- int r = 0;
+ int num_paths = 0, total_paths = 0;
udev_iter = udev_enumerate_new(conf->udev);
if (!udev_iter)
- return 1;
+ return -ENOMEM;
udev_enumerate_add_match_subsystem(udev_iter, "block");
udev_enumerate_scan_devices(udev_iter);
udevice = udev_device_new_from_syspath(conf->udev, devpath);
if (!udevice) {
condlog(4, "%s: no udev information", devpath);
- r++;
continue;
}
devtype = udev_device_get_devtype(udevice);
if(devtype && !strncmp(devtype, "disk", 4)) {
- r += path_discover(pathvec, conf,
- udevice, flag);
+ total_paths++;
+ if (path_discover(pathvec, conf,
+ udevice, flag) == PATHINFO_OK)
+ num_paths++;
}
udev_device_unref(udevice);
}
udev_enumerate_unref(udev_iter);
- condlog(4, "Discovery status %d", r);
- return r;
+ condlog(4, "Discovered %d/%d paths", num_paths, total_paths);
+ return (total_paths - num_paths);
}
#define declare_sysfs_get_str(fname) \
extern ssize_t \
sysfs_get_##fname (struct udev_device * udev, char * buff, size_t len) \
{ \
+ int l; \
const char * attr; \
const char * devname; \
\
devname, #fname); \
return -ENXIO; \
} \
- if (strchop(attr) > len) { \
+ for (l = strlen(attr); l >= 1 && isspace(attr[l-1]); l--); \
+ if (l > len) { \
condlog(3, "%s: overflow in attribute %s", \
devname, #fname); \
return -EINVAL; \
} \
- return strlcpy(buff, attr, len); \
+ strlcpy(buff, attr, len); \
+ return strchop(buff); \
}
declare_sysfs_get_str(devtype);
declare_sysfs_get_str(vendor);
declare_sysfs_get_str(model);
declare_sysfs_get_str(rev);
-declare_sysfs_get_str(dev);
+
+ssize_t
+sysfs_get_vpd (struct udev_device * udev, int pg,
+ unsigned char * buff, size_t len)
+{
+ ssize_t attr_len;
+ char attrname[9];
+ const char * devname;
+
+ if (!udev) {
+ condlog(3, "No udev device given\n");
+ return -ENOSYS;
+ }
+
+ devname = udev_device_get_sysname(udev);
+ sprintf(attrname, "vpd_pg%02x", pg);
+ attr_len = sysfs_bin_attr_get_value(udev, attrname, buff, len);
+ if (attr_len < 0) {
+ condlog(3, "%s: attribute %s not found in sysfs",
+ devname, attrname);
+ return attr_len;
+ }
+ return attr_len;
+}
int
sysfs_get_timeout(struct path *pp, unsigned int *timeout)
}
}
+ /* Check for USB */
+ tgtdev = udev_device_get_parent(parent);
+ while (tgtdev) {
+ value = udev_device_get_subsystem(tgtdev);
+ if (value && !strcmp(value, "usb")) {
+ pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
+ tgtname = udev_device_get_sysname(tgtdev);
+ strncpy(node, tgtname, strlen(tgtname));
+ condlog(3, "%s: skip USB device %s", pp->dev, node);
+ return 1;
+ }
+ tgtdev = udev_device_get_parent(tgtdev);
+ }
parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_target");
if (!parent)
return 1;
if (tgtdev) {
const char *value;
- value = udev_device_get_sysattr_value(tgtdev, "tgtname");
+ value = udev_device_get_sysattr_value(tgtdev, "targetname");
if (value) {
pp->sg_id.proto_id = SCSI_PROTOCOL_ISCSI;
pp->sg_id.transport_id = tgtid;
snprintf(node, NODE_NAME_SIZE, "ata-%d.00", tgtid);
return 0;
}
+ /* Unknown SCSI transport. Keep fingers crossed */
pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
+ return 0;
+}
+
+int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name)
+{
+ int proto_id;
+
+ if (!pp || !adapter_name)
+ return 1;
+
+ proto_id = pp->sg_id.proto_id;
+
+ if (proto_id != SCSI_PROTOCOL_FCP &&
+ proto_id != SCSI_PROTOCOL_SAS &&
+ proto_id != SCSI_PROTOCOL_ISCSI &&
+ proto_id != SCSI_PROTOCOL_SRP) {
+ return 1;
+ }
+ /* iscsi doesn't have adapter info in sysfs
+ * get ip_address for grouping paths
+ */
+ if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI)
+ return sysfs_get_iscsi_ip_address(pp, adapter_name);
+
+ /* fetch adapter pci name for other protocols
+ */
+ return sysfs_get_host_pci_name(pp, adapter_name);
+}
+
+int sysfs_get_host_pci_name(struct path *pp, char *pci_name)
+{
+ struct udev_device *hostdev, *parent;
+ char host_name[HOST_NAME_LEN];
+ const char *driver_name, *value;
+
+ if (!pp || !pci_name)
+ return 1;
+
+ sprintf(host_name, "host%d", pp->sg_id.host_no);
+ hostdev = udev_device_new_from_subsystem_sysname(conf->udev,
+ "scsi_host", host_name);
+ if (!hostdev)
+ return 1;
+
+ parent = udev_device_get_parent(hostdev);
+ while (parent) {
+ driver_name = udev_device_get_driver(parent);
+ if (!driver_name) {
+ parent = udev_device_get_parent(parent);
+ continue;
+ }
+ if (!strcmp(driver_name, "pcieport"))
+ break;
+ parent = udev_device_get_parent(parent);
+ }
+ if (parent) {
+ /* pci_device found
+ */
+ value = udev_device_get_sysname(parent);
+
+ strncpy(pci_name, value, SLOT_NAME_SIZE);
+ udev_device_unref(hostdev);
+ return 0;
+ }
+ udev_device_unref(hostdev);
+ return 1;
+}
+
+int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address)
+{
+ struct udev_device *hostdev;
+ char host_name[HOST_NAME_LEN];
+ const char *value;
+
+ sprintf(host_name, "host%d", pp->sg_id.host_no);
+ hostdev = udev_device_new_from_subsystem_sysname(conf->udev,
+ "iscsi_host", host_name);
+ if (hostdev) {
+ value = udev_device_get_sysattr_value(hostdev,
+ "ipaddress");
+ if (value) {
+ strncpy(ip_address, value, SLOT_NAME_SIZE);
+ udev_device_unref(hostdev);
+ return 0;
+ } else
+ udev_device_unref(hostdev);
+ }
return 1;
}
return 1;
}
+#define DEFAULT_SGIO_LEN 254
+
+static int
+sgio_get_vpd (unsigned char * buff, int maxlen, int fd)
+{
+ int len = DEFAULT_SGIO_LEN;
+
+ if (fd < 0) {
+ errno = EBADF;
+ return -1;
+ }
+retry:
+ if (0 == do_inq(fd, 0, 1, 0x83, buff, len)) {
+ len = buff[3] + (buff[2] << 8);
+ if (len >= maxlen)
+ return len;
+ if (len > DEFAULT_SGIO_LEN)
+ goto retry;
+ return 0;
+ }
+ return -1;
+}
+
static int
get_geometry(struct path *pp)
{
return 0;
}
+static int
+parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len)
+{
+ char *p = NULL;
+ int len = in[3] + (in[2] << 8);
+
+ if (len >= out_len) {
+ condlog(2, "vpd pg80 overflow, %d/%d bytes required",
+ len, (int)out_len);
+ len = out_len;
+ }
+ if (len > 0) {
+ memcpy(out, in + 4, len);
+ out[len] = '\0';
+ }
+ /*
+ * Strip trailing whitspaces
+ */
+ p = out + len - 1;
+ while (p > out && *p == ' ') {
+ *p = '\0';
+ p--;
+ len --;
+ }
+ return len;
+}
+
+static int
+parse_vpd_pg83(const unsigned char *in, size_t in_len,
+ char *out, size_t out_len)
+{
+ unsigned char *d;
+ unsigned char *vpd = NULL;
+ int len = -ENODATA, vpd_type, vpd_len, prio = -1, i, naa_prio;
+
+ d = (unsigned char *)in + 4;
+ while (d < (unsigned char *)in + in_len) {
+ /* Select 'association: LUN' */
+ if ((d[1] & 0x30) != 0) {
+ d += d[3] + 4;
+ continue;
+ }
+ switch (d[1] & 0xf) {
+ case 0x3:
+ /* NAA: Prio 5 */
+ switch (d[4] >> 4) {
+ case 6:
+ /* IEEE Registered Extended: Prio 8 */
+ naa_prio = 8;
+ break;
+ case 5:
+ /* IEEE Registered: Prio 7 */
+ naa_prio = 7;
+ break;
+ case 2:
+ /* IEEE Extended: Prio 6 */
+ naa_prio = 6;
+ break;
+ case 3:
+ /* IEEE Locally assigned: Prio 1 */
+ naa_prio = 1;
+ break;
+ default:
+ /* Default: no priority */
+ naa_prio = -1;
+ break;
+ }
+ if (prio < naa_prio) {
+ prio = naa_prio;
+ vpd = d;
+ }
+ break;
+ case 0x8:
+ /* SCSI Name: Prio 4 */
+ if (memcmp(d + 4, "eui.", 4) &&
+ memcmp(d + 4, "naa.", 4) &&
+ memcmp(d + 4, "iqn.", 4))
+ continue;
+ if (prio < 4) {
+ prio = 4;
+ vpd = d;
+ }
+ break;
+ case 0x2:
+ /* EUI-64: Prio 3 */
+ if (prio < 3) {
+ prio = 3;
+ vpd = d;
+ }
+ break;
+ case 0x1:
+ /* T-10 Vendor ID: Prio 2 */
+ if (prio < 2) {
+ prio = 2;
+ vpd = d;
+ }
+ break;
+ }
+ d += d[3] + 4;
+ }
+ if (prio > 0) {
+ vpd_type = vpd[1] & 0xf;
+ vpd_len = vpd[3];
+ vpd += 4;
+ if (vpd_type == 0x2 || vpd_type == 0x3) {
+ int i;
+
+ len = sprintf(out, "%d", vpd_type);
+ for (i = 0; i < vpd_len; i++) {
+ len += sprintf(out + len,
+ "%02x", vpd[i]);
+ if (len >= out_len)
+ break;
+ }
+ } else if (vpd_type == 0x8) {
+ if (!memcmp("eui.", vpd, 4)) {
+ out[0] = '2';
+ len = 1;
+ vpd += 4;
+ vpd_len -= 4;
+ for (i = 0; i < vpd_len; i++) {
+ len += sprintf(out + len, "%c",
+ tolower(vpd[i]));
+ if (len >= out_len)
+ break;
+ }
+ len = vpd_len + 1;
+ out[len] = '\0';
+ } else if (!memcmp("naa.", vpd, 4)) {
+ out[0] = '3';
+ len = 1;
+ vpd += 4;
+ vpd_len -= 4;
+ for (i = 0; i < vpd_len; i++) {
+ len += sprintf(out + len, "%c",
+ tolower(vpd[i]));
+ if (len >= out_len)
+ break;
+ }
+ len = vpd_len + 1;
+ out[len] = '\0';
+ } else {
+ out[0] = '8';
+ len = 1;
+ vpd += 4;
+ vpd_len -= 4;
+ if (vpd_len > out_len + 2)
+ vpd_len = out_len - 2;
+ memcpy(out, vpd, vpd_len);
+ len = vpd_len + 1;
+ out[len] = '\0';
+ }
+ } else if (vpd_type == 0x1) {
+ unsigned char *p;
+ int p_len;
+
+ out[0] = '1';
+ len = 1;
+ p = vpd;
+ while ((p = memchr(vpd, ' ', vpd_len))) {
+ p_len = p - vpd;
+ if (len + p_len > out_len - 1)
+ p_len = out_len - len - 2;
+ memcpy(out + len, vpd, p_len);
+ len += p_len;
+ if (len >= out_len - 1) {
+ out[len] = '\0';
+ break;
+ }
+ out[len] = '_';
+ len ++;
+ vpd = p;
+ vpd_len -= p_len;
+ while (vpd && *vpd == ' ') {
+ vpd++;
+ vpd_len --;
+ }
+ }
+ if (len > 1 && out[len - 1] == '_') {
+ out[len - 1] = '\0';
+ len--;
+ }
+ }
+ }
+ return len;
+}
+
+static int
+get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen)
+{
+ int len, buff_len;
+ unsigned char buff[4096];
+
+ memset(buff, 0x0, 4096);
+ if (!parent || sysfs_get_vpd(parent, pg, buff, 4096) <= 0) {
+ condlog(3, "failed to read sysfs vpd pg%02x", pg);
+ return -EINVAL;
+ }
+
+ if (buff[1] != pg) {
+ condlog(3, "vpd pg%02x error, invalid vpd page %02x",
+ pg, buff[1]);
+ return -ENODATA;
+ }
+ buff_len = (buff[2] << 8) + buff[3] + 4;
+ if (buff_len > 4096)
+ condlog(3, "vpd pg%02x page truncated", pg);
+
+ if (pg == 0x80)
+ len = parse_vpd_pg80(buff, str, maxlen);
+ else if (pg == 0x83)
+ len = parse_vpd_pg83(buff, buff_len, str, maxlen);
+ else
+ len = -ENOSYS;
+
+ return len;
+}
+
+static int
+get_vpd_sgio (int fd, int pg, char * str, int maxlen)
+{
+ int len, buff_len;
+ unsigned char buff[4096];
+
+ memset(buff, 0x0, 4096);
+ if (sgio_get_vpd(buff, 4096, fd) <= 0) {
+ condlog(3, "failed to issue vpd inquiry for pg%02x",
+ pg);
+ return -errno;
+ }
+
+ if (buff[1] != pg) {
+ condlog(3, "vpd pg%02x error, invalid vpd page %02x",
+ pg, buff[1]);
+ return -ENODATA;
+ }
+ buff_len = (buff[2] << 8) + buff[3] + 4;
+ if (buff_len > 4096)
+ condlog(3, "vpd pg%02x page truncated", pg);
+
+ if (pg == 0x80)
+ len = parse_vpd_pg80(buff, str, maxlen);
+ else if (pg == 0x83)
+ len = parse_vpd_pg83(buff, buff_len, str, maxlen);
+ else
+ len = -ENOSYS;
+
+ return len;
+}
+
static int
scsi_sysfs_pathinfo (struct path * pp)
{
/*
* target node name
*/
- if(!sysfs_get_tgt_nodename(pp, pp->tgt_node_name)) {
- condlog(3, "%s: tgt_node_name = %s",
- pp->dev, pp->tgt_node_name);
- }
+ if(sysfs_get_tgt_nodename(pp, pp->tgt_node_name))
+ return 1;
+
+ condlog(3, "%s: tgt_node_name = %s",
+ pp->dev, pp->tgt_node_name);
return 0;
}
static int
common_sysfs_pathinfo (struct path * pp)
{
+ dev_t devt;
+
if (!pp)
return 1;
condlog(4, "%s: udev not initialised", pp->dev);
return 1;
}
- if (sysfs_get_dev(pp->udev, pp->dev_t, BLK_DEV_SIZE) <= 0) {
- condlog(3, "%s: no 'dev' attribute in sysfs", pp->dev);
- return 1;
- }
+ devt = udev_device_get_devnum(pp->udev);
+ snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt), minor(devt));
condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t);
condlog(3, "%s: path state = %s", pp->dev, buff);
- if (!strncmp(buff, "offline", 7) ||
- !strncmp(buff, "quiesce", 7) ||
- !strncmp(buff, "transport-offline", 17)) {
+ if (!strncmp(buff, "offline", 7)) {
pp->offline = 1;
return PATH_DOWN;
}
static int
scsi_ioctl_pathinfo (struct path * pp, int mask)
{
- if (mask & DI_SERIAL) {
- get_serial(pp->serial, SERIAL_SIZE, pp->fd);
- condlog(3, "%s: serial = %s", pp->dev, pp->serial);
+ struct udev_device *parent;
+ const char *attr_path = NULL;
+
+ if (!(mask & DI_SERIAL))
+ return 0;
+
+ parent = pp->udev;
+ while (parent) {
+ const char *subsys = udev_device_get_subsystem(parent);
+ if (subsys && !strncmp(subsys, "scsi", 4)) {
+ attr_path = udev_device_get_sysname(parent);
+ if (!attr_path)
+ break;
+ if (sscanf(attr_path, "%i:%i:%i:%i",
+ &pp->sg_id.host_no,
+ &pp->sg_id.channel,
+ &pp->sg_id.scsi_id,
+ &pp->sg_id.lun) == 4)
+ break;
+ }
+ parent = udev_device_get_parent(parent);
}
+ if (!attr_path || pp->sg_id.host_no == -1)
+ return 0;
+
+ if (get_vpd_sysfs(parent, 0x80, pp->serial, SERIAL_SIZE) > 0)
+ condlog(3, "%s: serial = %s",
+ pp->dev, pp->serial);
return 0;
}
if (!checker_selected(c)) {
if (daemon) {
- if (pathinfo(pp, conf->hwtable, DI_SYSFS) != 0) {
+ if (pathinfo(pp, conf->hwtable, DI_SYSFS) != PATHINFO_OK) {
condlog(3, "%s: couldn't get sysfs pathinfo",
pp->dev);
return PATH_UNCHECKED;
}
}
checker_clear_message(c);
- if (daemon)
- checker_set_async(c);
+ if (daemon) {
+ if (conf->force_sync == 0)
+ checker_set_async(c);
+ else
+ checker_set_sync(c);
+ }
if (!conf->checker_timeout &&
sysfs_get_timeout(pp, &(c->timeout)) <= 0)
c->timeout = DEF_TIMEOUT;
return 0;
}
+static int
+get_udev_uid(struct path * pp, char *uid_attribute)
+{
+ ssize_t len;
+ const char *value;
+
+ value = udev_device_get_property_value(pp->udev,
+ uid_attribute);
+ if ((!value || strlen(value) == 0) && conf->cmd == CMD_VALID_PATH)
+ value = getenv(uid_attribute);
+ if (value && strlen(value)) {
+ if (strlen(value) + 1 > WWID_SIZE) {
+ condlog(0, "%s: wwid overflow", pp->dev);
+ len = WWID_SIZE;
+ } else {
+ len = strlen(value);
+ }
+ strncpy(pp->wwid, value, len);
+ } else {
+ condlog(3, "%s: no %s attribute", pp->dev,
+ uid_attribute);
+ len = -EINVAL;
+ }
+ return len;
+}
+
+static int
+get_vpd_uid(struct path * pp)
+{
+ struct udev_device *parent = pp->udev;
+
+ while (parent) {
+ const char *subsys = udev_device_get_subsystem(parent);
+ if (subsys && !strncmp(subsys, "scsi", 4))
+ break;
+ parent = udev_device_get_parent(parent);
+ }
+
+ return get_vpd_sysfs(parent, 0x83, pp->wwid, WWID_SIZE);
+}
+
static int
get_uid (struct path * pp)
{
char *c;
- const char *origin;
+ const char *origin = "unknown";
+ ssize_t len = 0;
if (!pp->uid_attribute && !pp->getuid)
select_getuid(pp);
if (apply_format(pp->getuid, &buff[0], pp)) {
condlog(0, "error formatting uid callout command");
memset(pp->wwid, 0, WWID_SIZE);
+ len = -EINVAL;
} else if (execute_program(buff, pp->wwid, WWID_SIZE)) {
condlog(3, "error calling out %s", buff);
memset(pp->wwid, 0, WWID_SIZE);
- }
+ len = -EIO;
+ } else
+ len = strlen(pp->wwid);
origin = "callout";
} else {
- const char *value;
-
- 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;
+ if (pp->uid_attribute) {
+ len = get_udev_uid(pp, pp->uid_attribute);
+ origin = "udev";
+ if (len <= 0)
+ condlog(1,
+ "%s: failed to get udev uid: %s",
+ pp->dev, strerror(-len));
- if (strlen(value) + 1 > WWID_SIZE) {
- condlog(0, "%s: wwid overflow", pp->dev);
- } else {
- len = strlen(value);
+ }
+ if (len <= 0 && pp->retriggers >= conf->retrigger_tries &&
+ !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) {
+ len = get_vpd_uid(pp);
+ origin = "sysfs";
+ pp->uid_attribute = NULL;
+ if (len < 0) {
+ condlog(1, "%s: failed to get sysfs uid: %s",
+ pp->dev, strerror(-len));
+ len = get_vpd_sgio(pp->fd, 0x83, pp->wwid,
+ WWID_SIZE);
+ origin = "sgio";
}
- strncpy(pp->wwid, value, len);
- } else {
- condlog(3, "%s: no %s attribute", pp->dev,
- pp->uid_attribute);
}
- origin = "udev";
}
- /* Strip any trailing blanks */
- c = strchr(pp->wwid, '\0');
- c--;
- while (c && c >= pp->wwid && *c == ' ') {
- *c = '\0';
+ if ( len < 0 ) {
+ condlog(1, "%s: failed to get %s uid: %s",
+ pp->dev, origin, strerror(-len));
+ memset(pp->wwid, 0x0, WWID_SIZE);
+ } else {
+ /* Strip any trailing blanks */
+ c = strchr(pp->wwid, '\0');
c--;
+ while (c && c >= pp->wwid && *c == ' ') {
+ *c = '\0';
+ c--;
+ }
}
condlog(3, "%s: uid = %s (%s)", pp->dev,
*pp->wwid == '\0' ? "<empty>" : pp->wwid, origin);
int path_state;
if (!pp)
- return 1;
+ return PATHINFO_FAILED;
condlog(3, "%s: mask = 0x%x", pp->dev, mask);
* fetch info available in sysfs
*/
if (mask & DI_SYSFS && sysfs_pathinfo(pp))
- return 1;
+ return PATHINFO_FAILED;
if (mask & DI_BLACKLIST && mask & DI_SYSFS) {
if (filter_device(conf->blist_device, conf->elist_device,
pp->vendor_id, pp->product_id) > 0) {
- return 2;
+ return PATHINFO_SKIPPED;
}
}
goto blank;
if (pp->state == PATH_TIMEOUT)
pp->state = PATH_DOWN;
+ if (pp->state == PATH_UP && !pp->size) {
+ condlog(3, "%s: device size is 0, "
+ "path unuseable", pp->dev);
+ pp->state = PATH_GHOST;
+ }
} else {
condlog(3, "%s: path inaccessible", pp->dev);
pp->chkrstate = pp->state = path_state;
}
}
- if ((mask & DI_WWID) && !strlen(pp->wwid))
+ if ((mask & DI_WWID) && !strlen(pp->wwid)) {
get_uid(pp);
+ if (!strlen(pp->wwid)) {
+ pp->initialized = INIT_MISSING_UDEV;
+ pp->tick = conf->retrigger_delay;
+ return PATHINFO_OK;
+ }
+ else
+ pp->tick = 1;
+ }
+
if (mask & DI_BLACKLIST && mask & DI_WWID) {
if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
- pp->wwid) > 0) {
- return 2;
+ pp->wwid, pp->dev) > 0) {
+ return PATHINFO_SKIPPED;
}
}
* Retrieve path priority, even for PATH_DOWN paths if it has never
* been successfully obtained before.
*/
- if ((mask & DI_PRIO) && path_state == PATH_UP) {
+ if ((mask & DI_PRIO) && path_state == PATH_UP && strlen(pp->wwid)) {
if (pp->state != PATH_DOWN || pp->priority == PRIO_UNDEF) {
- if (!strlen(pp->wwid))
- get_uid(pp);
get_prio(pp);
}
}
- return 0;
+ pp->initialized = INIT_OK;
+ return PATHINFO_OK;
blank:
/*
*/
memset(pp->wwid, 0, WWID_SIZE);
pp->chkrstate = pp->state = PATH_DOWN;
+ pp->initialized = INIT_FAILED;
- return 0;
+ return PATHINFO_OK;
}
#define SCSI_COMMAND_TERMINATED 0x22
#define SG_ERR_DRIVER_SENSE 0x08
+#define PATHINFO_OK 0
+#define PATHINFO_FAILED 1
+#define PATHINFO_SKIPPED 2
+
struct config;
-ssize_t sysfs_get_dev (struct udev_device *udev, char * buff, size_t len);
int path_discovery (vector pathvec, struct config * conf, int flag);
int do_tur (char *);
int path_offline (struct path *);
int get_state (struct path * pp, int daemon);
int pathinfo (struct path *, vector hwtable, int mask);
+int alloc_path_with_pathinfo (vector hwtable, struct udev_device *udevice,
+ int flag, struct path **pp_ptr);
int store_pathinfo (vector pathvec, vector hwtable,
struct udev_device *udevice, int flag,
struct path **pp_ptr);
int sysfs_set_scsi_tmo (struct multipath *mpp);
int sysfs_get_timeout(struct path *pp, unsigned int *timeout);
+int sysfs_get_host_pci_name(struct path *pp, char *pci_name);
+int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address);
+ssize_t sysfs_get_vpd (struct udev_device * udev, int pg, unsigned char * buff,
+ size_t len);
/*
* discovery bitmask
static int
merge_words (char ** dst, char * word, int space)
{
- char * p;
+ char * p = *dst;
int len;
len = strlen(*dst) + strlen(word) + space;
*dst = REALLOC(*dst, len + 1);
- if (!*dst)
+ if (!*dst) {
+ free(p);
return 1;
+ }
p = *dst;
.checker_name = EMC_CLARIION,
.prio_name = PRIO_EMC,
.prio_args = NULL,
+ .retain_hwhandler = RETAIN_HWHANDLER_ON,
+ .detect_prio = DETECT_PRIO_ON,
},
{
.vendor = "EMC",
.prio_name = DEFAULT_PRIO,
.prio_args = NULL,
},
+ {
+ .vendor = "XtremIO",
+ .product = "XtremApp",
+ .features = DEFAULT_FEATURES,
+ .hwhandler = DEFAULT_HWHANDLER,
+ .selector = "queue-length 0",
+ .pgpolicy = MULTIBUS,
+ .pgfailback = FAILBACK_UNDEF,
+ .checker_name = TUR,
+ .fast_io_fail = 5,
+ .prio_name = DEFAULT_PRIO,
+ .prio_args = NULL,
+ },
/*
* Fujitsu controller family
*
},
{
.vendor = "FUJITSU",
- .product = "ETERNUS_DX(L|400|8000)",
+ .product = "ETERNUS_DX(H|L|M|400|8000)",
.features = "1 queue_if_no_path",
.hwhandler = DEFAULT_HWHANDLER,
.pgpolicy = GROUP_BY_PRIO,
.prio_name = PRIO_RDAC,
.prio_args = NULL,
},
+ {
+ /* DELL MD36xxi */
+ .vendor = "DELL",
+ .product = "MD36xxi",
+ .bl_product = "Universal Xport",
+ .features = "2 pg_init_retries 50",
+ .hwhandler = "1 rdac",
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .rr_weight = RR_WEIGHT_NONE,
+ .no_path_retry = 15,
+ .checker_name = RDAC,
+ .prio_name = PRIO_RDAC,
+ .prio_args = NULL,
+ },
+ {
+ /* DELL MD36xxf */
+ .vendor = "DELL",
+ .product = "MD36xxf",
+ .bl_product = "Universal Xport",
+ .features = "2 pg_init_retries 50",
+ .hwhandler = "1 rdac",
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .rr_weight = RR_WEIGHT_NONE,
+ .no_path_retry = 15,
+ .checker_name = RDAC,
+ .prio_name = PRIO_RDAC,
+ .prio_args = NULL,
+ },
/*
* NETAPP controller family
*
.prio_name = PRIO_ALUA,
.prio_args = NULL,
},
+ {
+ .vendor = "PURE",
+ .product = "FlashArray",
+ .features = DEFAULT_FEATURES,
+ .hwhandler = DEFAULT_HWHANDLER,
+ .selector = "queue-length 0",
+ .pgpolicy = MULTIBUS,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .checker_name = TUR,
+ .fast_io_fail = 10,
+ .user_friendly_names = USER_FRIENDLY_NAMES_OFF,
+ .prio_name = DEFAULT_PRIO,
+ .no_path_retry = 0,
+ .dev_loss = 60,
+ .prio_args = NULL,
+ },
/*
* EOL
*/
#ifndef _LIST_H
#define _LIST_H
+#include <stddef.h>
+
/**
* container_of - cast a member of a structure out to the containing structure
*
return head->next == head;
}
-static inline void __list_splice(struct list_head *list,
- struct list_head *head)
+static inline void __list_splice(const struct list_head *list,
+ struct list_head *prev,
+ struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
- struct list_head *at = head->next;
- first->prev = head;
- head->next = first;
+ first->prev = prev;
+ prev->next = first;
- last->next = at;
- at->prev = last;
+ last->next = next;
+ next->prev = last;
}
/**
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
- __list_splice(list, head);
+ __list_splice(list, head, head->next);
+}
+
+/**
+ * list_splice_tail - join two lists, each list being a queue
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice_tail(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head->prev, head);
}
/**
struct list_head *head)
{
if (!list_empty(list)) {
- __list_splice(list, head);
+ __list_splice(list, head, head->next);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
void log_safe (int prio, const char * fmt, va_list ap)
{
if (log_thr == (pthread_t)0) {
- syslog(prio, fmt, ap);
+ vsyslog(prio, fmt, ap);
return;
}
*/
#include <syslog.h>
+#include <errno.h>
#include "parser.h"
#include "memory.h"
return NULL;
}
-int
-read_line(char *buf, int size)
+static int
+read_line(FILE *stream, char *buf, int size)
{
- int ch;
- int count = 0;
-
- while ((ch = fgetc(stream)) != EOF && (int) ch != '\n'
- && (int) ch != '\r') {
- if (count < size)
- buf[count] = (int) ch;
- else
- break;
- count++;
- }
- return (ch == EOF) ? 0 : 1;
-}
-
-vector
-read_value_block(void)
-{
- char *buf;
- int i;
- char *str = NULL;
- char *dup;
- vector vec = NULL;
- vector elements = vector_alloc();
-
- if (!elements)
- return NULL;
+ char *p;
- buf = (char *) MALLOC(MAXBUF);
-
- if (!buf) {
- vector_free(elements);
- return NULL;
- }
-
- while (read_line(buf, MAXBUF)) {
- vec = alloc_strvec(buf);
- if (vec) {
- str = VECTOR_SLOT(vec, 0);
- if (!strcmp(str, EOB)) {
- free_strvec(vec);
- break;
- }
-
- for (i = 0; i < VECTOR_SIZE(vec); i++) {
- str = VECTOR_SLOT(vec, i);
- dup = (char *) MALLOC(strlen(str) + 1);
- if (!dup)
- goto out;
- memcpy(dup, str, strlen(str));
-
- if (!vector_alloc_slot(elements)) {
- free_strvec(vec);
- goto out1;
- }
-
- vector_set_slot(elements, dup);
- }
- free_strvec(vec);
- }
- memset(buf, 0, MAXBUF);
- }
- FREE(buf);
- return elements;
-out1:
- FREE(dup);
-out:
- FREE(buf);
- vector_free(elements);
- return NULL;
-}
-
-int
-alloc_value_block(vector strvec, void (*alloc_func) (vector))
-{
- char *buf;
- char *str = NULL;
- vector vec = NULL;
-
- buf = (char *) MALLOC(MAXBUF);
-
- if (!buf)
- return 1;
-
- while (read_line(buf, MAXBUF)) {
- vec = alloc_strvec(buf);
- if (vec) {
- str = VECTOR_SLOT(vec, 0);
- if (!strcmp(str, EOB)) {
- free_strvec(vec);
- break;
- }
-
- if (VECTOR_SIZE(vec))
- (*alloc_func) (vec);
-
- free_strvec(vec);
- }
- memset(buf, 0, MAXBUF);
- }
- FREE(buf);
- return 0;
+ if (fgets(buf, size, stream) == NULL)
+ return 0;
+ strtok_r(buf, "\n\r", &p);
+ return 1;
}
void *
char *alloc = NULL;
char *tmp;
- if (!str)
+ if (!str) {
+ condlog(0, "option '%s' missing value",
+ (char *)VECTOR_SLOT(strvec, 0));
return NULL;
-
+ }
size = strlen(str);
- if (size == 0)
+ if (size == 0) {
+ condlog(0, "option '%s' has empty value",
+ (char *)VECTOR_SLOT(strvec, 0));
return NULL;
-
- if (*str == '"') {
- for (i = 2; i < VECTOR_SIZE(strvec); i++) {
- str = VECTOR_SLOT(strvec, i);
- len += strlen(str);
- if (!alloc)
- alloc =
- (char *) MALLOC(sizeof (char *) *
- (len + 1));
- else {
- alloc =
- REALLOC(alloc, sizeof (char *) * (len + 1));
- tmp = VECTOR_SLOT(strvec, i-1);
- if (alloc && *str != '"' && *tmp != '"')
- strncat(alloc, " ", 1);
- }
-
- if (alloc && i != VECTOR_SIZE(strvec)-1)
- strncat(alloc, str, strlen(str));
- }
- } else {
- alloc = MALLOC(sizeof (char *) * (size + 1));
+ }
+ if (*str != '"') {
+ alloc = MALLOC(sizeof (char) * (size + 1));
if (alloc)
memcpy(alloc, str, size);
+ else
+ condlog(0, "can't allocate memeory for option '%s'",
+ (char *)VECTOR_SLOT(strvec, 0));
+ return alloc;
+ }
+ /* Even empty quotes counts as a value (An empty string) */
+ alloc = (char *) MALLOC(sizeof (char));
+ if (!alloc) {
+ condlog(0, "can't allocate memeory for option '%s'",
+ (char *)VECTOR_SLOT(strvec, 0));
+ return NULL;
+ }
+ for (i = 2; i < VECTOR_SIZE(strvec); i++) {
+ str = VECTOR_SLOT(strvec, i);
+ if (!str) {
+ free(alloc);
+ condlog(0, "parse error for option '%s'",
+ (char *)VECTOR_SLOT(strvec, 0));
+ return NULL;
+ }
+ if (*str == '"')
+ break;
+ tmp = alloc;
+ /* The first +1 is for the NULL byte. The rest are for the
+ * spaces between words */
+ len += strlen(str) + 1;
+ alloc = REALLOC(alloc, sizeof (char) * len);
+ if (!alloc) {
+ FREE(tmp);
+ condlog(0, "can't allocate memeory for option '%s'",
+ (char *)VECTOR_SLOT(strvec, 0));
+ return NULL;
+ }
+ if (*alloc != '\0')
+ strncat(alloc, " ", 1);
+ strncat(alloc, str, strlen(str));
}
return alloc;
}
/* non-recursive configuration stream handler */
static int kw_level = 0;
-int warn_on_duplicates(vector uniques, char *str)
+int warn_on_duplicates(vector uniques, char *str, char *file)
{
char *tmp;
int i;
vector_foreach_slot(uniques, tmp, i) {
if (!strcmp(str, tmp)) {
- condlog(1, "multipath.conf line %d, duplicate keyword: %s", line_nr, str);
+ condlog(1, "%s line %d, duplicate keyword: %s",
+ file, line_nr, str);
return 0;
}
}
}
int
-process_stream(vector keywords)
+is_sublevel_keyword(char *str)
+{
+ return (strcmp(str, "defaults") == 0 || strcmp(str, "blacklist") == 0 ||
+ strcmp(str, "blacklist_exceptions") == 0 ||
+ strcmp(str, "devices") == 0 || strcmp(str, "devices") == 0 ||
+ strcmp(str, "device") == 0 || strcmp(str, "multipaths") == 0 ||
+ strcmp(str, "multipath") == 0);
+}
+
+int
+validate_config_strvec(vector strvec, char *file)
+{
+ char *str;
+ int i;
+
+ str = VECTOR_SLOT(strvec, 0);
+ if (str == NULL) {
+ condlog(0, "can't parse option on line %d of %s",
+ line_nr, file);
+ return -1;
+ }
+ if (*str == '}') {
+ if (VECTOR_SIZE(strvec) > 1)
+ condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file);
+ return 0;
+ }
+ if (*str == '{') {
+ condlog(0, "invalid keyword '%s' on line %d of %s",
+ str, line_nr, file);
+ return -1;
+ }
+ if (is_sublevel_keyword(str)) {
+ str = VECTOR_SLOT(strvec, 1);
+ if (str == NULL)
+ condlog(0, "missing '{' on line %d of %s",
+ line_nr, file);
+ else if (*str != '{')
+ condlog(0, "expecting '{' on line %d of %s. found '%s'",
+ line_nr, file, str);
+ else if (VECTOR_SIZE(strvec) > 2)
+ condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
+ return 0;
+ }
+ str = VECTOR_SLOT(strvec, 1);
+ if (str == NULL) {
+ condlog(0, "missing value for option '%s' on line %d of %s",
+ (char *)VECTOR_SLOT(strvec, 0), line_nr, file);
+ return -1;
+ }
+ if (*str != '"') {
+ if (VECTOR_SIZE(strvec) > 2)
+ condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file);
+ return 0;
+ }
+ for (i = 2; i < VECTOR_SIZE(strvec); i++) {
+ str = VECTOR_SLOT(strvec, i);
+ if (str == NULL) {
+ condlog(0, "can't parse value on line %d of %s",
+ line_nr, file);
+ return -1;
+ }
+ if (*str == '"') {
+ if (VECTOR_SIZE(strvec) > i + 1)
+ condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file);
+ return 0;
+ }
+ }
+ condlog(0, "missing closing quotes on line %d of %s",
+ line_nr, file);
+ return 0;
+}
+
+static int
+process_stream(FILE *stream, vector keywords, char *file)
{
int i;
- int r = 0;
+ int r = 0, t;
struct keyword *keyword;
char *str;
char *buf;
return 1;
}
- while (read_line(buf, MAXBUF)) {
+ while (read_line(stream, buf, MAXBUF)) {
line_nr++;
strvec = alloc_strvec(buf);
- memset(buf,0, MAXBUF);
-
if (!strvec)
continue;
+ if (validate_config_strvec(strvec, file) != 0) {
+ free_strvec(strvec);
+ continue;
+ }
+
str = VECTOR_SLOT(strvec, 0);
- if (!strcmp(str, EOB) && kw_level > 0) {
- free_strvec(strvec);
- break;
+ if (!strcmp(str, EOB)) {
+ if (kw_level > 0) {
+ free_strvec(strvec);
+ break;
+ }
+ condlog(0, "unmatched '%s' at line %d of %s",
+ EOB, line_nr, file);
}
for (i = 0; i < VECTOR_SIZE(keywords); i++) {
if (!strcmp(keyword->string, str)) {
if (keyword->unique &&
- warn_on_duplicates(uniques, str)) {
+ warn_on_duplicates(uniques, str, file)) {
r = 1;
free_strvec(strvec);
goto out;
}
- if (keyword->handler)
- r += (*keyword->handler) (strvec);
+ if (keyword->handler) {
+ t = (*keyword->handler) (strvec);
+ r += t;
+ if (t)
+ condlog(1, "multipath.conf +%d, parsing failed: %s",
+ line_nr, buf);
+ }
if (keyword->sub) {
kw_level++;
- r += process_stream(keyword->sub);
+ r += process_stream(stream,
+ keyword->sub, file);
kw_level--;
}
break;
}
}
if (i >= VECTOR_SIZE(keywords))
- condlog(1, "multipath.conf +%d, invalid keyword: %s",
- line_nr, str);
+ condlog(1, "%s line %d, invalid keyword: %s",
+ file, line_nr, str);
free_strvec(strvec);
}
/* Data initialization */
int
-init_data(char *conf_file, void (*init_keywords) (void))
+process_file(char *file)
{
int r;
+ FILE *stream;
- stream = fopen(conf_file, "r");
+ if (!keywords) {
+ condlog(0, "No keywords alocated");
+ return 1;
+ }
+ stream = fopen(file, "r");
if (!stream) {
- syslog(LOG_WARNING, "Configuration file open problem");
+ condlog(0, "couldn't open configuration file '%s': %s",
+ file, strerror(errno));
return 1;
}
- /* Init Keywords structure */
- (*init_keywords) ();
-
-/* Dump configuration *
- vector_dump(keywords);
- dump_keywords(keywords, 0);
-*/
-
/* Stream handling */
line_nr = 0;
- r = process_stream(keywords);
+ r = process_stream(stream, keywords, file);
fclose(stream);
//free_keywords(keywords);
int unique;
};
-/* global var exported */
-FILE *stream;
-
/* Reloading helpers */
#define SET_RELOAD (reload = 1)
#define UNSET_RELOAD (reload = 0)
extern void dump_keywords(vector keydump, int level);
extern void free_keywords(vector keywords);
extern vector alloc_strvec(char *string);
-extern int read_line(char *buf, int size);
-extern vector read_value_block(void);
-extern int alloc_value_block(vector strvec, void (*alloc_func) (vector));
extern void *set_value(vector strvec);
-extern int process_stream(vector keywords);
extern int alloc_keywords(void);
-extern int init_data(char *conf_file, void (*init_keywords) (void));
+extern int process_file(char *conf_file);
extern struct keyword * find_keyword(vector v, char * name);
void set_current_keywords (vector *k);
int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
if (0 == strncmp(str, "group_by_node_name", 18))
return GROUP_BY_NODE_NAME;
- return -1;
+ return IOPOLICY_UNDEF;
}
extern int
#include <unistd.h>
#include <string.h>
#include <errno.h>
+#include <libudev.h>
#include "checkers.h"
#include "vector.h"
#define MAX(x,y) (x > y) ? x : y
#define TAIL (line + len - 1 - c)
#define NOPAD s = c
-#define PAD(x) while ((int)(c - s) < (x) && (c < (line + len - 1))) \
- *c++ = ' '; s = c
+#define PAD(x) \
+do { \
+ while ((int)(c - s) < (x) && (c < (line + len - 1))) \
+ *c++ = ' '; \
+ s = c; \
+} while (0)
+
#define ENDLINE \
if (c > line) \
line[c - line - 1] = '\n'
-#define PRINT(var, size, format, args...) \
- fwd = snprintf(var, size, format, ##args); \
- c += (fwd >= size) ? size : fwd;
+#define PRINT(var, size, format, args...) \
+do { \
+ fwd = snprintf(var, size, format, ##args); \
+ c += (fwd >= size) ? size : fwd; \
+} while (0)
/*
* information printing helpers
*/
static int
-snprint_str (char * buff, size_t len, char * str)
+snprint_str (char * buff, size_t len, const char * str)
{
return snprintf(buff, len, "%s", str);
}
static int
snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp)
{
- struct path * pp = first_path(mpp);
- if (!pp)
- return 0;
- return snprintf(buff, len, "%s,%s",
- pp->vendor_id, pp->product_id);
+ struct pathgroup * pgp;
+ struct path * pp;
+ int i, j;
+
+ vector_foreach_slot(mpp->pg, pgp, i) {
+ if (!pgp)
+ continue;
+ vector_foreach_slot(pgp->paths, pp, j) {
+ if (strlen(pp->vendor_id) && strlen(pp->product_id))
+ return snprintf(buff, len, "%s,%s",
+ pp->vendor_id, pp->product_id);
+ }
+ }
+ return snprintf(buff, len, "##,##");
}
static int
static int
snprint_hcil (char * buff, size_t len, struct path * pp)
{
- if (pp->sg_id.host_no < 0)
+ if (!pp || pp->sg_id.host_no < 0)
return snprintf(buff, len, "#:#:#:#");
return snprintf(buff, len, "%i:%i:%i:%i",
static int
snprint_dev (char * buff, size_t len, struct path * pp)
{
- if (!strlen(pp->dev))
+ if (!pp || !strlen(pp->dev))
return snprintf(buff, len, "-");
else
return snprint_str(buff, len, pp->dev);
static int
snprint_dev_t (char * buff, size_t len, struct path * pp)
{
- if (!strlen(pp->dev))
+ if (!pp || !strlen(pp->dev))
return snprintf(buff, len, "#:#");
else
return snprint_str(buff, len, pp->dev_t);
static int
snprint_offline (char * buff, size_t len, struct path * pp)
{
- if (pp->offline)
+ if (!pp)
+ return snprintf(buff, len, "unknown");
+ else if (pp->offline)
return snprintf(buff, len, "offline");
else
return snprintf(buff, len, "running");
static int
snprint_chk_state (char * buff, size_t len, struct path * pp)
{
+ if (!pp)
+ return snprintf(buff, len, "undef");
+
switch (pp->state) {
case PATH_UP:
return snprintf(buff, len, "ready");
return snprintf(buff, len, "i/o pending");
case PATH_TIMEOUT:
return snprintf(buff, len, "i/o timeout");
+ case PATH_DELAYED:
+ return snprintf(buff, len, "delayed");
default:
return snprintf(buff, len, "undef");
}
static int
snprint_dm_path_state (char * buff, size_t len, struct path * pp)
{
+ if (!pp)
+ return snprintf(buff, len, "undef");
+
switch (pp->dmstate) {
case PSTATE_ACTIVE:
return snprintf(buff, len, "active");
static int
snprint_next_check (char * buff, size_t len, struct path * pp)
{
- if (!pp->mpp)
+ if (!pp || !pp->mpp)
return snprintf(buff, len, "orphan");
return snprint_progress(buff, len, pp->tick, pp->checkint);
static int
snprint_pri (char * buff, size_t len, struct path * pp)
{
- return snprint_int(buff, len, pp->priority);
+ return snprint_int(buff, len, pp ? pp->priority : -1);
}
static int
return snprint_str(buff, len, pp->mpp->alias);
}
+static int
+snprint_host_attr (char * buff, size_t len, struct path * pp, char *attr)
+{
+ struct udev_device *host_dev = NULL;
+ char host_id[32];
+ const char *value = NULL;
+ int ret;
+
+ if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
+ return snprintf(buff, len, "[undef]");
+ sprintf(host_id, "host%d", pp->sg_id.host_no);
+ host_dev = udev_device_new_from_subsystem_sysname(conf->udev, "fc_host",
+ host_id);
+ if (!host_dev) {
+ condlog(1, "%s: No fc_host device for '%s'", pp->dev, host_id);
+ goto out;
+ }
+ value = udev_device_get_sysattr_value(host_dev, attr);
+ if (value)
+ ret = snprint_str(buff, len, value);
+ udev_device_unref(host_dev);
+out:
+ if (!value)
+ ret = snprintf(buff, len, "[unknown]");
+ return ret;
+}
+
+int
+snprint_host_wwnn (char * buff, size_t len, struct path * pp)
+{
+ return snprint_host_attr(buff, len, pp, "node_name");
+}
+
+int
+snprint_host_wwpn (char * buff, size_t len, struct path * pp)
+{
+ return snprint_host_attr(buff, len, pp, "port_name");
+}
+
+int
+snprint_tgt_wwpn (char * buff, size_t len, struct path * pp)
+{
+ struct udev_device *rport_dev = NULL;
+ char rport_id[32];
+ const char *value = NULL;
+ int ret;
+
+ if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
+ return snprintf(buff, len, "[undef]");
+ sprintf(rport_id, "rport-%d:%d-%d",
+ pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
+ rport_dev = udev_device_new_from_subsystem_sysname(conf->udev,
+ "fc_remote_ports", rport_id);
+ if (!rport_dev) {
+ condlog(1, "%s: No fc_remote_port device for '%s'", pp->dev,
+ rport_id);
+ goto out;
+ }
+ value = udev_device_get_sysattr_value(rport_dev, "port_name");
+ if (value)
+ ret = snprint_str(buff, len, value);
+ udev_device_unref(rport_dev);
+out:
+ if (!value)
+ ret = snprintf(buff, len, "[unknown]");
+ return ret;
+}
+
+
+int
+snprint_tgt_wwnn (char * buff, size_t len, struct path * pp)
+{
+ if (pp->tgt_node_name[0] == '\0')
+ return snprintf(buff, len, "[undef]");
+ return snprint_str(buff, len, pp->tgt_node_name);
+}
+
+static int
+snprint_host_adapter (char * buff, size_t len, struct path * pp)
+{
+ char adapter[SLOT_NAME_SIZE];
+
+ if (sysfs_get_host_adapter_name(pp, adapter))
+ return snprintf(buff, len, "[undef]");
+ return snprint_str(buff, len, adapter);
+}
+
static int
snprint_path_checker (char * buff, size_t len, struct path * pp)
{
{'S', "size", 0, snprint_path_size},
{'z', "serial", 0, snprint_path_serial},
{'m', "multipath", 0, snprint_path_mpp},
+ {'N', "host WWNN", 0, snprint_host_wwnn},
+ {'n', "target WWNN", 0, snprint_tgt_wwnn},
+ {'R', "host WWPN", 0, snprint_host_wwpn},
+ {'r', "target WWPN", 0, snprint_tgt_wwpn},
+ {'a', "host adapter", 0, snprint_host_adapter},
{0, NULL, 0 , NULL}
};
int
snprint_multipath (char * line, int len, char * format,
- struct multipath * mpp)
+ struct multipath * mpp, int pad)
{
char * c = line; /* line cursor */
char * s = line; /* for padding */
data->snprint(buff, MAX_FIELD_LEN, mpp);
PRINT(c, TAIL, "%s", buff);
- PAD(data->width);
+ if (pad)
+ PAD(data->width);
buff[0] = '\0';
} while (*f++);
int
snprint_path (char * line, int len, char * format,
- struct path * pp)
+ struct path * pp, int pad)
{
char * c = line; /* line cursor */
char * s = line; /* for padding */
data->snprint(buff, MAX_FIELD_LEN, pp);
PRINT(c, TAIL, "%s", buff);
- PAD(data->width);
+ if (pad)
+ PAD(data->width);
} while (*f++);
ENDLINE;
}
} while (resize);
printf("%s", buff);
+ FREE(buff);
}
extern int
reset_multipath_layout();
if (verbosity == 1)
- return snprint_multipath(buff, len, "%n", mpp);
+ return snprint_multipath(buff, len, "%n", mpp, 1);
if(isatty(1))
c += sprintf(c, "%c[%dm", 0x1B, 1); /* bold on */
if(isatty(1))
c += sprintf(c, "%c[%dm", 0x1B, 0); /* bold off */
- fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp);
+ fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp, 1);
if (fwd > len)
return len;
- fwd += snprint_multipath(buff + fwd, len - fwd, PRINT_MAP_PROPS, mpp);
+ fwd += snprint_multipath(buff + fwd, len - fwd, PRINT_MAP_PROPS, mpp,
+ 1);
if (fwd > len)
return len;
strcpy(f, " |- " PRINT_PATH_INDENT);
else
strcpy(f, " `- " PRINT_PATH_INDENT);
- fwd += snprint_path(buff + fwd, len - fwd, fmt, pp);
+ fwd += snprint_path(buff + fwd, len - fwd, fmt, pp, 1);
if (fwd > len)
return len;
}
return fwd;
}
+extern int
+snprint_overrides (char * buff, int len, struct hwentry *overrides)
+{
+ int fwd = 0;
+ int i;
+ struct keyword *rootkw;
+ struct keyword *kw;
+
+ rootkw = find_keyword(NULL, "overrides");
+ if (!rootkw)
+ return 0;
+
+ fwd += snprintf(buff + fwd, len - fwd, "overrides {\n");
+ if (fwd > len)
+ return len;
+ if (!overrides)
+ goto out;
+ iterate_sub_keywords(rootkw, kw, i) {
+ fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
+ kw, NULL);
+ if (fwd > len)
+ return len;
+ }
+out:
+ fwd += snprintf(buff + fwd, len - fwd, "}\n");
+ if (fwd > len)
+ return len;
+ return fwd;
+}
+
extern int
snprint_defaults (char * buff, int len)
{
if (!(blkdir = opendir("/sys/block")))
return 1;
- if ((len - fwd - threshold) <= 0)
+ if ((len - fwd - threshold) <= 0) {
+ closedir(blkdir);
return len;
+ }
fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n");
strcpy(devpath,"/sys/block/");
if (S_ISDIR(statbuf.st_mode) == 0)
continue;
- if ((len - fwd - threshold) <= 0)
+ if ((len - fwd - threshold) <= 0) {
+ closedir(blkdir);
return len;
+ }
fwd += snprintf(buff + fwd, len - fwd, " %s", devptr);
pp = find_path_by_dev(vecs->pathvec, devptr);
if (r > 0)
fwd += snprintf(buff + fwd, len - fwd,
" devnode blacklisted, unmonitored");
- else if (r < 0)
+ else if (r <= 0)
fwd += snprintf(buff + fwd, len - fwd,
" devnode whitelisted, unmonitored");
} else
char line[MAX_LINE_LEN];
memset(&line[0], 0, MAX_LINE_LEN);
- snprint_path(&line[0], MAX_LINE_LEN, style, pp);
+ snprint_path(&line[0], MAX_LINE_LEN, style, pp, 1);
printf("%s", line);
}
char line[MAX_LINE_LEN];
memset(&line[0], 0, MAX_LINE_LEN);
- snprint_multipath(&line[0], MAX_LINE_LEN, style, mpp);
+ snprint_multipath(&line[0], MAX_LINE_LEN, style, mpp, 1);
printf("%s", line);
}
void get_multipath_layout (vector mpvec, int header);
int snprint_path_header (char *, int, char *);
int snprint_multipath_header (char *, int, char *);
-int snprint_path (char *, int, char *, struct path *);
-int snprint_multipath (char *, int, char *, struct multipath *);
+int snprint_path (char *, int, char *, struct path *, int);
+int snprint_multipath (char *, int, char *, struct multipath *, int);
int snprint_multipath_topology (char *, int, struct multipath * mpp,
int verbosity);
int snprint_defaults (char *, int);
int snprint_devices (char *, int, struct vectors *);
int snprint_hwtable (char *, int, vector);
int snprint_mptable (char *, int, vector);
+int snprint_overrides (char *, int, struct hwentry *);
+int snprint_host_wwnn (char *, size_t, struct path *);
+int snprint_host_wwpn (char *, size_t, struct path *);
+int snprint_tgt_wwnn (char *, size_t, struct path *);
+int snprint_tgt_wwpn (char *, size_t, struct path *);
void print_multipath_topology (struct multipath * mpp, int verbosity);
void print_path (struct path * pp, char * style);
static LIST_HEAD(prioritizers);
+unsigned int get_prio_timeout(unsigned int default_timeout)
+{
+ if (conf->checker_timeout)
+ return conf->checker_timeout * 1000;
+ return default_timeout;
+}
+
int init_prio (void)
{
if (!add_prio(DEFAULT_PRIO))
int prio_selected (struct prio * p)
{
- if (!p || !p->getprio)
+ if (!p)
return 0;
return (p->getprio) ? 1 : 0;
}
{
struct prio * src;
- if (!dst)
+ if (!dst || !dst->getprio)
return;
src = prio_lookup(dst->name);
int (*getprio)(struct path *, char *);
};
+unsigned int get_prio_timeout(unsigned int default_timeout);
int init_prio (void);
void cleanup_prio (void);
struct prio * add_prio (char *);
}
int
-get_alua_info(int fd)
+get_alua_info(struct path * pp)
{
int rc;
int tpg;
- int aas;
- rc = get_target_port_group_support(fd);
- if (rc < 0)
- return -ALUA_PRIO_TPGS_FAILED;
-
- if (rc == TPGS_NONE)
- return -ALUA_PRIO_NOT_SUPPORTED;
-
- tpg = get_target_port_group(fd);
- if (tpg < 0)
+ tpg = get_target_port_group(pp);
+ if (tpg < 0) {
+ rc = get_target_port_group_support(pp->fd);
+ if (rc < 0)
+ return -ALUA_PRIO_TPGS_FAILED;
+ if (rc == TPGS_NONE)
+ return -ALUA_PRIO_NOT_SUPPORTED;
return -ALUA_PRIO_RTPG_FAILED;
-
+ }
condlog(3, "reported target port group is %i", tpg);
- rc = get_asymmetric_access_state(fd, tpg);
+ rc = get_asymmetric_access_state(pp->fd, tpg);
if (rc < 0)
return -ALUA_PRIO_GETAAS_FAILED;
- aas = (rc & 0x0f);
condlog(3, "aas = %02x [%s]%s", rc, aas_print_string(rc),
(rc & 0x80) ? " [preferred]" : "");
return rc;
}
+int get_exclusive_perf_arg(char *args)
+{
+ char *ptr;
+
+ if (args == NULL)
+ return 0;
+ ptr = strstr(args, "exclusive_pref_bit");
+ if (!ptr)
+ return 0;
+ if (ptr[18] != '\0' && ptr[18] != ' ' && ptr[18] != '\t')
+ return 0;
+ if (ptr != args && ptr[-1] != ' ' && ptr[-1] != '\t')
+ return 0;
+ return 1;
+}
+
int getprio (struct path * pp, char * args)
{
int rc;
int aas;
int priopath;
+ int exclusive_perf;
if (pp->fd < 0)
return -ALUA_PRIO_NO_INFORMATION;
- rc = get_alua_info(pp->fd);
+ exclusive_perf = get_exclusive_perf_arg(args);
+ rc = get_alua_info(pp);
if (rc >= 0) {
aas = (rc & 0x0f);
priopath = (rc & 0x80);
default:
rc = 0;
}
- if (priopath && aas != AAS_OPTIMIZED)
+ if (priopath && (aas != AAS_OPTIMIZED || exclusive_perf))
rc += 80;
} else {
switch(-rc) {
condlog(0, "%s: couldn't get target port group", pp->dev);
break;
case ALUA_PRIO_GETAAS_FAILED:
- condlog(0, "%s: couln't get asymmetric access state", pp->dev);
+ condlog(0, "%s: couldn't get asymmetric access state", pp->dev);
break;
case ALUA_PRIO_TPGS_FAILED:
- condlog(3, "%s: couln't get supported alua states", pp->dev);
+ condlog(3, "%s: couldn't get supported alua states", pp->dev);
break;
}
}
#include <string.h>
#include <sys/ioctl.h>
#include <inttypes.h>
+#include <libudev.h>
#define __user
#include <scsi/sg.h>
+#include "../structs.h"
+#include "../prio.h"
+#include "../discovery.h"
#include "alua_rtpg.h"
#define SENSE_BUFF_LEN 32
-#define DEF_TIMEOUT 60000
+#define SGIO_TIMEOUT 60000
/*
* Macro used to print debug messaged.
hdr.dxfer_len = resplen;
hdr.sbp = sense;
hdr.mx_sb_len = sizeof(sense);
- hdr.timeout = DEF_TIMEOUT;
+ hdr.timeout = get_prio_timeout(SGIO_TIMEOUT);
if (ioctl(fd, SG_IO, &hdr) < 0) {
PRINT_DEBUG("do_inquiry: IOCTL failed!\n");
return rc;
}
+static int
+get_sysfs_pg83(struct path *pp, unsigned char *buff, int buflen)
+{
+ struct udev_device *parent = pp->udev;
+
+ while (parent) {
+ const char *subsys = udev_device_get_subsystem(parent);
+ if (subsys && !strncmp(subsys, "scsi", 4))
+ break;
+ parent = udev_device_get_parent(parent);
+ }
+
+ if (!parent || sysfs_get_vpd(parent, 0x83, buff, buflen) <= 0) {
+ PRINT_DEBUG("failed to read sysfs vpd pg83\n");
+ return -1;
+ }
+ return 0;
+}
+
int
-get_target_port_group(int fd)
+get_target_port_group(struct path * pp)
{
unsigned char *buf;
struct vpd83_data * vpd83;
int rc;
int buflen, scsi_buflen;
- buflen = 128; /* Lets start from 128 */
+ buflen = 4096;
buf = (unsigned char *)malloc(buflen);
if (!buf) {
PRINT_DEBUG("malloc failed: could not allocate"
}
memset(buf, 0, buflen);
- rc = do_inquiry(fd, 1, 0x83, buf, buflen);
- if (rc < 0)
- goto out;
- scsi_buflen = (buf[2] << 8 | buf[3]) + 4;
- if (buflen < scsi_buflen) {
- free(buf);
- buf = (unsigned char *)malloc(scsi_buflen);
- if (!buf) {
- PRINT_DEBUG("malloc failed: could not allocate"
- "%u bytes\n", scsi_buflen);
- return -RTPG_RTPG_FAILED;
- }
- buflen = scsi_buflen;
- memset(buf, 0, buflen);
- rc = do_inquiry(fd, 1, 0x83, buf, buflen);
+ rc = get_sysfs_pg83(pp, buf, buflen);
+
+ if (rc < 0) {
+ rc = do_inquiry(pp->fd, 1, 0x83, buf, buflen);
if (rc < 0)
goto out;
+
+ scsi_buflen = (buf[2] << 8 | buf[3]) + 4;
+ if (buflen < scsi_buflen) {
+ free(buf);
+ buf = (unsigned char *)malloc(scsi_buflen);
+ if (!buf) {
+ PRINT_DEBUG("malloc failed: could not allocate"
+ "%u bytes\n", scsi_buflen);
+ return -RTPG_RTPG_FAILED;
+ }
+ buflen = scsi_buflen;
+ memset(buf, 0, buflen);
+ rc = do_inquiry(pp->fd, 1, 0x83, buf, buflen);
+ if (rc < 0)
+ goto out;
+ }
}
vpd83 = (struct vpd83_data *) buf;
hdr.dxfer_len = resplen;
hdr.mx_sb_len = sizeof(sense);
hdr.sbp = sense;
- hdr.timeout = DEF_TIMEOUT;
+ hdr.timeout = get_prio_timeout(SGIO_TIMEOUT);
if (ioctl(fd, SG_IO, &hdr) < 0)
return -RTPG_RTPG_FAILED;
int buflen;
uint32_t scsi_buflen;
- buflen = 128; /* Initial value from old code */
+ buflen = 4096;
buf = (unsigned char *)malloc(buflen);
if (!buf) {
PRINT_DEBUG ("malloc failed: could not allocate"
#define RTPG_TPG_NOT_FOUND 4
int get_target_port_group_support(int fd);
-int get_target_port_group(int fd);
+int get_target_port_group(struct path * pp);
int get_asymmetric_access_state(int fd, unsigned int tpg);
#endif /* __RTPG_H__ */
int emc_clariion_prio(const char *dev, int fd)
{
- unsigned char sense_buffer[256];
+ unsigned char sense_buffer[128];
unsigned char sb[128];
unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0,
- sizeof(sb), 0};
+ sizeof(sense_buffer), 0};
struct sg_io_hdr io_hdr;
int ret = PRIO_UNDEF;
memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
- memset(&sense_buffer, 0, 256);
+ memset(&sense_buffer, 0, 128);
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof (inqCmdBlk);
io_hdr.mx_sb_len = sizeof (sb);
io_hdr.dxferp = sense_buffer;
io_hdr.cmdp = inqCmdBlk;
io_hdr.sbp = sb;
- io_hdr.timeout = 60000;
+ io_hdr.timeout = get_prio_timeout(60000);
io_hdr.pack_id = 0;
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
pp_emc_log(0, "sending query command failed");
io_hdr.dxferp = inqBuff;
io_hdr.cmdp = inqCmdBlk;
io_hdr.sbp = sense_buffer;
- io_hdr.timeout = 2000; /* TimeOut = 2 seconds */
+ io_hdr.timeout = get_prio_timeout(2000); /* TimeOut = 2 seconds */
if (ioctl (fd, SG_IO, &io_hdr) < 0) {
pp_hds_log(0, "SG_IO error");
io_hdr.dxfer_direction = SG_DXFER_NONE;
io_hdr.cmdp = turCmdBlk;
io_hdr.sbp = sb;
- io_hdr.timeout = 60000;
+ io_hdr.timeout = get_prio_timeout(60000);
io_hdr.pack_id = 0;
retry:
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
ssize_t nchars = readlink(path, buffer, sizeof(buffer)-1);
if (nchars != -1) {
char *device;
+ buffer[nchars] = '\0';
device = find_regex(buffer,"(sd[a-z]+)");
// if device parsed is the right one
if (device!=NULL && strncmp(device, dev, strlen(device)) == 0) {
if (ip!=NULL && strncmp(ip, preferredip, strlen(ip)) == 0) {
// high prio
free(ip);
+ free(device);
closedir(dir_p);
return 20;
}
io_hdr.dxferp = results;
io_hdr.cmdp = cdb;
io_hdr.sbp = sb;
- io_hdr.timeout = SG_TIMEOUT;
+ io_hdr.timeout = get_prio_timeout(SG_TIMEOUT);
io_hdr.pack_id = 0;
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
pp_ontap_log(0, "SG_IO ioctl failed, errno=%d", errno);
io_hdr.dxferp = results;
io_hdr.cmdp = cdb;
io_hdr.sbp = sb;
- io_hdr.timeout = SG_TIMEOUT;
+ io_hdr.timeout = get_prio_timeout(SG_TIMEOUT);
io_hdr.pack_id = 0;
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
pp_ontap_log(0, "ioctl sending inquiry command failed, "
int rdac_prio(const char *dev, int fd)
{
- unsigned char sense_buffer[256];
+ unsigned char sense_buffer[128];
unsigned char sb[128];
unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0,
- sizeof(sb), 0};
+ sizeof(sense_buffer), 0};
struct sg_io_hdr io_hdr;
int ret = 0;
memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
- memset(sense_buffer, 0, 256);
+ memset(sense_buffer, 0, 128);
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof (inqCmdBlk);
io_hdr.mx_sb_len = sizeof (sb);
io_hdr.dxferp = sense_buffer;
io_hdr.cmdp = inqCmdBlk;
io_hdr.sbp = sb;
- io_hdr.timeout = 60000;
+ io_hdr.timeout = get_prio_timeout(60000);
io_hdr.pack_id = 0;
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
pp_rdac_log(0, "sending inquiry command failed");
#include <memory.h>
#include <debug.h>
#include <regex.h>
+#include <structs_vec.h>
+#include <print.h>
char *get_next_string(char **temp, char *split_char)
{
return token;
}
+#define CHECK_LEN \
+do { \
+ if ((p - str) >= (len - 1)) { \
+ condlog(0, "%s: %s - buffer size too small", pp->dev, pp->prio.name); \
+ return -1; \
+ } \
+} while(0)
+
+static int
+build_wwn_path(struct path *pp, char *str, int len)
+{
+ char *p = str;
+
+ p += snprint_host_wwnn(p, str + len - p, pp);
+ CHECK_LEN;
+ p += snprintf(p, str + len - p, ":");
+ CHECK_LEN;
+ p += snprint_host_wwpn(p, str + len - p, pp);
+ CHECK_LEN;
+ p += snprintf(p, str + len - p, ":");
+ CHECK_LEN;
+ p += snprint_tgt_wwnn(p, str + len - p, pp);
+ CHECK_LEN;
+ p += snprintf(p, str + len - p, ":");
+ CHECK_LEN;
+ p += snprint_tgt_wwpn(p, str + len - p, pp);
+ CHECK_LEN;
+ return 0;
+}
+
/* main priority routine */
int prio_path_weight(struct path *pp, char *prio_args)
{
regex = get_next_string(&temp, split_char);
/* Return default priority if the argument is not parseable */
- if (!regex)
+ if (!regex) {
+ FREE(arg);
return priority;
+ }
if (!strcmp(regex, HBTL)) {
sprintf(path, "%d:%d:%d:%d", pp->sg_id.host_no,
pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun);
} else if (!strcmp(regex, DEV_NAME)) {
strcpy(path, pp->dev);
+ } else if (!strcmp(regex, WWN)) {
+ if (build_wwn_path(pp, path, FILE_NAME_SIZE) != 0) {
+ FREE(arg);
+ return priority;
+ }
} else {
condlog(0, "%s: %s - Invalid arguments", pp->dev,
pp->prio.name);
+ FREE(arg);
return priority;
}
#define PRIO_WEIGHTED_PATH "weightedpath"
#define HBTL "hbtl"
#define DEV_NAME "devname"
+#define WWN "wwn"
#define DEFAULT_PRIORITY 0
int prio_path_weight(struct path *pp, char *prio_args);
#include "devmapper.h"
#include "prio.h"
#include "discovery.h"
+#include "dict.h"
#include "prioritizers/alua_rtpg.h"
#include <inttypes.h>
group_by_node_name
};
+#define do_set(var, src, dest, msg) \
+do { \
+ if (src && src->var) { \
+ dest = src->var; \
+ origin = msg; \
+ goto out; \
+ } \
+} while(0)
+#define do_default(dest, value) \
+do { \
+ dest = value; \
+ origin = "(internal default)"; \
+} while(0)
+
+#define mp_set_mpe(var) \
+do_set(var, mp->mpe, mp->var, "(LUN setting)")
+#define mp_set_hwe(var) \
+do_set(var, mp->hwe, mp->var, "(controller setting)")
+#define mp_set_ovr(var) \
+do_set(var, conf->overrides, mp->var, "(overrides setting)")
+#define mp_set_conf(var) \
+do_set(var, conf, mp->var, "(config file default)")
+#define mp_set_default(var, value) \
+do_default(mp->var, value)
+
+#define pp_set_mpe(var) \
+do_set(var, mpe, pp->var, "(LUN setting)")
+#define pp_set_hwe(var) \
+do_set(var, pp->hwe, pp->var, "(controller setting)")
+#define pp_set_conf(var) \
+do_set(var, conf, pp->var, "(config file default)")
+#define pp_set_ovr(var) \
+do_set(var, conf->overrides, pp->var, "(overrides setting)")
+#define pp_set_default(var, value) \
+do_default(pp->var, value)
+
+#define do_attr_set(var, src, shift, msg) \
+do { \
+ if (src && (src->attribute_flags & (1 << shift))) { \
+ mp->attribute_flags |= (1 << shift); \
+ mp->var = src->var; \
+ origin = msg; \
+ goto out; \
+ } \
+} while(0)
+
+#define set_attr_mpe(var, shift) \
+do_attr_set(var, mp->mpe, shift, "(LUN setting)")
+#define set_attr_conf(var, shift) \
+do_attr_set(var, conf, shift, "(config file default)")
+
extern int
select_mode (struct multipath *mp)
{
- if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_MODE))) {
- mp->attribute_flags |= (1 << ATTR_MODE);
- mp->mode = mp->mpe->mode;
- condlog(3, "mode = 0%o (LUN setting)", mp->mode);
- }
- else if (conf->attribute_flags & (1 << ATTR_MODE)) {
- mp->attribute_flags |= (1 << ATTR_MODE);
- mp->mode = conf->mode;
- condlog(3, "mode = 0%o (config file default)", mp->mode);
- }
- else
- mp->attribute_flags &= ~(1 << ATTR_MODE);
+ char *origin;
+
+ set_attr_mpe(mode, ATTR_MODE);
+ set_attr_conf(mode, ATTR_MODE);
+ mp->attribute_flags &= ~(1 << ATTR_MODE);
+ return 0;
+out:
+ condlog(3, "%s: mode = 0%o %s", mp->alias, mp->mode, origin);
return 0;
}
extern int
select_uid (struct multipath *mp)
{
- if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_UID))) {
- mp->attribute_flags |= (1 << ATTR_UID);
- mp->uid = mp->mpe->uid;
- condlog(3, "uid = %u (LUN setting)", mp->uid);
- }
- else if (conf->attribute_flags & (1 << ATTR_UID)) {
- mp->attribute_flags |= (1 << ATTR_UID);
- mp->uid = conf->uid;
- condlog(3, "uid = %u (config file default)", mp->uid);
- }
- else
- mp->attribute_flags &= ~(1 << ATTR_UID);
+ char *origin;
+
+ set_attr_mpe(uid, ATTR_UID);
+ set_attr_conf(uid, ATTR_UID);
+ mp->attribute_flags &= ~(1 << ATTR_UID);
+ return 0;
+out:
+ condlog(3, "%s: uid = 0%o %s", mp->alias, mp->uid, origin);
return 0;
}
extern int
select_gid (struct multipath *mp)
{
- if (mp->mpe && (mp->mpe->attribute_flags & (1 << ATTR_GID))) {
- mp->attribute_flags |= (1 << ATTR_GID);
- mp->gid = mp->mpe->gid;
- condlog(3, "gid = %u (LUN setting)", mp->gid);
- }
- else if (conf->attribute_flags & (1 << ATTR_GID)) {
- mp->attribute_flags |= (1 << ATTR_GID);
- mp->gid = conf->gid;
- condlog(3, "gid = %u (config file default)", mp->gid);
- }
- else
- mp->attribute_flags &= ~(1 << ATTR_GID);
+ char *origin;
+
+ set_attr_mpe(gid, ATTR_GID);
+ set_attr_conf(gid, ATTR_GID);
+ mp->attribute_flags &= ~(1 << ATTR_GID);
+ return 0;
+out:
+ condlog(3, "%s: gid = 0%o %s", mp->alias, mp->gid, origin);
return 0;
}
extern int
select_rr_weight (struct multipath * mp)
{
- if (mp->mpe && mp->mpe->rr_weight) {
- mp->rr_weight = mp->mpe->rr_weight;
- condlog(3, "%s: rr_weight = %i (LUN setting)",
- mp->alias, mp->rr_weight);
- return 0;
- }
- if (mp->hwe && mp->hwe->rr_weight) {
- mp->rr_weight = mp->hwe->rr_weight;
- condlog(3, "%s: rr_weight = %i (controller setting)",
- mp->alias, mp->rr_weight);
- return 0;
- }
- if (conf->rr_weight) {
- mp->rr_weight = conf->rr_weight;
- condlog(3, "%s: rr_weight = %i (config file default)",
- mp->alias, mp->rr_weight);
- return 0;
- }
- mp->rr_weight = RR_WEIGHT_NONE;
- condlog(3, "%s: rr_weight = %i (internal default)",
- mp->alias, mp->rr_weight);
+ char *origin, buff[13];
+
+ mp_set_mpe(rr_weight);
+ mp_set_ovr(rr_weight);
+ mp_set_hwe(rr_weight);
+ mp_set_conf(rr_weight);
+ mp_set_default(rr_weight, RR_WEIGHT_NONE);
+out:
+ print_rr_weight(buff, 13, &mp->rr_weight);
+ condlog(3, "%s: rr_weight = %s %s", mp->alias, buff, origin);
return 0;
}
extern int
select_pgfailback (struct multipath * mp)
{
- if (mp->mpe && mp->mpe->pgfailback != FAILBACK_UNDEF) {
- mp->pgfailback = mp->mpe->pgfailback;
- condlog(3, "%s: pgfailback = %i (LUN setting)",
- mp->alias, mp->pgfailback);
- return 0;
- }
- if (mp->hwe && mp->hwe->pgfailback != FAILBACK_UNDEF) {
- mp->pgfailback = mp->hwe->pgfailback;
- condlog(3, "%s: pgfailback = %i (controller setting)",
- mp->alias, mp->pgfailback);
- return 0;
- }
- if (conf->pgfailback != FAILBACK_UNDEF) {
- mp->pgfailback = conf->pgfailback;
- condlog(3, "%s: pgfailback = %i (config file default)",
- mp->alias, mp->pgfailback);
- return 0;
- }
- mp->pgfailback = DEFAULT_FAILBACK;
- condlog(3, "%s: pgfailover = %i (internal default)",
- mp->alias, mp->pgfailback);
+ char *origin, buff[13];
+
+ mp_set_mpe(pgfailback);
+ mp_set_ovr(pgfailback);
+ mp_set_hwe(pgfailback);
+ mp_set_conf(pgfailback);
+ mp_set_default(pgfailback, DEFAULT_FAILBACK);
+out:
+ print_pgfailback(buff, 13, &mp->pgfailback);
+ condlog(3, "%s: failback = %s %s", mp->alias, buff, origin);
return 0;
}
extern int
select_pgpolicy (struct multipath * mp)
{
- char pgpolicy_name[POLICY_NAME_SIZE];
+ char *origin, buff[POLICY_NAME_SIZE];
if (conf->pgpolicy_flag > 0) {
mp->pgpolicy = conf->pgpolicy_flag;
- mp->pgpolicyfn = pgpolicies[mp->pgpolicy];
- get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE,
- mp->pgpolicy);
- condlog(3, "%s: pgpolicy = %s (cmd line flag)",
- mp->alias, pgpolicy_name);
- return 0;
- }
- if (mp->mpe && mp->mpe->pgpolicy > 0) {
- mp->pgpolicy = mp->mpe->pgpolicy;
- mp->pgpolicyfn = pgpolicies[mp->pgpolicy];
- get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE,
- mp->pgpolicy);
- condlog(3, "%s: pgpolicy = %s (LUN setting)",
- mp->alias, pgpolicy_name);
- return 0;
- }
- if (mp->hwe && mp->hwe->pgpolicy > 0) {
- mp->pgpolicy = mp->hwe->pgpolicy;
- mp->pgpolicyfn = pgpolicies[mp->pgpolicy];
- get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE,
- mp->pgpolicy);
- condlog(3, "%s: pgpolicy = %s (controller setting)",
- mp->alias, pgpolicy_name);
- return 0;
- }
- if (conf->pgpolicy > 0) {
- mp->pgpolicy = conf->pgpolicy;
- mp->pgpolicyfn = pgpolicies[mp->pgpolicy];
- get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE,
- mp->pgpolicy);
- condlog(3, "%s: pgpolicy = %s (config file default)",
- mp->alias, pgpolicy_name);
- return 0;
+ origin = "(cmd line flag)";
+ goto out;
}
- mp->pgpolicy = DEFAULT_PGPOLICY;
+ mp_set_mpe(pgpolicy);
+ mp_set_ovr(pgpolicy);
+ mp_set_hwe(pgpolicy);
+ mp_set_conf(pgpolicy);
+ mp_set_default(pgpolicy, DEFAULT_PGPOLICY);
+out:
mp->pgpolicyfn = pgpolicies[mp->pgpolicy];
- get_pgpolicy_name(pgpolicy_name, POLICY_NAME_SIZE, mp->pgpolicy);
- condlog(3, "%s: pgpolicy = %s (internal default)",
- mp->alias, pgpolicy_name);
+ get_pgpolicy_name(buff, POLICY_NAME_SIZE, mp->pgpolicy);
+ condlog(3, "%s: path_grouping_policy = %s %s", mp->alias, buff, origin);
return 0;
}
extern int
select_selector (struct multipath * mp)
{
- if (mp->mpe && mp->mpe->selector) {
- mp->selector = mp->mpe->selector;
- condlog(3, "%s: selector = %s (LUN setting)",
- mp->alias, mp->selector);
- return 0;
- }
- if (mp->hwe && mp->hwe->selector) {
- mp->selector = mp->hwe->selector;
- condlog(3, "%s: selector = %s (controller setting)",
- mp->alias, mp->selector);
- return 0;
- }
- if (conf->selector) {
- mp->selector = conf->selector;
- condlog(3, "%s: selector = %s (config file default)",
- mp->alias, mp->selector);
- return 0;
- }
- mp->selector = set_default(DEFAULT_SELECTOR);
- condlog(3, "%s: selector = %s (internal default)",
- mp->alias, mp->selector);
+ char *origin;
+
+ mp_set_mpe(selector);
+ mp_set_ovr(selector);
+ mp_set_hwe(selector);
+ mp_set_conf(selector);
+ mp_set_default(selector, DEFAULT_SELECTOR);
+out:
+ mp->selector = STRDUP(mp->selector);
+ condlog(3, "%s: path_selector = \"%s\" %s", mp->alias, mp->selector,
+ origin);
return 0;
}
static void
select_alias_prefix (struct multipath * mp)
{
- if (mp->hwe && mp->hwe->alias_prefix) {
- mp->alias_prefix = mp->hwe->alias_prefix;
- condlog(3, "%s: alias_prefix = %s (controller setting)",
- mp->wwid, mp->alias_prefix);
- return;
- }
- if (conf->alias_prefix) {
- mp->alias_prefix = conf->alias_prefix;
- condlog(3, "%s: alias_prefix = %s (config file default)",
- mp->wwid, mp->alias_prefix);
- return;
- }
- mp->alias_prefix = set_default(DEFAULT_ALIAS_PREFIX);
- condlog(3, "%s: alias_prefix = %s (internal default)",
- mp->wwid, mp->alias_prefix);
+ char *origin;
+
+ mp_set_ovr(alias_prefix);
+ mp_set_hwe(alias_prefix);
+ mp_set_conf(alias_prefix);
+ mp_set_default(alias_prefix, DEFAULT_ALIAS_PREFIX);
+out:
+ condlog(3, "%s: alias_prefix = %s %s", mp->wwid, mp->alias_prefix,
+ origin);
}
static int
want_user_friendly_names(struct multipath * mp)
{
- if (mp->mpe &&
- mp->mpe->user_friendly_names != USER_FRIENDLY_NAMES_UNDEF)
- return (mp->mpe->user_friendly_names == USER_FRIENDLY_NAMES_ON);
- if (mp->hwe &&
- mp->hwe->user_friendly_names != USER_FRIENDLY_NAMES_UNDEF)
- return (mp->hwe->user_friendly_names == USER_FRIENDLY_NAMES_ON);
- return (conf->user_friendly_names == USER_FRIENDLY_NAMES_ON);
+
+ char *origin;
+ int user_friendly_names;
+
+ do_set(user_friendly_names, mp->mpe, user_friendly_names,
+ "(LUN setting)");
+ do_set(user_friendly_names, conf->overrides, user_friendly_names,
+ "(overrides setting)");
+ do_set(user_friendly_names, mp->hwe, user_friendly_names,
+ "(controller setting)");
+ do_set(user_friendly_names, conf, user_friendly_names,
+ "(config file setting)");
+ do_default(user_friendly_names, USER_FRIENDLY_NAMES_OFF);
+out:
+ condlog(3, "%s: user_friendly_names = %s %s", mp->wwid,
+ (user_friendly_names == USER_FRIENDLY_NAMES_ON)? "yes" : "no",
+ origin);
+ return (user_friendly_names == USER_FRIENDLY_NAMES_ON);
}
extern int
select_alias (struct multipath * mp)
{
- if (mp->mpe && mp->mpe->alias)
+ char *origin = NULL;
+
+ if (mp->mpe && mp->mpe->alias) {
mp->alias = STRDUP(mp->mpe->alias);
- else {
- mp->alias = NULL;
- if (want_user_friendly_names(mp)) {
- select_alias_prefix(mp);
- mp->alias = get_user_friendly_alias(mp->wwid,
- conf->bindings_file, mp->alias_prefix, conf->bindings_read_only);
- }
- if (mp->alias == NULL)
- mp->alias = STRDUP(mp->wwid);
+ origin = "(LUN setting)";
+ goto out;
+ }
+
+ mp->alias = NULL;
+ if (!want_user_friendly_names(mp))
+ goto out;
+
+ select_alias_prefix(mp);
+
+ if (strlen(mp->alias_old) > 0) {
+ mp->alias = use_existing_alias(mp->wwid, conf->bindings_file,
+ mp->alias_old, mp->alias_prefix,
+ conf->bindings_read_only);
+ memset (mp->alias_old, 0, WWID_SIZE);
+ origin = "(using existing alias)";
}
+ if (mp->alias == NULL) {
+ mp->alias = get_user_friendly_alias(mp->wwid,
+ conf->bindings_file, mp->alias_prefix, conf->bindings_read_only);
+ origin = "(user_friendly_name)";
+ }
+out:
+ if (mp->alias == NULL) {
+ mp->alias = STRDUP(mp->wwid);
+ origin = "(default to wwid)";
+ }
+ if (mp->alias)
+ condlog(3, "%s: alias = %s %s", mp->wwid, mp->alias, origin);
return mp->alias ? 0 : 1;
}
extern int
select_features (struct multipath * mp)
{
- struct mpentry * mpe;
char *origin;
- if ((mpe = find_mpe(mp->wwid)) && mpe->features) {
- mp->features = STRDUP(mpe->features);
- origin = "LUN setting";
- } else if (mp->hwe && mp->hwe->features) {
- mp->features = STRDUP(mp->hwe->features);
- origin = "controller setting";
- } else if (conf->features) {
- mp->features = STRDUP(conf->features);
- origin = "config file default";
- } else {
- mp->features = set_default(DEFAULT_FEATURES);
- origin = "internal default";
- }
- condlog(3, "%s: features = %s (%s)",
- mp->alias, mp->features, origin);
+ mp_set_mpe(features);
+ mp_set_ovr(features);
+ mp_set_hwe(features);
+ mp_set_conf(features);
+ mp_set_default(features, DEFAULT_FEATURES);
+out:
+ mp->features = STRDUP(mp->features);
+ condlog(3, "%s: features = \"%s\" %s", mp->alias, mp->features, origin);
+
if (strstr(mp->features, "queue_if_no_path")) {
if (mp->no_path_retry == NO_PATH_RETRY_UNDEF)
mp->no_path_retry = NO_PATH_RETRY_QUEUE;
extern int
select_hwhandler (struct multipath * mp)
{
- if (mp->hwe && mp->hwe->hwhandler) {
- mp->hwhandler = mp->hwe->hwhandler;
- condlog(3, "%s: hwhandler = %s (controller setting)",
- mp->alias, mp->hwhandler);
- return 0;
- }
- if (conf->hwhandler) {
- mp->hwhandler = conf->hwhandler;
- condlog(3, "%s: hwhandler = %s (config file default)",
- mp->alias, mp->hwhandler);
- return 0;
- }
- mp->hwhandler = set_default(DEFAULT_HWHANDLER);
- condlog(3, "%s: hwhandler = %s (internal default)",
- mp->alias, mp->hwhandler);
+ char *origin;
+
+ mp_set_hwe(hwhandler);
+ mp_set_conf(hwhandler);
+ mp_set_default(hwhandler, DEFAULT_HWHANDLER);
+out:
+ mp->hwhandler = STRDUP(mp->hwhandler);
+ condlog(3, "%s: hardware_handler = \"%s\" %s", mp->alias, mp->hwhandler,
+ origin);
return 0;
}
extern int
select_checker(struct path *pp)
{
+ char *origin, *checker_name;
struct checker * c = &pp->checker;
- if (pp->hwe && pp->hwe->checker_name) {
- checker_get(c, pp->hwe->checker_name);
- condlog(3, "%s: path checker = %s (controller setting)",
- pp->dev, checker_name(c));
- goto out;
- }
- if (conf->checker_name) {
- checker_get(c, conf->checker_name);
- condlog(3, "%s: path checker = %s (config file default)",
- pp->dev, checker_name(c));
- goto out;
- }
- checker_get(c, DEFAULT_CHECKER);
- condlog(3, "%s: path checker = %s (internal default)",
- pp->dev, checker_name(c));
+ do_set(checker_name, conf->overrides, checker_name, "(overrides setting)");
+ do_set(checker_name, pp->hwe, checker_name, "(controller setting)");
+ do_set(checker_name, conf, checker_name, "(config file setting)");
+ do_default(checker_name, DEFAULT_CHECKER);
out:
+ checker_get(c, checker_name);
+ condlog(3, "%s: path_checker = %s %s", pp->dev, c->name, origin);
if (conf->checker_timeout) {
c->timeout = conf->checker_timeout;
condlog(3, "%s: checker timeout = %u s (config file default)",
extern int
select_getuid (struct path * pp)
{
- if (pp->hwe && pp->hwe->uid_attribute) {
- pp->uid_attribute = pp->hwe->uid_attribute;
- condlog(3, "%s: uid_attribute = %s (controller setting)",
- pp->dev, pp->uid_attribute);
- return 0;
- }
- if (pp->hwe && pp->hwe->getuid) {
- pp->getuid = pp->hwe->getuid;
- condlog(3, "%s: getuid = %s (deprecated) (controller setting)",
- pp->dev, pp->getuid);
- return 0;
- }
- if (conf->uid_attribute) {
- pp->uid_attribute = conf->uid_attribute;
- condlog(3, "%s: uid_attribute = %s (config file default)",
- pp->dev, pp->uid_attribute);
- return 0;
- }
- if (conf->getuid) {
- pp->getuid = conf->getuid;
- condlog(3, "%s: getuid = %s (deprecated) (config file default)",
- pp->dev, pp->getuid);
- return 0;
- }
- pp->uid_attribute = STRDUP(DEFAULT_UID_ATTRIBUTE);
- condlog(3, "%s: uid_attribute = %s (internal default)",
- pp->dev, pp->uid_attribute);
+ char *origin;
+
+ pp_set_ovr(getuid);
+ pp_set_ovr(uid_attribute);
+ pp_set_hwe(getuid);
+ pp_set_hwe(uid_attribute);
+ pp_set_conf(getuid);
+ pp_set_conf(uid_attribute);
+ pp_set_default(uid_attribute, DEFAULT_UID_ATTRIBUTE);
+out:
+ if (pp->uid_attribute)
+ condlog(3, "%s: uid_attribute = %s %s", pp->dev,
+ pp->uid_attribute, origin);
+ else if (pp->getuid)
+ condlog(3, "%s: getuid = \"%s\" %s", pp->dev, pp->getuid,
+ origin);
return 0;
}
{
int ret;
struct prio *p = &pp->prio;
+ int tpgs = 0;
- if (get_target_port_group_support(pp->fd) <= 0)
+ if ((tpgs = get_target_port_group_support(pp->fd)) <= 0)
return;
- ret = get_target_port_group(pp->fd);
+ pp->tpgs = tpgs;
+ ret = get_target_port_group(pp);
if (ret < 0)
return;
if (get_asymmetric_access_state(pp->fd, ret) < 0)
prio_get(p, PRIO_ALUA, DEFAULT_PRIO_ARGS);
}
+#define set_prio(src, msg) \
+do { \
+ if (src && src->prio_name) { \
+ prio_get(p, src->prio_name, src->prio_args); \
+ origin = msg; \
+ goto out; \
+ } \
+} while(0)
+
extern int
select_prio (struct path * pp)
{
+ char *origin;
struct mpentry * mpe;
struct prio * p = &pp->prio;
if (pp->detect_prio == DETECT_PRIO_ON) {
detect_prio(pp);
if (prio_selected(p)) {
- condlog(3, "%s: prio = %s (detected setting)",
- pp->dev, prio_name(p));
- return 0;
- }
- }
-
- if ((mpe = find_mpe(pp->wwid))) {
- if (mpe->prio_name) {
- prio_get(p, mpe->prio_name, mpe->prio_args);
- condlog(3, "%s: prio = %s (LUN setting)",
- pp->dev, prio_name(p));
- return 0;
+ origin = "(detected setting)";
+ goto out;
}
}
-
- if (pp->hwe && pp->hwe->prio_name) {
- prio_get(p, pp->hwe->prio_name, pp->hwe->prio_args);
- condlog(3, "%s: prio = %s (controller setting)",
- pp->dev, pp->hwe->prio_name);
- condlog(3, "%s: prio args = %s (controller setting)",
- pp->dev, pp->hwe->prio_args);
- return 0;
- }
- if (conf->prio_name) {
- prio_get(p, conf->prio_name, conf->prio_args);
- condlog(3, "%s: prio = %s (config file default)",
- pp->dev, conf->prio_name);
- condlog(3, "%s: prio args = %s (config file default)",
- pp->dev, conf->prio_args);
- return 0;
- }
+ mpe = find_mpe(pp->wwid);
+ set_prio(mpe, "(LUN setting)");
+ set_prio(conf->overrides, "(overrides setting)");
+ set_prio(pp->hwe, "controller setting)");
+ set_prio(conf, "(config file default)");
prio_get(p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
- condlog(3, "%s: prio = %s (internal default)",
- pp->dev, DEFAULT_PRIO);
- condlog(3, "%s: prio args = %s (internal default)",
- pp->dev, DEFAULT_PRIO_ARGS);
+ origin = "(internal default)";
+out:
+ /*
+ * fetch tpgs mode for alua, if its not already obtained
+ */
+ if (!strncmp(prio_name(p), PRIO_ALUA, PRIO_NAME_LEN)) {
+ int tpgs = 0;
+ if(!pp->tpgs &&
+ (tpgs = get_target_port_group_support(pp->fd)) >= 0)
+ pp->tpgs = tpgs;
+ }
+ condlog(3, "%s: prio = %s %s", pp->dev, prio_name(p), origin);
+ condlog(3, "%s: prio args = \"%s\" %s", pp->dev, prio_args(p), origin);
return 0;
}
extern int
select_no_path_retry(struct multipath *mp)
{
+ char *origin = NULL;
+ char buff[12];
+
if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) {
condlog(0, "flush_on_last_del in progress");
mp->no_path_retry = NO_PATH_RETRY_FAIL;
return 0;
}
- if (mp->mpe && mp->mpe->no_path_retry != NO_PATH_RETRY_UNDEF) {
- mp->no_path_retry = mp->mpe->no_path_retry;
- condlog(3, "%s: no_path_retry = %i (multipath setting)",
- mp->alias, mp->no_path_retry);
- return 0;
- }
- if (mp->hwe && mp->hwe->no_path_retry != NO_PATH_RETRY_UNDEF) {
- mp->no_path_retry = mp->hwe->no_path_retry;
- condlog(3, "%s: no_path_retry = %i (controller setting)",
- mp->alias, mp->no_path_retry);
- return 0;
- }
- if (conf->no_path_retry != NO_PATH_RETRY_UNDEF) {
- mp->no_path_retry = conf->no_path_retry;
- condlog(3, "%s: no_path_retry = %i (config file default)",
- mp->alias, mp->no_path_retry);
- return 0;
- }
- if (mp->no_path_retry != NO_PATH_RETRY_UNDEF)
- condlog(3, "%s: no_path_retry = %i (inherited setting)",
- mp->alias, mp->no_path_retry);
+ mp_set_mpe(no_path_retry);
+ mp_set_ovr(no_path_retry);
+ mp_set_hwe(no_path_retry);
+ mp_set_conf(no_path_retry);
+out:
+ print_no_path_retry(buff, 12, &mp->no_path_retry);
+ if (origin)
+ condlog(3, "%s: no_path_retry = %s %s", mp->alias, buff,
+ origin);
+ else if (mp->no_path_retry != NO_PATH_RETRY_UNDEF)
+ condlog(3, "%s: no_path_retry = %s (inheritied setting)",
+ mp->alias, buff);
else
- condlog(3, "%s: no_path_retry = %i (internal default)",
- mp->alias, mp->no_path_retry);
+ condlog(3, "%s: no_path_retry = undef (internal default)",
+ mp->alias);
return 0;
}
int
select_minio_rq (struct multipath * mp)
{
- if (mp->mpe && mp->mpe->minio_rq) {
- mp->minio = mp->mpe->minio_rq;
- condlog(3, "%s: minio = %i rq (LUN setting)",
- mp->alias, mp->minio);
- return 0;
- }
- if (mp->hwe && mp->hwe->minio_rq) {
- mp->minio = mp->hwe->minio_rq;
- condlog(3, "%s: minio = %i rq (controller setting)",
- mp->alias, mp->minio);
- return 0;
- }
- if (conf->minio) {
- mp->minio = conf->minio_rq;
- condlog(3, "%s: minio = %i rq (config file default)",
- mp->alias, mp->minio);
- return 0;
- }
- mp->minio = DEFAULT_MINIO_RQ;
- condlog(3, "%s: minio = %i rq (internal default)",
- mp->alias, mp->minio);
+ char *origin;
+
+ do_set(minio_rq, mp->mpe, mp->minio, "(LUN setting)");
+ do_set(minio_rq, conf->overrides, mp->minio, "(overrides setting)");
+ do_set(minio_rq, mp->hwe, mp->minio, "(controller setting)");
+ do_set(minio_rq, conf, mp->minio, "(config file setting)");
+ do_default(mp->minio, DEFAULT_MINIO_RQ);
+out:
+ condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin);
return 0;
}
int
select_minio_bio (struct multipath * mp)
{
- if (mp->mpe && mp->mpe->minio) {
- mp->minio = mp->mpe->minio;
- condlog(3, "%s: minio = %i (LUN setting)",
- mp->alias, mp->minio);
- return 0;
- }
- if (mp->hwe && mp->hwe->minio) {
- mp->minio = mp->hwe->minio;
- condlog(3, "%s: minio = %i (controller setting)",
- mp->alias, mp->minio);
- return 0;
- }
- if (conf->minio) {
- mp->minio = conf->minio;
- condlog(3, "%s: minio = %i (config file default)",
- mp->alias, mp->minio);
- return 0;
- }
- mp->minio = DEFAULT_MINIO;
- condlog(3, "%s: minio = %i (internal default)",
- mp->alias, mp->minio);
+ char *origin;
+
+ mp_set_mpe(minio);
+ mp_set_ovr(minio);
+ mp_set_hwe(minio);
+ mp_set_conf(minio);
+ mp_set_default(minio, DEFAULT_MINIO);
+out:
+ condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin);
return 0;
}
extern int
select_fast_io_fail(struct multipath *mp)
{
- if (mp->hwe && mp->hwe->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
- mp->fast_io_fail = mp->hwe->fast_io_fail;
- if (mp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
- condlog(3, "%s: fast_io_fail_tmo = off "
- "(controller setting)", mp->alias);
- else
- condlog(3, "%s: fast_io_fail_tmo = %d "
- "(controller setting)", mp->alias,
- mp->fast_io_fail == MP_FAST_IO_FAIL_ZERO ? 0 : mp->fast_io_fail);
- return 0;
- }
- if (conf->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
- mp->fast_io_fail = conf->fast_io_fail;
- if (mp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
- condlog(3, "%s: fast_io_fail_tmo = off "
- "(config file default)", mp->alias);
- else
- condlog(3, "%s: fast_io_fail_tmo = %d "
- "(config file default)", mp->alias,
- mp->fast_io_fail == MP_FAST_IO_FAIL_ZERO ? 0 : mp->fast_io_fail);
- return 0;
- }
- mp->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
+ char *origin, buff[12];
+
+ mp_set_ovr(fast_io_fail);
+ mp_set_hwe(fast_io_fail);
+ mp_set_conf(fast_io_fail);
+ mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL);
+out:
+ print_fast_io_fail(buff, 12, &mp->fast_io_fail);
+ condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin);
return 0;
}
extern int
select_dev_loss(struct multipath *mp)
{
- if (mp->hwe && mp->hwe->dev_loss) {
- mp->dev_loss = mp->hwe->dev_loss;
- condlog(3, "%s: dev_loss_tmo = %u (controller default)",
- mp->alias, mp->dev_loss);
- return 0;
- }
- if (conf->dev_loss) {
- mp->dev_loss = conf->dev_loss;
- condlog(3, "%s: dev_loss_tmo = %u (config file default)",
- mp->alias, mp->dev_loss);
- return 0;
- }
+ char *origin, buff[12];
+
+ mp_set_ovr(dev_loss);
+ mp_set_hwe(dev_loss);
+ mp_set_conf(dev_loss);
mp->dev_loss = 0;
return 0;
+out:
+ print_dev_loss(buff, 12, &mp->dev_loss);
+ condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias, buff, origin);
+ return 0;
}
extern int
select_flush_on_last_del(struct multipath *mp)
{
+ char *origin;
+
if (mp->flush_on_last_del == FLUSH_IN_PROGRESS)
return 0;
- if (mp->mpe && mp->mpe->flush_on_last_del != FLUSH_UNDEF) {
- mp->flush_on_last_del = mp->mpe->flush_on_last_del;
- condlog(3, "%s: flush_on_last_del = %i (multipath setting)",
- mp->alias, mp->flush_on_last_del);
- return 0;
- }
- if (mp->hwe && mp->hwe->flush_on_last_del != FLUSH_UNDEF) {
- mp->flush_on_last_del = mp->hwe->flush_on_last_del;
- condlog(3, "%s: flush_on_last_del = %i (controler setting)",
- mp->alias, mp->flush_on_last_del);
- return 0;
- }
- if (conf->flush_on_last_del != FLUSH_UNDEF) {
- mp->flush_on_last_del = conf->flush_on_last_del;
- condlog(3, "%s: flush_on_last_del = %i (config file default)",
- mp->alias, mp->flush_on_last_del);
- return 0;
- }
- mp->flush_on_last_del = FLUSH_UNDEF;
- condlog(3, "%s: flush_on_last_del = DISABLED (internal default)",
- mp->alias);
+ mp_set_mpe(flush_on_last_del);
+ mp_set_ovr(flush_on_last_del);
+ mp_set_hwe(flush_on_last_del);
+ mp_set_conf(flush_on_last_del);
+ mp_set_default(flush_on_last_del, FLUSH_DISABLED);
+out:
+ condlog(3, "%s: flush_on_last_del = %s %s", mp->alias,
+ (mp->flush_on_last_del == FLUSH_ENABLED)? "yes" : "no", origin);
return 0;
}
extern int
select_reservation_key (struct multipath * mp)
{
- int j;
- unsigned char *keyp;
- uint64_t prkey = 0;
+ char *origin, buff[12];
+ mp_set_mpe(reservation_key);
+ mp_set_conf(reservation_key);
mp->reservation_key = NULL;
-
- if (mp->mpe && mp->mpe->reservation_key) {
- keyp = mp->mpe->reservation_key;
- for (j = 0; j < 8; ++j) {
- if (j > 0)
- prkey <<= 8;
- prkey |= *keyp;
- ++keyp;
- }
-
- condlog(3, "%s: reservation_key = 0x%" PRIx64 " "
- "(multipath setting)", mp->alias, prkey);
-
- mp->reservation_key = mp->mpe->reservation_key;
- return 0;
- }
-
- if (conf->reservation_key) {
- keyp = conf->reservation_key;
- for (j = 0; j < 8; ++j) {
- if (j > 0)
- prkey <<= 8;
- prkey |= *keyp;
- ++keyp;
- }
-
- condlog(3, "%s: reservation_key = 0x%" PRIx64
- " (config file default)", mp->alias, prkey);
-
- mp->reservation_key = conf->reservation_key;
- return 0;
- }
-
+ return 0;
+out:
+ print_reservation_key(buff, 12, &mp->reservation_key);
+ condlog(3, "%s: reservation_key = %s %s", mp->alias, buff, origin);
return 0;
}
extern int
select_retain_hwhandler (struct multipath * mp)
{
+ char *origin;
unsigned int minv_dm_retain[3] = {1, 5, 0};
if (!VERSION_GE(conf->version, minv_dm_retain)) {
mp->retain_hwhandler = RETAIN_HWHANDLER_OFF;
- condlog(3, "%s: retain_attached_hw_handler disabled (requires kernel version >= 1.5.0)", mp->alias);
- return 0;
- }
-
- if (mp->hwe && mp->hwe->retain_hwhandler) {
- mp->retain_hwhandler = mp->hwe->retain_hwhandler;
- condlog(3, "%s: retain_attached_hw_handler = %d (controller default)", mp->alias, mp->retain_hwhandler);
- return 0;
- }
- if (conf->retain_hwhandler) {
- mp->retain_hwhandler = conf->retain_hwhandler;
- condlog(3, "%s: retain_attached_hw_handler = %d (config file default)", mp->alias, mp->retain_hwhandler);
- return 0;
+ origin = "(requires kernel version >= 1.5.0)";
+ goto out;
}
- mp->retain_hwhandler = 0;
- condlog(3, "%s: retain_attached_hw_handler = %d (compiled in default)", mp->alias, mp->retain_hwhandler);
+ mp_set_ovr(retain_hwhandler);
+ mp_set_hwe(retain_hwhandler);
+ mp_set_conf(retain_hwhandler);
+ mp_set_default(retain_hwhandler, DEFAULT_RETAIN_HWHANDLER);
+out:
+ condlog(3, "%s: retain_attached_hw_handler = %s %s", mp->alias,
+ (mp->retain_hwhandler == RETAIN_HWHANDLER_ON)? "yes" : "no",
+ origin);
return 0;
}
extern int
select_detect_prio (struct path * pp)
{
- if (pp->hwe && pp->hwe->detect_prio) {
- pp->detect_prio = pp->hwe->detect_prio;
- condlog(3, "%s: detect_prio = %d (controller default)", pp->dev, pp->detect_prio);
- return 0;
- }
- if (conf->detect_prio) {
- pp->detect_prio = conf->detect_prio;
- condlog(3, "%s: detect_prio = %d (config file default)", pp->dev, pp->detect_prio);
+ char *origin;
+
+ pp_set_ovr(detect_prio);
+ pp_set_hwe(detect_prio);
+ pp_set_conf(detect_prio);
+ pp_set_default(detect_prio, DEFAULT_DETECT_PRIO);
+out:
+ condlog(3, "%s: detect_prio = %s %s", pp->dev,
+ (pp->detect_prio == DETECT_PRIO_ON)? "yes" : "no", origin);
+ return 0;
+}
+
+extern int
+select_deferred_remove (struct multipath *mp)
+{
+ char *origin;
+
+#ifndef LIBDM_API_DEFERRED
+ mp->deferred_remove = DEFERRED_REMOVE_OFF;
+ origin = "(not compiled with support)";
+ goto out;
+#endif
+ if (mp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS) {
+ condlog(3, "%s: deferred remove in progress", mp->alias);
return 0;
}
- pp->detect_prio = 0;
- condlog(3, "%s: detect_prio = %d (compiled in default)", pp->dev, pp->detect_prio);
+ mp_set_mpe(deferred_remove);
+ mp_set_ovr(deferred_remove);
+ mp_set_hwe(deferred_remove);
+ mp_set_conf(deferred_remove);
+ mp_set_default(deferred_remove, DEFAULT_DEFERRED_REMOVE);
+out:
+ condlog(3, "%s: deferred_remove = %s %s", mp->alias,
+ (mp->deferred_remove == DEFERRED_REMOVE_ON)? "yes" : "no",
+ origin);
+ return 0;
+}
+
+extern int
+select_delay_watch_checks(struct multipath *mp)
+{
+ char *origin, buff[12];
+
+ mp_set_mpe(delay_watch_checks);
+ mp_set_ovr(delay_watch_checks);
+ mp_set_hwe(delay_watch_checks);
+ mp_set_conf(delay_watch_checks);
+ mp_set_default(delay_watch_checks, DEFAULT_DELAY_CHECKS);
+out:
+ print_delay_checks(buff, 12, &mp->delay_watch_checks);
+ condlog(3, "%s: delay_watch_checks = %s %s", mp->alias, buff, origin);
+ return 0;
+}
+
+extern int
+select_delay_wait_checks(struct multipath *mp)
+{
+ char *origin, buff[12];
+
+ mp_set_mpe(delay_wait_checks);
+ mp_set_ovr(delay_wait_checks);
+ mp_set_hwe(delay_wait_checks);
+ mp_set_conf(delay_wait_checks);
+ mp_set_default(delay_wait_checks, DEFAULT_DELAY_CHECKS);
+out:
+ print_delay_checks(buff, 12, &mp->delay_wait_checks);
+ condlog(3, "%s: delay_wait_checks = %s %s", mp->alias, buff, origin);
return 0;
}
int select_reservation_key(struct multipath *mp);
int select_retain_hwhandler (struct multipath * mp);
int select_detect_prio(struct path * pp);
+int select_deferred_remove(struct multipath *mp);
+int select_delay_watch_checks (struct multipath * mp);
+int select_delay_wait_checks (struct multipath * mp);
#include "blacklist.h"
#include "prio.h"
+struct adapter_group *
+alloc_adaptergroup(void)
+{
+ struct adapter_group *agp;
+
+ agp = (struct adapter_group *)MALLOC(sizeof(struct adapter_group));
+
+ if (!agp)
+ return NULL;
+
+ agp->host_groups = vector_alloc();
+ if (!agp->host_groups) {
+ FREE(agp);
+ agp = NULL;
+ }
+ return agp;
+}
+
+void free_adaptergroup(vector adapters)
+{
+ int i;
+ struct adapter_group *agp;
+
+ vector_foreach_slot(adapters, agp, i) {
+ free_hostgroup(agp->host_groups);
+ FREE(agp);
+ }
+ vector_free(adapters);
+}
+
+void free_hostgroup(vector hostgroups)
+{
+ int i;
+ struct host_group *hgp;
+
+ if (!hostgroups)
+ return;
+
+ vector_foreach_slot(hostgroups, hgp, i) {
+ vector_free(hgp->paths);
+ FREE(hgp);
+ }
+ vector_free(hostgroups);
+}
+
+struct host_group *
+alloc_hostgroup(void)
+{
+ struct host_group *hgp;
+
+ hgp = (struct host_group *)MALLOC(sizeof(struct host_group));
+
+ if (!hgp)
+ return NULL;
+
+ hgp->paths = vector_alloc();
+
+ if (!hgp->paths) {
+ FREE(hgp);
+ hgp = NULL;
+ }
+ return hgp;
+}
+
struct path *
alloc_path (void)
{
if (!mpp)
return;
- if (mpp->selector &&
- mpp->selector != conf->selector &&
- (!mpp->mpe || (mpp->mpe && mpp->selector != mpp->mpe->selector)) &&
- (!mpp->hwe || (mpp->hwe && mpp->selector != mpp->hwe->selector))) {
+ if (mpp->selector) {
FREE(mpp->selector);
mpp->selector = NULL;
}
mpp->features = NULL;
}
- if (mpp->hwhandler &&
- mpp->hwhandler != conf->hwhandler &&
- (!mpp->hwe || (mpp->hwe && mpp->hwhandler != mpp->hwe->hwhandler))) {
+ if (mpp->hwhandler) {
FREE(mpp->hwhandler);
mpp->hwhandler = NULL;
}
return 0;
}
+int
+store_hostgroup(vector hostgroupvec, struct host_group * hgp)
+{
+ if (!vector_alloc_slot(hostgroupvec))
+ return 1;
+
+ vector_set_slot(hostgroupvec, hgp);
+ return 0;
+}
+
+int
+store_adaptergroup(vector adapters, struct adapter_group * agp)
+{
+ if (!vector_alloc_slot(adapters))
+ return 1;
+
+ vector_set_slot(adapters, agp);
+ return 0;
+}
+
struct multipath *
find_mp_by_minor (vector mpvec, int minor)
{
#define BLK_DEV_SIZE 33
#define PATH_SIZE 512
#define NAME_SIZE 512
-
+#define HOST_NAME_LEN 16
+#define SLOT_NAME_SIZE 40
#define SCSI_VENDOR_SIZE 9
#define SCSI_PRODUCT_SIZE 17
PGSTATE_ACTIVE
};
-enum queue_without_daemon_states {
- QUE_NO_DAEMON_OFF,
- QUE_NO_DAEMON_ON,
- QUE_NO_DAEMON_FORCE,
+enum yes_no_states {
+ YN_NO,
+ YN_YES,
};
-enum pgtimeouts {
- PGTIMEOUT_UNDEF,
- PGTIMEOUT_NONE
+enum queue_without_daemon_states {
+ QUE_NO_DAEMON_OFF = YN_NO,
+ QUE_NO_DAEMON_ON = YN_YES,
+ QUE_NO_DAEMON_FORCE,
};
enum attribute_bits {
ATTR_MODE,
};
+enum yes_no_undef_states {
+ YNU_UNDEF,
+ YNU_NO,
+ YNU_YES,
+};
+
enum flush_states {
- FLUSH_UNDEF,
- FLUSH_DISABLED,
- FLUSH_ENABLED,
+ FLUSH_UNDEF = YNU_UNDEF,
+ FLUSH_DISABLED = YNU_NO,
+ FLUSH_ENABLED = YNU_YES,
FLUSH_IN_PROGRESS,
};
};
enum user_friendly_names_states {
- USER_FRIENDLY_NAMES_UNDEF,
- USER_FRIENDLY_NAMES_OFF,
- USER_FRIENDLY_NAMES_ON,
+ USER_FRIENDLY_NAMES_UNDEF = YNU_UNDEF,
+ USER_FRIENDLY_NAMES_OFF = YNU_NO,
+ USER_FRIENDLY_NAMES_ON = YNU_YES,
};
enum retain_hwhandler_states {
- RETAIN_HWHANDLER_UNDEF,
- RETAIN_HWHANDLER_OFF,
- RETAIN_HWHANDLER_ON,
+ RETAIN_HWHANDLER_UNDEF = YNU_UNDEF,
+ RETAIN_HWHANDLER_OFF = YNU_NO,
+ RETAIN_HWHANDLER_ON = YNU_YES,
};
enum detect_prio_states {
- DETECT_PRIO_UNDEF,
- DETECT_PRIO_OFF,
- DETECT_PRIO_ON,
+ DETECT_PRIO_UNDEF = YNU_UNDEF,
+ DETECT_PRIO_OFF = YNU_NO,
+ DETECT_PRIO_ON = YNU_YES,
+};
+
+enum deferred_remove_states {
+ DEFERRED_REMOVE_UNDEF = YNU_UNDEF,
+ DEFERRED_REMOVE_OFF = YNU_NO,
+ DEFERRED_REMOVE_ON = YNU_YES,
+ DEFERRED_REMOVE_IN_PROGRESS,
};
enum scsi_protocol {
SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
};
+enum delay_checks_states {
+ DELAY_CHECKS_OFF = -1,
+ DELAY_CHECKS_UNDEF = 0,
+};
+
+enum initialized_states {
+ INIT_FAILED,
+ INIT_MISSING_UDEV,
+ INIT_REQUESTED_UDEV,
+ INIT_OK,
+};
+
struct sg_id {
int host_no;
int channel;
int priority;
int pgindex;
int detect_prio;
+ int watch_checks;
+ int wait_checks;
+ int tpgs;
char * uid_attribute;
char * getuid;
struct prio prio;
struct checker checker;
struct multipath * mpp;
int fd;
+ int initialized;
+ int retriggers;
/* configlet pointers */
struct hwentry * hwe;
int bestpg;
int queuedio;
int action;
+ int wait_for_udev;
+ int uev_wait_tick;
int pgfailback;
int failback_tick;
int rr_weight;
int attribute_flags;
int fast_io_fail;
int retain_hwhandler;
+ int deferred_remove;
+ int delay_watch_checks;
+ int delay_wait_checks;
unsigned int dev_loss;
uid_t uid;
gid_t gid;
char * selector;
};
+struct adapter_group {
+ char adapter_name[SLOT_NAME_SIZE];
+ struct pathgroup *pgp;
+ int num_hosts;
+ vector host_groups;
+ int next_host_index;
+};
+
+struct host_group {
+ int host_no;
+ int num_paths;
+ vector paths;
+};
+
struct path * alloc_path (void);
struct pathgroup * alloc_pathgroup (void);
struct multipath * alloc_multipath (void);
void drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths);
void free_multipathvec (vector mpvec, enum free_path_mode free_paths);
+struct adapter_group * alloc_adaptergroup(void);
+struct host_group * alloc_hostgroup(void);
+void free_adaptergroup(vector adapters);
+void free_hostgroup(vector hostgroups);
+
+int store_adaptergroup(vector adapters, struct adapter_group *agp);
+int store_hostgroup(vector hostgroupvec, struct host_group *hgp);
+
int store_path (vector pathvec, struct path * pp);
int store_pathgroup (vector pgvec, struct pathgroup * pgp);
#include "debug.h"
#include "structs.h"
#include "structs_vec.h"
+#include "sysfs.h"
#include "waiter.h"
#include "devmapper.h"
#include "dmparser.h"
}
if (pp) {
+ if (!strlen(pp->vendor_id) ||
+ !strlen(pp->product_id) ||
+ !strlen(pp->rev)) {
+ condlog(3, "%s: no device details available", pp->dev);
+ return NULL;
+ }
condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
condlog(3, "%s: product = %s", pp->dev, pp->product_id);
condlog(3, "%s: rev = %s", pp->dev, pp->rev);
return 0;
}
+void sync_paths(struct multipath *mpp, vector pathvec)
+{
+ struct path *pp;
+ struct pathgroup *pgp;
+ int found, i, j;
+
+ vector_foreach_slot (mpp->paths, pp, i) {
+ found = 0;
+ vector_foreach_slot(mpp->pg, pgp, j) {
+ if (find_slot(pgp->paths, (void *)pp) != -1) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
+ vector_del_slot(mpp->paths, i--);
+ orphan_path(pp, "path removed externally");
+ }
+ }
+ update_mpp_paths(mpp, pathvec);
+ vector_foreach_slot (mpp->paths, pp, i)
+ pp->mpp = mpp;
+}
+
extern int
update_multipath_strings (struct multipath *mpp, vector pathvec)
{
if (!mpp)
return 1;
+ update_mpp_paths(mpp, pathvec);
condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
free_multipath_attributes(mpp);
if (update_multipath_table(mpp, pathvec))
return 1;
+ sync_paths(mpp, pathvec);
if (update_multipath_status(mpp))
return 1;
select_pgfailback(mpp);
set_no_path_retry(mpp);
select_flush_on_last_del(mpp);
+ if (VECTOR_SIZE(mpp->paths) != 0)
+ dm_cancel_deferred_remove(mpp);
}
return 0;
return NULL;
}
+static void
+find_existing_alias (struct multipath * mpp,
+ struct vectors *vecs)
+{
+ struct multipath * mp;
+ int i;
+
+ vector_foreach_slot (vecs->mpvec, mp, i)
+ if (strcmp(mp->wwid, mpp->wwid) == 0) {
+ strncpy(mpp->alias_old, mp->alias, WWID_SIZE);
+ return;
+ }
+}
+
extern struct multipath *
add_map_with_path (struct vectors * vecs,
struct path * pp, int add_vec)
{
struct multipath * mpp;
+ if (!strlen(pp->wwid))
+ return NULL;
+
if (!(mpp = alloc_multipath()))
return NULL;
mpp->hwe = pp->hwe;
strcpy(mpp->wwid, pp->wwid);
+ find_existing_alias(mpp, vecs);
if (select_alias(mpp))
goto out;
mpp->size = pp->size;
}
extern int
-verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec)
+verify_paths(struct multipath * mpp, struct vectors * vecs)
{
struct path * pp;
int count = 0;
/*
* see if path is in sysfs
*/
- if (sysfs_get_dev(pp->udev, pp->dev_t, BLK_DEV_SIZE) <= 0) {
+ if (sysfs_attr_get_value(pp->udev, "dev",
+ pp->dev_t, BLK_DEV_SIZE) < 0) {
if (pp->state != PATH_DOWN) {
condlog(1, "%s: removing valid path %s in state %d",
mpp->alias, pp->dev, pp->state);
vector_del_slot(mpp->paths, i);
i--;
- if (rpvec)
- store_path(rpvec, pp);
- else {
- if ((j = find_slot(vecs->pathvec,
- (void *)pp)) != -1)
- vector_del_slot(vecs->pathvec, j);
- free_path(pp);
- }
+ if ((j = find_slot(vecs->pathvec,
+ (void *)pp)) != -1)
+ vector_del_slot(vecs->pathvec, j);
+ free_path(pp);
} else {
condlog(4, "%s: verified path %s dev_t %s",
mpp->alias, pp->dev, pp->dev_t);
return 2;
}
- free_pgvec(mpp->pg, KEEP_PATHS);
- mpp->pg = NULL;
-
if (__setup_multipath(vecs, mpp, reset))
return 1; /* mpp freed in setup_multipath */
- adopt_paths(vecs->pathvec, mpp, 0);
/*
* compare checkers states with DM states
*/
if (pp->state != PATH_DOWN) {
int oldstate = pp->state;
- condlog(2, "%s: mark as failed", pp->dev_t);
+ condlog(2, "%s: mark as failed", pp->dev);
mpp->stat_path_failures++;
pp->state = PATH_DOWN;
if (oldstate == PATH_UP ||
}
}
}
-
return 0;
}
void orphan_paths (vector pathvec, struct multipath * mpp);
void orphan_path (struct path * pp, const char *reason);
-int verify_paths(struct multipath * mpp, struct vectors * vecs, vector rpvec);
+int verify_paths(struct multipath * mpp, struct vectors * vecs);
int update_mpp_paths(struct multipath * mpp, vector pathvec);
int __setup_multipath (struct vectors * vecs, struct multipath * mpp,
int reset);
return -EPERM;
}
+ /* read attribute value */
+ fd = open(devpath, O_RDONLY);
+ if (fd < 0) {
+ condlog(4, "attribute '%s' can not be opened: %s",
+ devpath, strerror(errno));
+ return -errno;
+ }
+ size = read(fd, value, value_len);
+ if (size < 0) {
+ condlog(4, "read from %s failed: %s", devpath, strerror(errno));
+ size = -errno;
+ } else if (size == value_len) {
+ condlog(4, "overflow while reading from %s", devpath);
+ size = 0;
+ } else {
+ value[size] = '\0';
+ }
+
+ close(fd);
+ if (size > 0)
+ size = strchop(value);
+ return size;
+}
+
+ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
+ unsigned char * value, size_t value_len)
+{
+ char devpath[PATH_SIZE];
+ struct stat statbuf;
+ int fd;
+ ssize_t size = -1;
+
+ if (!dev || !attr_name || !value)
+ return 0;
+
+ snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
+ attr_name);
+ condlog(4, "open '%s'", devpath);
+ if (stat(devpath, &statbuf) != 0) {
+ condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
+ return -ENXIO;
+ }
+
+ /* skip directories */
+ if (S_ISDIR(statbuf.st_mode)) {
+ condlog(4, "%s is a directory", devpath);
+ return -EISDIR;
+ }
+
+ /* skip non-writeable files */
+ if ((statbuf.st_mode & S_IRUSR) == 0) {
+ condlog(4, "%s is not readable", devpath);
+ return -EPERM;
+ }
+
/* read attribute value */
fd = open(devpath, O_RDONLY);
if (fd < 0) {
return 1;
attr[0] = '\0';
- if (sysfs_attr_get_value(pp->udev, "size", attr, 255) == 0) {
+ if (sysfs_attr_get_value(pp->udev, "size", attr, 255) <= 0) {
condlog(3, "%s: No size attribute in sysfs", pp->dev);
return 1;
}
char * value, size_t value_len);
ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
char * value, size_t value_len);
+ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
+ unsigned char * value, size_t value_len);
int sysfs_get_size (struct path *pp, unsigned long long * size);
int sysfs_check_holders(char * check_devt, char * new_devt);
#endif
#include <sys/socket.h>
#include <sys/user.h>
#include <sys/un.h>
+#include <sys/poll.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <pthread.h>
+#include <signal.h>
#include <limits.h>
#include <sys/mman.h>
#include <libudev.h>
return 0;
}
+struct uevent *uevent_from_buffer(char *buf, ssize_t buflen)
+{
+ struct uevent *uev;
+ char *buffer;
+ size_t bufpos;
+ int i;
+ char *pos;
+
+ uev = alloc_uevent();
+ if (!uev) {
+ condlog(1, "lost uevent, oom");
+ return NULL;
+ }
+
+ if ((size_t)buflen > sizeof(buf)-1)
+ buflen = sizeof(buf)-1;
+
+ /*
+ * Copy the shared receive buffer contents to buffer private
+ * to this uevent so we can immediately reuse the shared buffer.
+ */
+ memcpy(uev->buffer, buf, HOTPLUG_BUFFER_SIZE + OBJECT_SIZE);
+ buffer = uev->buffer;
+ buffer[buflen] = '\0';
+
+ /* save start of payload */
+ bufpos = strlen(buffer) + 1;
+
+ /* action string */
+ uev->action = buffer;
+ pos = strchr(buffer, '@');
+ if (!pos) {
+ condlog(3, "bad action string '%s'", buffer);
+ FREE(uev);
+ return NULL;
+ }
+ pos[0] = '\0';
+
+ /* sysfs path */
+ uev->devpath = &pos[1];
+
+ /* hotplug events have the environment attached - reconstruct envp[] */
+ for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) {
+ int keylen;
+ char *key;
+
+ key = &buffer[bufpos];
+ keylen = strlen(key);
+ uev->envp[i] = key;
+ /* Filter out sequence number */
+ if (strncmp(key, "SEQNUM=", 7) == 0) {
+ char *eptr;
+
+ uev->seqnum = strtoul(key + 7, &eptr, 10);
+ if (eptr == key + 7)
+ uev->seqnum = -1;
+ }
+ bufpos += keylen + 1;
+ }
+ uev->envp[i] = NULL;
+
+ condlog(3, "uevent %ld '%s' from '%s'", uev->seqnum,
+ uev->action, uev->devpath);
+ uev->kernel = strrchr(uev->devpath, '/');
+ if (uev->kernel)
+ uev->kernel++;
+
+ /* print payload environment */
+ for (i = 0; uev->envp[i] != NULL; i++)
+ condlog(5, "%s", uev->envp[i]);
+
+ return uev;
+}
+
int failback_listen(void)
{
int sock;
}
while (1) {
- int i;
- char *pos;
size_t bufpos;
ssize_t buflen;
struct uevent *uev;
- char *buffer;
struct msghdr smsg;
struct iovec iov;
char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
buflen = sizeof(buf)-1;
}
- uev = alloc_uevent();
-
- if (!uev) {
- condlog(1, "lost uevent, oom");
+ uev = uevent_from_buffer(buf, buflen);
+ if (!uev)
continue;
- }
-
- if ((size_t)buflen > sizeof(buf)-1)
- buflen = sizeof(buf)-1;
-
- /*
- * Copy the shared receive buffer contents to buffer private
- * to this uevent so we can immediately reuse the shared buffer.
- */
- memcpy(uev->buffer, buf, HOTPLUG_BUFFER_SIZE + OBJECT_SIZE);
- buffer = uev->buffer;
- buffer[buflen] = '\0';
-
- /* save start of payload */
- bufpos = strlen(buffer) + 1;
-
- /* action string */
- uev->action = buffer;
- pos = strchr(buffer, '@');
- if (!pos) {
- condlog(3, "bad action string '%s'", buffer);
- continue;
- }
- pos[0] = '\0';
-
- /* sysfs path */
- uev->devpath = &pos[1];
-
- /* hotplug events have the environment attached - reconstruct envp[] */
- for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) {
- int keylen;
- char *key;
-
- key = &buffer[bufpos];
- keylen = strlen(key);
- uev->envp[i] = key;
- /* Filter out sequence number */
- if (strncmp(key, "SEQNUM=", 7) == 0) {
- char *eptr;
-
- uev->seqnum = strtoul(key + 7, &eptr, 10);
- if (eptr == key + 7)
- uev->seqnum = -1;
- }
- bufpos += keylen + 1;
- }
- uev->envp[i] = NULL;
-
- condlog(3, "uevent %ld '%s' from '%s'", uev->seqnum,
- uev->action, uev->devpath);
- uev->kernel = strrchr(uev->devpath, '/');
- if (uev->kernel)
- uev->kernel++;
-
- /* print payload environment */
- for (i = 0; uev->envp[i] != NULL; i++)
- condlog(5, "%s", uev->envp[i]);
-
/*
* Queue uevent and poke service pthread.
*/
return 1;
}
+struct uevent *uevent_from_udev_device(struct udev_device *dev)
+{
+ struct uevent *uev;
+ int i = 0;
+ char *pos, *end;
+ struct udev_list_entry *list_entry;
+
+ uev = alloc_uevent();
+ if (!uev) {
+ udev_device_unref(dev);
+ condlog(1, "lost uevent, oom");
+ return NULL;
+ }
+ pos = uev->buffer;
+ end = pos + HOTPLUG_BUFFER_SIZE + OBJECT_SIZE - 1;
+ udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev)) {
+ const char *name, *value;
+ int bytes;
+
+ name = udev_list_entry_get_name(list_entry);
+ if (!name)
+ name = "(null)";
+ value = udev_list_entry_get_value(list_entry);
+ if (!value)
+ value = "(null)";
+ bytes = snprintf(pos, end - pos, "%s=%s", name, value);
+ if (pos + bytes >= end) {
+ condlog(2, "buffer overflow for uevent");
+ break;
+ }
+ uev->envp[i] = pos;
+ pos += bytes;
+ *pos = '\0';
+ pos++;
+ if (strcmp(name, "DEVPATH") == 0)
+ uev->devpath = uev->envp[i] + 8;
+ if (strcmp(name, "ACTION") == 0)
+ uev->action = uev->envp[i] + 7;
+ i++;
+ if (i == HOTPLUG_NUM_ENVP - 1)
+ break;
+ }
+ uev->udev = dev;
+ uev->envp[i] = NULL;
+
+ condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath);
+ uev->kernel = strrchr(uev->devpath, '/');
+ if (uev->kernel)
+ uev->kernel++;
+
+ /* print payload environment */
+ for (i = 0; uev->envp[i] != NULL; i++)
+ condlog(5, "%s", uev->envp[i]);
+ return uev;
+}
+
int uevent_listen(struct udev *udev)
{
- int err;
+ int err = 2;
struct udev_monitor *monitor = NULL;
- int fd, socket_flags;
+ int fd, fd_ep = -1, socket_flags, events;
int need_failback = 1;
+ int timeout = 30;
+ sigset_t mask;
+ LIST_HEAD(uevlisten_tmp);
+
/*
* Queue uevents for service by dedicated thread so that the uevent
* listening thread does not block on multipathd locks (vecs->lock)
monitor = udev_monitor_new_from_netlink(udev, "udev");
if (!monitor) {
condlog(2, "failed to create udev monitor");
- err = 2;
goto out;
}
#ifdef LIBUDEV_API_RECVBUF
condlog(2, "failed to enable receiving : %s", strerror(-err));
goto out;
}
+
+ pthread_sigmask(SIG_SETMASK, NULL, &mask);
+ sigdelset(&mask, SIGHUP);
+ sigdelset(&mask, SIGUSR1);
+ events = 0;
while (1) {
- int i = 0;
- char *pos, *end;
struct uevent *uev;
struct udev_device *dev;
- struct udev_list_entry *list_entry;
-
- dev = udev_monitor_receive_device(monitor);
- if (!dev) {
- condlog(0, "failed getting udev device");
+ struct pollfd ev_poll;
+ struct timespec poll_timeout;
+ int fdcount;
+
+ memset(&ev_poll, 0, sizeof(struct pollfd));
+ ev_poll.fd = fd;
+ ev_poll.events = POLLIN;
+ memset(&poll_timeout, 0, sizeof(struct timespec));
+ poll_timeout.tv_sec = timeout;
+ errno = 0;
+ fdcount = ppoll(&ev_poll, 1, &poll_timeout, &mask);
+ if (fdcount && ev_poll.revents & POLLIN) {
+ timeout = 0;
+ dev = udev_monitor_receive_device(monitor);
+ if (!dev) {
+ condlog(0, "failed getting udev device");
+ continue;
+ }
+ uev = uevent_from_udev_device(dev);
+ if (!uev)
+ continue;
+ list_add_tail(&uev->node, &uevlisten_tmp);
+ events++;
continue;
}
-
- uev = alloc_uevent();
- if (!uev) {
- udev_device_unref(dev);
- condlog(1, "lost uevent, oom");
- continue;
+ if (fdcount < 0) {
+ if (errno != EINTR)
+ condlog(0, "error receiving "
+ "uevent message: %m");
+ err = -errno;
+ break;
}
- pos = uev->buffer;
- end = pos + HOTPLUG_BUFFER_SIZE + OBJECT_SIZE - 1;
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev)) {
- const char *name, *value;
- int bytes;
-
- name = udev_list_entry_get_name(list_entry);
- if (!name)
- name = "(null)";
- value = udev_list_entry_get_value(list_entry);
- if (!value)
- value = "(null)";
- bytes = snprintf(pos, end - pos, "%s=%s", name,
- value);
- if (pos + bytes >= end) {
- condlog(2, "buffer overflow for uevent");
- break;
- }
- uev->envp[i] = pos;
- pos += bytes;
- *pos = '\0';
- pos++;
- if (strcmp(name, "DEVPATH") == 0)
- uev->devpath = uev->envp[i] + 8;
- if (strcmp(name, "ACTION") == 0)
- uev->action = uev->envp[i] + 7;
- i++;
- if (i == HOTPLUG_NUM_ENVP - 1)
- break;
+ if (!list_empty(&uevlisten_tmp)) {
+ /*
+ * Queue uevents and poke service pthread.
+ */
+ condlog(3, "Forwarding %d uevents", events);
+ pthread_mutex_lock(uevq_lockp);
+ list_splice_tail_init(&uevlisten_tmp, &uevq);
+ pthread_cond_signal(uev_condp);
+ pthread_mutex_unlock(uevq_lockp);
+ events = 0;
}
- uev->udev = dev;
- uev->envp[i] = NULL;
-
- condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath);
- uev->kernel = strrchr(uev->devpath, '/');
- if (uev->kernel)
- uev->kernel++;
-
- /* print payload environment */
- for (i = 0; uev->envp[i] != NULL; i++)
- condlog(5, "%s", uev->envp[i]);
-
- /*
- * Queue uevent and poke service pthread.
- */
- pthread_mutex_lock(uevq_lockp);
- list_add_tail(&uev->node, &uevq);
- pthread_cond_signal(uev_condp);
- pthread_mutex_unlock(uevq_lockp);
+ timeout = 30;
}
need_failback = 0;
out:
+ if (fd_ep >= 0)
+ close(fd_ep);
if (monitor)
udev_monitor_unref(monitor);
if (need_failback)
bytes++;
}
- if (bytes == size)
+ /* If size == 0 there is no space for a final null... */
+ if (size)
*q = '\0';
return bytes;
}
sprintf(block_path,"/sys/dev/block/%u:%u", major, minor);
if (lstat(block_path, &statbuf) == 0) {
if (S_ISLNK(statbuf.st_mode) &&
- readlink(block_path, dev, FILE_NAME_SIZE) > 0) {
+ readlink(block_path, dev, FILE_NAME_SIZE-1) > 0) {
char *p = strrchr(dev, '/');
if (!p) {
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
+#include <mpath_cmd.h>
#include "memory.h"
#include "uxsock.h"
#include "debug.h"
-
-/*
- * connect to a unix domain socket
- */
-int ux_socket_connect(const char *name)
-{
- int fd, len;
- struct sockaddr_un addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_LOCAL;
- addr.sun_path[0] = '\0';
- len = strlen(name) + 1 + sizeof(sa_family_t);
- strncpy(&addr.sun_path[1], name, len);
-
- fd = socket(AF_LOCAL, SOCK_STREAM, 0);
- if (fd == -1) {
- condlog(3, "Couldn't create ux_socket, error %d", errno);
- return -1;
- }
-
- if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
- condlog(3, "Couldn't connect to ux_socket, error %d", errno);
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
/*
* create a unix domain socket and start listening on it
* return a file descriptor open on the socket
/*
* keep reading until its all read
*/
-size_t read_all(int fd, void *buf, size_t len)
+ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout)
{
size_t total = 0;
ssize_t n;
while (len) {
pfd.fd = fd;
pfd.events = POLLIN;
- ret = poll(&pfd, 1, 1000);
+ ret = poll(&pfd, 1, timeout);
if (!ret) {
- errno = ETIMEDOUT;
- return total;
+ return -ETIMEDOUT;
} else if (ret < 0) {
if (errno == EINTR)
continue;
- return total;
+ return -errno;
} else if (!pfd.revents & POLLIN)
continue;
n = read(fd, buf, len);
if (n < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
- return total;
+ return -errno;
}
if (!n)
return total;
/*
* send a packet in length prefix format
*/
-int send_packet(int fd, const char *buf, size_t len)
+int send_packet(int fd, const char *buf)
{
int ret = 0;
sigset_t set, old;
sigaddset(&set, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &set, &old);
- if (write_all(fd, &len, sizeof(len)) != sizeof(len))
- ret = -1;
- if (!ret && write_all(fd, buf, len) != len)
- ret = -1;
+ ret = mpath_send_cmd(fd, buf);
/* And unblock it again */
pthread_sigmask(SIG_SETMASK, &old, NULL);
/*
* receive a packet in length prefix format
*/
-int recv_packet(int fd, char **buf, size_t *len)
+int recv_packet(int fd, char **buf, unsigned int timeout)
{
- if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) {
- (*buf) = NULL;
- *len = 0;
- return -1;
- }
- if (len == 0) {
- (*buf) = NULL;
- return 0;
- }
- (*buf) = MALLOC(*len);
+ int err;
+ ssize_t len;
+
+ *buf = NULL;
+ len = mpath_recv_reply_len(fd, timeout);
+ if (len <= 0)
+ return len;
+ (*buf) = MALLOC(len);
if (!*buf)
- return -1;
- if (read_all(fd, *buf, *len) != *len) {
+ return -ENOMEM;
+ err = mpath_recv_reply_data(fd, *buf, len, timeout);
+ if (err) {
FREE(*buf);
(*buf) = NULL;
- *len = 0;
- return -1;
+ return err;
}
return 0;
}
/* some prototypes */
-int ux_socket_connect(const char *name);
int ux_socket_listen(const char *name);
-int send_packet(int fd, const char *buf, size_t len);
-int recv_packet(int fd, char **buf, size_t *len);
+int send_packet(int fd, const char *buf);
+int recv_packet(int fd, char **buf, unsigned int timeout);
size_t write_all(int fd, const void *buf, size_t len);
-size_t read_all(int fd, void *buf, size_t len);
+ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout);
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000500
-#define DATE_CODE 0x0c110d
+#define VERSION_CODE 0x000600
+#define DATE_CODE 0x120410
#define PROG "multipath-tools"
return ret;
}
+int
+should_multipath(struct path *pp1, vector pathvec)
+{
+ int i;
+ struct path *pp2;
+
+ if (!conf->find_multipaths && !conf->ignore_new_devs)
+ return 1;
+
+ condlog(4, "checking if %s should be multipathed", pp1->dev);
+ if (!conf->ignore_new_devs) {
+ vector_foreach_slot(pathvec, pp2, i) {
+ if (pp1->dev == pp2->dev)
+ continue;
+ if (strncmp(pp1->wwid, pp2->wwid, WWID_SIZE) == 0) {
+ condlog(3, "found multiple paths with wwid %s, "
+ "multipathing %s", pp1->wwid, pp1->dev);
+ return 1;
+ }
+ }
+ }
+ if (check_wwids_file(pp1->wwid, 0) < 0) {
+ condlog(3, "wwid %s not in wwids file, skipping %s",
+ pp1->wwid, pp1->dev);
+ return 0;
+ }
+ condlog(3, "found wwid %s in wwids file, multipathing %s", pp1->wwid,
+ pp1->dev);
+ return 1;
+}
+
int
remember_wwid(char *wwid)
{
"#\n" \
"# Valid WWIDs:\n"
+int should_multipath(struct path *pp, vector pathvec);
int remember_wwid(char *wwid);
int check_wwids_file(char *wwid, int write_wwid);
int remove_wwid(char *wwid);
OBJS = main.o
CFLAGS += -I$(multipathdir) -I$(mpathpersistdir)
-LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath -ludev
+LDFLAGS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -L$(mpathcmddir) -lmpathcmd -lmultipath -ludev
EXEC = mpathpersist
# #
# # name : checker_timeout
# # scope : multipath & multipathd
-# # desc : The timeout to use for path checkers that issue scsi
-# # commands with an explicit timeout, in seconds.
+# # desc : The timeout to use for path checkers and prioritizers
+# # that issue scsi commands with an explicit timeout, in
+# # seconds.
# # values : n > 0
# # default : taken from /sys/block/sd<x>/device/timeout
# checker_timeout 60
# # default : determined by the OS
# dev_loss_tmo 600
#
+# #
# # name : bindings_file
# # scope : multipath
# # desc : The location of the bindings file that is used with
# # default : "/var/lib/multipath/bindings"
# bindings_file "/etc/multipath/bindings"
#
+# #
# # name : wwids_file
# # scope : multipath
# # desc : The location of the wwids file multipath uses to
# # default : "/var/lib/multipath/wwids"
# wwids_file "/etc/multipath/wwids"
#
+# #
# # name : reservation_key
# # scope : multipath
# # desc : Service action reservation key used by mpathpersist.
# # default : (null)
# reservation_key "mpathkey"
#
+# #
+# # name : force_sync
+# # scope : multipathd
+# # desc : If set to yes, multipath will run all of the checkers in
+# # sync mode, even if the checker has an async mode.
+# # values : yes|no
+# # default : no
+# force_sync yes
+#
+# #
+# # name : config_dir
+# # scope : multipath & multipathd
+# # desc : If not set to an empty string, multipath will search
+# # this directory alphabetically for files ending in ".conf"
+# # and it will read configuration information from these
+# # files, just as if it was in /etc/multipath.conf
+# # values : "" or a fully qualified pathname
+# # default : "/etc/multipath/conf.d"
+#
+# #
+# # name : delay_watch_checks
+# # scope : multipathd
+# # desc : If set to a value greater than 0, multipathd will watch
+# # paths that have recently become valid for this many
+# # checks. If they fail again while they are being watched,
+# # when they next become valid, they will not be used until
+# # they have stayed up for delay_wait_checks checks.
+# # values : no|<n> > 0
+# # default : no
+# delay_watch_checks 12
+#
+# #
+# # name : delay_wait_checks
+# # scope : multipathd
+# # desc : If set to a value greater than 0, when a device that has
+# # recently come back online fails again within
+# # delay_watch_checks checks, the next time it comes back
+# # online, it will marked and delayed, and not used until
+# # it has passed delay_wait_checks checks.
+# # values : no|<n> > 0
+# # default : no
+# delay_wait_checks 12
#}
#
##
# # default : determined by the process
# gid 0
#
+# #
+# # name : delay_watch_checks
+# # scope : multipathd
+# # desc : If set to a value greater than 0, multipathd will
+# # watch paths that have recently become valid for
+# # this many checks. If they fail again while they
+# # are being watched, when they next become valid,
+# # they will not be used until they have stayed up for
+# # delay_wait_checks checks.
+# # values : no|<n> > 0
+# delay_watch_checks 12
+#
+# #
+# # name : delay_wait_checks
+# # scope : multipathd
+# # desc : If set to a value greater than 0, when a device
+# # that has recently come back online fails again
+# # within delay_watch_checks checks, the next time it
+# # comes online, it will marked and delayed, and not
+# # used until it has passed delay_wait_checks checks.
+# # values : no|<n> > 0
+# delay_wait_checks 12
# }
# multipath {
# wwid 1DEC_____321816758474
## scope : multipath & multipathd
## desc : list of per storage controller settings
## overrides default settings (device_maps block)
-## overriden by per multipath settings (multipaths block)
+## overriden by per multipath settings (multipaths block)
+## and the overrides settings (overrides block)
##
#devices {
# #
# # before removing it from the system.
# # values : n > 0
# dev_loss_tmo 600
+#
+# #
+# # name : delay_watch_checks
+# # scope : multipathd
+# # desc : If set to a value greater than 0, multipathd will
+# # watch paths that have recently become valid for
+# # this many checks. If they fail again while they
+# # are being watched, when they next become valid,
+# # they will not be used until they have stayed up for
+# # delay_wait_checks checks.
+# # values : no|<n> > 0
+# delay_watch_checks 12
+#
+# #
+# # name : delay_wait_checks
+# # scope : multipathd
+# # desc : If set to a value greater than 0, when a device
+# # that has recently come back online fails again
+# # within delay_watch_checks checks, the next time it
+# # comes online, it will marked and delayed, and not
+# # used until it has passed delay_wait_checks checks.
+# # values : no|<n> > 0
+# delay_wait_checks 12
+#
# }
# device {
# vendor "COMPAQ "
# rr_weight priorities
# }
#}
+#
+##
+## name : devices
+## scope : multipath & multipathd
+## desc : list of settings to override all hadware settings for all devices
+## overrides default settings (device_maps block)
+## and per device type settings (devices block)
+## overriden by per multipath settings (multipaths block)
+##
+# attributes and values are identical to the device block
+#
+#overrides {
+# dev_loss_tmo 60
+# no_path_retry fail
+#}
# log_checker_err always
# retain_attached_hw_handler no
# detect_prio no
+# config_dir "/etc/multipath/conf.d"
+# delay_watch_checks no
+# delay_wait_checks no
+# missing_uev_wait_timeout 30
#}
#blacklist {
# devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
-# devnode "^hd[a-z]"
+# devnode "^(td|hd|vd)[a-z]"
# devnode "^dcssblk[0-9]*"
+# devnode "^nvme.*"
# device {
# vendor "DGC"
# product "LUNZ"
# }
#}
#blacklist_exceptions {
-# property "(ID_SCSI_VPD|ID_WWN)"
+# property "(SCSI_IDENT_.*|ID_WWN)"
#}
#devices {
# device {
# failback "immediate"
# rr_weight "uniform"
# no_path_retry 60
+# retain_attached_hw_handler yes
+# detect_prio yes
# }
# device {
# vendor "EMC"
# }
# device {
# vendor "FUJITSU"
-# product "ETERNUS_DX(L|400|8000)"
+# product "ETERNUS_DX(L|M|400|8000)"
# path_grouping_policy "group_by_prio"
# path_checker "tur"
# features "1 queue_if_no_path"
# no_path_retry 15
# }
# device {
+# vendor "DELL"
+# product "MD36xxi"
+# product_blacklist "Universal Xport"
+# path_grouping_policy "group_by_prio"
+# path_checker "rdac"
+# features "2 pg_init_retries 50"
+# hardware_handler "1 rdac"
+# prio "rdac"
+# failback "immediate"
+# rr_weight "uniform"
+# no_path_retry 15
+# }
+# device {
+# vendor "DELL"
+# product "MD36xxf"
+# product_blacklist "Universal Xport"
+# path_grouping_policy "group_by_prio"
+# path_checker "rdac"
+# features "2 pg_init_retries 50"
+# hardware_handler "1 rdac"
+# prio "rdac"
+# failback "immediate"
+# rr_weight "uniform"
+# no_path_retry 15
+# }
+# device {
# vendor "NETAPP"
# product "LUN.*"
# path_grouping_policy "group_by_prio"
# rr_weight "uniform"
# no_path_retry "queue"
# }
+# device {
+# vendor "PURE"
+# path_selector "queue-length 0"
+# path_grouping_policy "multibus"
+# path_checker "tur"
+# fast_io_fail_tmo 10
+# user_friendly_names "no"
+# no_path_retry 0
+# features 0
+# dev_loss_tmo 60
+# }
#}
#multipaths {
#}
+#overrides {
+#}
# path_grouping_policy multibus
# }
#}
+#overrides {
+# no_path_retry fail
+#}
--- /dev/null
+ACTION!="add|change", GOTO="mpath_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="mpath_end"
+ENV{DM_UUID}!="mpath-?*", GOTO="mpath_end"
+
+# Do not initiate scanning if no path is available,
+# otherwise there would be a hang or IO error on access.
+# We'd like to avoid this, especially within udev processing.
+ENV{DM_NR_VALID_PATHS}!="?*", IMPORT{db}="DM_NR_VALID_PATHS"
+ENV{DM_NR_VALID_PATHS}!="0", GOTO="mpath_blkid_end"
+IMPORT{db}="ID_FS_TYPE"
+IMPORT{db}="ID_FS_USAGE"
+IMPORT{db}="ID_FS_UUID"
+IMPORT{db}="ID_FS_UUID_ENC"
+IMPORT{db}="ID_FS_VERSION"
+LABEL="mpath_blkid_end"
+
+# Also skip all foreign rules if no path is available.
+# Remember the original value of DM_DISABLE_OTHER_RULES_FLAG
+# and restore it back once we have at least one path available.
+IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD"
+ENV{DM_ACTION}=="PATH_FAILED",\
+ ENV{DM_NR_VALID_PATHS}=="0",\
+ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\
+ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}",\
+ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+ENV{DM_ACTION}=="PATH_REINSTATED",\
+ ENV{DM_NR_VALID_PATHS}=="1",\
+ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\
+ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\
+ ENV{DM_ACTIVATION}="1"
+
+# DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem.
+# Drop the DM_ACTIVATION flag here as mpath reloads tables if any of its
+# paths are lost/recovered. For any stack above the mpath device, this is not
+# something that should be reacted upon since it would be useless extra work.
+# It's exactly mpath's job to provide *seamless* device access to any of the
+# paths that are available underneath.
+ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0"
+
+LABEL="mpath_end"
OBJS = main.o
-CFLAGS += -I$(multipathdir)
-LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev
+CFLAGS += -I$(multipathdir) -I$(mpathcmddir)
+LDFLAGS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev \
+ -L$(mpathcmddir) -lmpathcmd
EXEC = multipath
install:
$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
+ $(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
+ $(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
+ $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
$(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)
$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(mandir)
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir)
uninstall:
rm $(DESTDIR)$(bindir)/$(EXEC)
+ rm $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules
+ rm $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
rm $(DESTDIR)$(mandir)/$(EXEC).8.gz
rm $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz
+++ /dev/null
-#define MAJOR(dev) ((dev & 0xfff00) >> 8)
-#define MINOR(dev) ((dev & 0xff) | ((dev >> 12) & 0xfff00))
-#define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
#include <unistd.h>
#include <ctype.h>
#include <libudev.h>
+#include <syslog.h>
#include <checkers.h>
#include <prio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <wwids.h>
-#include "dev_t.h"
+#include <uxsock.h>
+#include <mpath_cmd.h>
int logsink;
{
fprintf (stderr, VERSION_STRING);
fprintf (stderr, "Usage:\n");
- fprintf (stderr, " %s [-c|-w|-W] [-d] [-r] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname);
+ fprintf (stderr, " %s [-a|-c|-w|-W] [-d] [-r] [-i] [-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);
" -ll show multipath topology (maximum info)\n" \
" -f flush a multipath device map\n" \
" -F flush all multipath device maps\n" \
+ " -a add a device wwid to the wwids file\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" \
" -r force devmap reload\n" \
+ " -i ignore wwids file\n" \
" -B treat the bindings file as read only\n" \
- " -p policy failover|multibus|group_by_serial|group_by_prio\n" \
" -b fil bindings file location\n" \
" -w remove a device from the wwids file\n" \
" -W reset the wwids file include only the current devices\n" \
continue;
}
+ if (conf->cmd == CMD_VALID_PATH)
+ continue;
+
dm_get_map(mpp->alias, &mpp->size, params);
condlog(3, "params = %s", params);
dm_get_status(mpp->alias, status);
* If not in "fast list mode", we need to fetch information
* about them
*/
- if (conf->list != 1)
+ if (conf->cmd != CMD_LIST_SHORT)
update_paths(mpp);
- if (conf->list > 1)
+ if (conf->cmd == CMD_LIST_LONG)
mpp->bestpg = select_path_group(mpp);
disassemble_status(status, mpp);
- if (conf->list)
+ if (conf->cmd == CMD_LIST_SHORT ||
+ conf->cmd == CMD_LIST_LONG)
print_multipath_topology(mpp, conf->verbosity);
- if (!conf->dry_run)
+ if (conf->cmd == CMD_CREATE)
reinstate_paths(mpp);
}
return 0;
/*
* if we have a blacklisted device parameter, exit early
*/
- if (dev && conf->dev_type == DEV_DEVNODE && conf->dry_run != 3 &&
+ if (dev && conf->dev_type == DEV_DEVNODE &&
+ conf->cmd != CMD_REMOVE_WWID &&
(filter_devnode(conf->blist_devnode,
conf->elist_devnode, dev) > 0)) {
- if (conf->dry_run == 2)
+ if (conf->cmd == CMD_VALID_PATH)
printf("%s is not a valid multipath device path\n",
conf->dev);
goto out;
int failed = get_refwwid(conf->dev, conf->dev_type, pathvec,
&refwwid);
if (!refwwid) {
- if (failed == 2 && conf->dry_run == 2)
+ condlog(3, "%s: failed to get wwid", conf->dev);
+ if (failed == 2 && conf->cmd == CMD_VALID_PATH)
printf("%s is not a valid multipath device path\n", conf->dev);
else
condlog(3, "scope is nul");
goto out;
}
- if (conf->dry_run == 3) {
+ if (conf->cmd == CMD_REMOVE_WWID) {
r = remove_wwid(refwwid);
if (r == 0)
printf("wwid '%s' removed\n", refwwid);
}
goto out;
}
+ if (conf->cmd == CMD_ADD_WWID) {
+ r = remember_wwid(refwwid);
+ if (r == 0)
+ printf("wwid '%s' added\n", refwwid);
+ else
+ printf("failed adding '%s' to wwids file\n",
+ refwwid);
+ goto out;
+ }
condlog(3, "scope limited to %s", refwwid);
- if (conf->dry_run == 2) {
- if (check_wwids_file(refwwid, 0) == 0){
- printf("%s is a valid multipath device path\n", conf->dev);
+ /* If you are ignoring the wwids file and find_multipaths is
+ * set, you need to actually check if there are two available
+ * paths to determine if this path should be multipathed. To
+ * do this, we put off the check until after discovering all
+ * the paths */
+ if (conf->cmd == CMD_VALID_PATH &&
+ (!conf->find_multipaths || !conf->ignore_wwids)) {
+ if (conf->ignore_wwids ||
+ check_wwids_file(refwwid, 0) == 0)
r = 0;
- }
- else
- printf("%s is not a valid multipath device path\n", conf->dev);
+
+ printf("%s %s a valid multipath device path\n",
+ conf->dev, r == 0 ? "is" : "is not");
goto out;
}
}
if (conf->dev)
di_flag = DI_WWID;
- if (conf->list > 1)
+ if (conf->cmd == CMD_LIST_LONG)
/* extended path info '-ll' */
di_flag |= DI_SYSFS | DI_CHECKER;
- else if (conf->list)
+ else if (conf->cmd == CMD_LIST_SHORT)
/* minimum path info '-l' */
di_flag |= DI_SYSFS;
else
/* maximum info */
di_flag = DI_ALL;
- if (path_discovery(pathvec, conf, di_flag))
+ if (path_discovery(pathvec, conf, di_flag) < 0)
goto out;
if (conf->verbosity > 2)
filter_pathvec(pathvec, refwwid);
- if (conf->list) {
+
+ if (conf->cmd == CMD_VALID_PATH) {
+ /* This only happens if find_multipaths is and
+ * ignore_wwids is set.
+ * If there is currently a multipath device matching
+ * the refwwid, or there is more than one path matching
+ * the refwwid, then the path is valid */
+ if (VECTOR_SIZE(curmp) != 0 || VECTOR_SIZE(pathvec) > 1)
+ r = 0;
+ printf("%s %s a valid multipath device path\n",
+ conf->dev, r == 0 ? "is" : "is not");
+ goto out;
+ }
+
+ if (conf->cmd != CMD_CREATE && conf->cmd != CMD_DRY_RUN) {
r = 0;
goto out;
}
/*
* core logic entry point
*/
- r = coalesce_paths(&vecs, NULL, NULL, conf->force_reload);
+ r = coalesce_paths(&vecs, NULL, refwwid, conf->force_reload);
out:
if (refwwid)
static int
dump_config (void)
{
- char * c;
+ char * c, * tmp = NULL;
char * reply;
unsigned int maxlen = 256;
int again = 1;
reply = MALLOC(maxlen);
while (again) {
- if (!reply)
+ if (!reply) {
+ if (tmp)
+ free(tmp);
return 1;
- c = reply;
+ }
+ c = tmp = reply;
c += snprint_defaults(c, reply + maxlen - c);
again = ((c - reply) == maxlen);
if (again) {
reply = REALLOC(reply, maxlen *= 2);
continue;
}
- c += snprint_mptable(c, reply + maxlen - c, conf->mptable);
+ c += snprint_overrides(c, reply + maxlen - c, conf->overrides);
again = ((c - reply) == maxlen);
- if (again)
+ if (again) {
reply = REALLOC(reply, maxlen *= 2);
+ continue;
+ }
+ if (VECTOR_SIZE(conf->mptable) > 0) {
+ c += snprint_mptable(c, reply + maxlen - c,
+ conf->mptable);
+ again = ((c - reply) == maxlen);
+ if (again)
+ reply = REALLOC(reply, maxlen *= 2);
+ }
}
printf("%s", reply);
int i;
if (stat(dev, &buf) == 0 && S_ISBLK(buf.st_mode)) {
- if (dm_is_dm_major(MAJOR(buf.st_rdev)))
+ if (dm_is_dm_major(major(buf.st_rdev)))
return DEV_DEVMAP;
return DEV_DEVNODE;
}
int r = 1;
udev = udev_new();
-
+ logsink = 0;
if (load_config(DEFAULT_CONFIGFILE, udev))
exit(1);
- while ((arg = getopt(argc, argv, ":dchl::FfM:v:p:b:BrtqwW")) != EOF ) {
+ while ((arg = getopt(argc, argv, ":adchl::FfM:v:p:b:BritquwW")) != EOF ) {
switch(arg) {
case 1: printf("optarg : %s\n",optarg);
break;
conf->allow_queueing = 1;
break;
case 'c':
- conf->dry_run = 2;
+ conf->cmd = CMD_VALID_PATH;
break;
case 'd':
- if (!conf->dry_run)
- conf->dry_run = 1;
+ if (conf->cmd == CMD_CREATE)
+ conf->cmd = CMD_DRY_RUN;
break;
case 'f':
conf->remove = FLUSH_ONE;
conf->remove = FLUSH_ALL;
break;
case 'l':
- conf->list = 1;
- conf->dry_run = 1;
-
if (optarg && !strncmp(optarg, "l", 1))
- conf->list++;
+ conf->cmd = CMD_LIST_LONG;
+ else
+ conf->cmd = CMD_LIST_SHORT;
break;
case 'M':
case 'r':
conf->force_reload = 1;
break;
+ case 'i':
+ conf->ignore_wwids = 1;
+ break;
case 't':
r = dump_config();
goto out_free_config;
case 'h':
usage(argv[0]);
exit(0);
+ case 'u':
+ conf->cmd = CMD_VALID_PATH;
+ conf->dev_type = DEV_UEVENT;
+ break;
case 'w':
- conf->dry_run = 3;
+ conf->cmd = CMD_REMOVE_WWID;
break;
case 'W':
- conf->dry_run = 4;
+ conf->cmd = CMD_RESET_WWIDS;
+ break;
+ case 'a':
+ conf->cmd = CMD_ADD_WWID;
break;
case ':':
fprintf(stderr, "Missing option argument\n");
if (dm_prereq())
exit(1);
dm_drv_version(conf->version, TGT_MPATH);
+ dm_udev_set_sync_support(1);
if (optind < argc) {
conf->dev = MALLOC(FILE_NAME_SIZE);
goto out;
strncpy(conf->dev, argv[optind], FILE_NAME_SIZE);
- conf->dev_type = get_dev_type(conf->dev);
+ if (conf->dev_type != DEV_UEVENT)
+ conf->dev_type = get_dev_type(conf->dev);
}
conf->daemon = 0;
+ if (conf->dev_type == DEV_UEVENT) {
+ openlog("multipath", 0, LOG_DAEMON);
+ setlogmask(LOG_UPTO(conf->verbosity + 3));
+ logsink = 1;
+ }
if (conf->max_fds) {
struct rlimit fd_limit;
}
dm_init();
- if (conf->dry_run == 2 &&
+ if (conf->cmd == CMD_VALID_PATH &&
(!conf->dev || conf->dev_type == DEV_DEVMAP)) {
condlog(0, "the -c option requires a path to check");
goto out;
}
- if (conf->dry_run == 3 && !conf->dev) {
+ if (conf->cmd == CMD_VALID_PATH &&
+ conf->dev_type == DEV_UEVENT) {
+ int fd;
+
+ fd = mpath_connect();
+ if (fd == -1) {
+ printf("%s is not a valid multipath device path\n",
+ conf->dev);
+ goto out;
+ }
+ mpath_disconnect(fd);
+ }
+ if (conf->cmd == CMD_REMOVE_WWID && !conf->dev) {
condlog(0, "the -w option requires a device");
goto out;
}
- if (conf->dry_run == 4) {
+ if (conf->cmd == CMD_RESET_WWIDS) {
struct multipath * mpp;
int i;
vector curmp;
condlog(3, "restart multipath configuration process");
out:
- udev_wait(conf->cookie);
-
dm_lib_release();
dm_lib_exit();
cleanup_prio();
cleanup_checkers();
+ if (conf->dev_type == DEV_UEVENT)
+ closelog();
+
out_free_config:
/*
* Freeing config must be done after dm_lib_exit(), because
.RB [\| \-b\ \c
.IR bindings_file \|]
.RB [\| \-d \|]
-.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-q | \|-r | \-w | \-W \|]
+.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-q | \|-r | \|-i | \-a | \|-u | \-w | \-W \|]
.RB [\| \-p\ \c
.BR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|]
.RB [\| device \|]
.B \-r
force devmap reload
.TP
+.B \-i
+ignore wwids file when processing devices
+.TP
.B \-B
treat the bindings file as read only
.TP
.B \-q
allow device tables with queue_if_no_path when multipathd is not running
.TP
+.B \-a
+add the wwid for the specified device to the wwids file
+.TP
+.B \-u
+check if the device specified in the program environment should be
+a path in a multipath device.
+.TP
.B \-w
remove the wwid for the specified device from the wwids file
.TP
.TP
.B multipaths
This section defines the multipath topologies. They are indexed by a
-\fIWorld Wide Identifier\fR(wwid), which is taken to be the value of
-the udev attribute given by the
-\fIuid_attribute\fR keyword.
+\fIWorld Wide Identifier\fR(wwid). For details on the wwid generation
+see section \fBWWID generation\fR below.
.TP
.B devices
This section defines the device-specific settings.
+.TP
+.B overrides
+This section defines values for attributes that should override the
+device-specific settings for all devices.
.RE
.LP
.SH "defaults section"
dependent, commonly
.I /lib/multipath
.TP
+.B find_multipaths
+If set to
+.I yes
+, instead of trying to create a multipath device for every non-blacklisted
+path, multipath will only create a device if one of three condidions are
+met.
+.I 1
+There are at least two non-blacklisted paths with the same wwid,
+.I 2
+the user manually forces the creation, by specifying a device with the multipath
+command, or
+.I 3
+a path has the same WWID as a multipath device that was previously created
+while find_multipaths was set (even if that multipath device doesn't currently
+exist).
+Whenever a multipath device is created with find_multipaths set, multipath will
+remeber the WWID of the device, so that it will automatically create the
+device again, as soon as it sees a path with that WWID. This should allow most
+users to have multipath automatically choose the correct paths to make into
+multipath devices, without having to edit the blacklist; Default is
+.I no
+.TP
.B verbosity
default verbosity. Higher values increase the verbosity level. Valid
levels are between 0 and 6; default is
.B getuid_callout
The default program and args to callout to obtain a unique path
identifier. Should be specified with an absolute path.
-This parameter is deprecated; \fIuid_attribute\fR should be used instead.
+This parameter is deprecated.
.TP
.B prio
The name of the path priority routine. The specified routine
Generate the path priority for EMC arrays.
.TP
.B alua
-Generate the path priority based on the SCSI-3 ALUA settings.
+Generate the path priority based on the SCSI-3 ALUA settings. This prioritizer
+accepts the optional prio_arg
+.I exclusive_pref_bit
.TP
.B ontap
Generate the path priority for NetApp arrays.
.RE
.TP
.B prio_args
-Arguments to pass to to the prio function. Currently only used with
-.I weighted, which needs a value of the form
-.I "<hbtl|devname> <regex1> <prio1> <regex2> <prio2> ..."
+Arguments to pass to to the prio function. This only applies to certain
+prioritizers
+.RS
+.TP 12
+.B weighted
+Needs a value of the form
+.I "<hbtl|devname|wwn> <regex1> <prio1> <regex2> <prio2> ..."
.I hbtl
regex can be of SCSI H:B:T:L format Ex: 1:0:.:. , *:0:0:.
.I devname
regex can be of device name format Ex: sda , sd.e
+.I wwn
+regex can be of the form
+.I "host_wwnn:host_wwpn:target_wwnn:target_wwpn"
+these values can be looked up through sysfs or by running
+.I mulitpathd show paths format "%N:%R:%n:%r" Ex: 0x200100e08ba0aea0:0x210100e08ba0aea0:.*:.* , .*:.*:iqn.2009-10.com.redhat.msp.lab.ask-06:.*
+.TP
+.B alua
+If
+.I exclusive_pref_bit
+is set, paths with the TPGS pref bit set will always be in their own path
+group.
+.RE
.TP
.B features
Specify any device-mapper features to be used. Syntax is
if that number is greated than 1024.
.TP
.B checker_timeout
-Specify the timeout to user for path checkers that issue scsi commands with an
-explicit timeout, in seconds; default taken from
+Specify the timeout to use for path checkers and prioritizers that issue scsi
+commands with an explicit timeout, in seconds; default taken from
.I /sys/block/sd<x>/device/timeout
.TP
.B fast_io_fail_tmo
cannot be told to stop queueing IO. Setting queue_without_daemon to
.I no
, avoids this problem. Default is
-.I yes
+.I no
.TP
.B bindings_file
The full pathname of the binding file to be used when the user_friendly_names option is set. Defaults to
.I alua
prioritizer. If not, the prioritizer will be selected as usual. Default is
.I no
+.TP
+.B force_sync
+If set to
+.I yes
+, multipathd will call the path checkers in sync mode only. This means that
+only one checker will run at a time. This is useful in the case where many
+multipathd checkers running in parallel causes significant CPU pressure. The
+Default is
+.I no
+.TP
+.B deferred_remove
+If set to
+.I yes
+, multipathd will do a deferred remove instead of a regular remove when the
+last path device has been deleted. This means that if the multipath device is
+still in use, it will be freed when the last user closes it. If path is added
+to the multipath device before the last user closes it, the deferred remove
+will be canceled. Default is
+.I no
+.TP
+.B config_dir
+If set to anything other than "", multipath will search this directory
+alphabetically for file ending in ".conf" and it will read configuration
+information from them, just as if it was in /etc/multipath.conf. config_dir
+must either be "" or a fully qualified directory name. Default is
+.I "/etc/multipath/conf.d"
+.TP
+.B delay_watch_checks
+If set to a value greater than 0, multipathd will watch paths that have
+recently become valid for this many checks. If they fail again while they are
+being watched, when they next become valid, they will not be used until they
+have stayed up for
+.I delay_wait_checks
+checks. Default is
+.I no
+.TP
+.B delay_wait_checks
+If set to a value greater than 0, when a device that has recently come back
+online fails again within
+.I delay_watch_checks
+checks, the next time it comes back online, it will marked and delayed, and not
+used until it has passed
+.I delay_wait_checks
+checks. Default is
+.I no
+.TP
+.B uxsock_timeout
+CLI receive timeout in milliseconds. For larger systems CLI commands
+might timeout before the multipathd lock is released and the CLI command
+can be processed. This will result in errors like
+'timeout receiving packet' to be returned from CLI commands.
+In these cases it is recommended to increase the CLI timeout to avoid
+those issues. The default is
+.I 1000
+.TP
+.B missing_uev_wait_timeout
+Controls how many seconds multipathd will wait, after a new multipath device
+is created, to receive a change event from udev for the device, before
+automatically enabling device reloads. Usually multipathd will delay reloads
+on a device until it receives a change uevent from the initial table load. The
+default is
+.I 30
.
.SH "blacklist section"
The
Regular expression of the device nodes to be excluded.
.TP
.B property
-Regular expresion of the udev property to be excluded.
+Regular expression of the udev property to be excluded.
.TP
.B device
Subsection for the device description. This subsection recognizes the
The \fIWorld Wide Identification\fR of a device.
.TP
.B property
-Regular expresion of the udev property to be whitelisted. Defaults to
-.I (ID_WWN|ID_SCSI_VPD)
+Regular expression of the udev property to be whitelisted. Defaults to
+.I (ID_WWN|SCSI_IDENT_.*)
.TP
.B devnode
Regular expression of the device nodes to be whitelisted.
.B features
.TP
.B reservation_key
+.TP
+.B deferred_remove
+.TP
+.B delay_watch_checks
+.TP
+.B delay_wait_checks
.RE
.PD
.LP
.B retain_attached_hw_handler
.TP
.B detect_prio
+.TP
+.B deferred_remove
+.TP
+.B delay_watch_checks
+.TP
+.B delay_wait_checks
.RE
.PD
.LP
+.SH "overrides section"
+The overrides section recognizes the following optional attributes; if not set
+the values are taken from the
+.I devices
+or
+.I defaults
+sections:
+.sp 1
+.PD .1v
+.RS
+.TP 18
+.B path_grouping_policy
+.TP
+.B uid_attribute
+.TP
+.B getuid_callout
+.TP
+.B path_selector
+.TP
+.B path_checker
+.TP
+.B alias_prefix
+.TP
+.B features
+.TP
+.B prio
+.TP
+.B prio_args
+.TP
+.B failback
+.TP
+.B rr_weight
+.TP
+.B no_path_retry
+.TP
+.B rr_min_io
+.TP
+.B rr_min_io_rq
+.TP
+.B flush_on_last_del
+.TP
+.B fast_io_fail_tmo
+.TP
+.B dev_loss_tmo
+.TP
+.B user_friendly_names
+.TP
+.B retain_attached_hw_handler
+.TP
+.B detect_prio
+.TP
+.B deferred_remove
+.TP
+.B delay_watch_checks
+.TP
+.B delay_wait_checks
+.RE
+.PD
+.LP
+.SH "WWID generation"
+Multipath uses a \fIWorld Wide Identification\fR (wwid) to determine
+which paths belong to the same device. Each path presenting the same
+wwid is assumed to point to the same device.
+.LP
+The wwid is generated by three methods (in the order of preference):
+.TP 17
+.B getuid_callout
+Use the specified external program; cf \fIgetuid_callout\fR above.
+Care should be taken when using this method; the external program
+needs to be loaded from disk for execution, which might lead to
+deadlock situations in an all-paths-down scenario.
+.TP
+.B uid_attribute
+Use the value of the specified udev attribute; cf \fIuid_attribute\fR
+above. This method is preferred to \fIgetuid_callout\fR as multipath
+does not need to call any external programs here. However, under
+certain circumstances udev might not be able to generate the requested
+variable.
+.TP
+.B vpd_pg83
+If none of the \fIgetuid_callout\fR or \fIuid_attribute\fR parameters
+are present multipath will try to use the sysfs attribute
+\fIvpd_pg83\fR to generate the wwid.
.SH "KNOWN ISSUES"
The usage of
.B queue_if_no_path
--- /dev/null
+# Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath
+SUBSYSTEM!="block", GOTO="end_mpath"
+ACTION!="add|change", GOTO="end_mpath"
+KERNEL!="sd*|dasd*", GOTO="end_mpath"
+
+ENV{DEVTYPE}!="partition", GOTO="test_dev"
+IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH"
+ENV{DM_MULTIPATH_DEVICE_PATH}=="1", ENV{ID_FS_TYPE}="none", \
+ ENV{SYSTEMD_READY}="0"
+GOTO="end_mpath"
+
+LABEL="test_dev"
+
+ENV{MPATH_SBIN_PATH}="/sbin"
+TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin"
+
+ENV{DM_MULTIPATH_DEVICE_PATH}!="1", \
+ PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -u %k", \
+ ENV{DM_MULTIPATH_DEVICE_PATH}="1", ENV{ID_FS_TYPE}="none", \
+ ENV{SYSTEMD_READY}="0"
+
+LABEL="end_mpath"
#
# basic flags setting
#
-CFLAGS += -I$(multipathdir) -I$(mpathpersistdir)
+CFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir)
ifdef SYSTEMD
CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
endif
LDFLAGS += -lpthread -ldevmapper -lreadline
ifdef SYSTEMD
- LDFLAGS += -lsystemd-daemon
+ ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1)
+ LDFLAGS += -lsystemd
+ else
+ LDFLAGS += -lsystemd-daemon
+ endif
endif
LDFLAGS += -ludev -ldl \
- -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist
+ -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
+ -L$(mpathcmddir) -lmpathcmd
#
# debuging stuff
}
static int
-add_key (vector vec, char * str, unsigned long code, int has_param)
+add_key (vector vec, char * str, uint64_t code, int has_param)
{
struct key * kw;
}
int
-add_handler (unsigned long fp, int (*fn)(void *, char **, int *, void *))
+add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *))
{
struct handler * h;
}
static struct handler *
-find_handler (unsigned long fp)
+find_handler (uint64_t fp)
{
int i;
struct handler *h;
}
int
-set_handler_callback (unsigned long fp, int (*fn)(void *, char **, int *, void *))
+set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *))
{
struct handler * h = find_handler(fp);
r += add_key(keys, "config", CONFIG, 0);
r += add_key(keys, "blacklist", BLACKLIST, 0);
r += add_key(keys, "devices", DEVICES, 0);
- r += add_key(keys, "format", FMT, 1);
+ r += add_key(keys, "raw", RAW, 0);
r += add_key(keys, "wildcards", WILDCARDS, 0);
r += add_key(keys, "quit", QUIT, 0);
r += add_key(keys, "exit", QUIT, 0);
r += add_key(keys, "getprstatus", GETPRSTATUS, 0);
r += add_key(keys, "setprstatus", SETPRSTATUS, 0);
r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0);
+ r += add_key(keys, "format", FMT, 1);
if (r) {
free_keys(keys);
return r;
}
-static unsigned long
+static uint64_t
fingerprint(vector vec)
{
int i;
- unsigned long fp = 0;
+ uint64_t fp = 0;
struct key * kw;
if (!vec)
}
static int
-genhelp_sprint_aliases (char * reply, vector keys, struct key * refkw)
+genhelp_sprint_aliases (char * reply, int maxlen, vector keys,
+ struct key * refkw)
{
- int i, fwd = 0;
+ int i, len = 0;
struct key * kw;
- vector_foreach_slot (keys, kw, i)
- if (kw->code == refkw->code && kw != refkw)
- fwd += sprintf(reply, "|%s", kw->str);
+ vector_foreach_slot (keys, kw, i) {
+ if (kw->code == refkw->code && kw != refkw) {
+ len += snprintf(reply + len, maxlen - len,
+ "|%s", kw->str);
+ if (len >= maxlen)
+ return len;
+ }
+ }
- return fwd;
+ return len;
}
-static char *
-genhelp_handler (void)
-{
+static int
+do_genhelp(char *reply, int maxlen) {
+ int len = 0;
int i, j;
- unsigned long fp;
+ uint64_t fp;
struct handler * h;
struct key * kw;
- char * reply;
- char * p;
- reply = MALLOC(INITIAL_REPLY_LEN);
-
- if (!reply)
- return NULL;
-
- p = reply;
- p += sprintf(p, VERSION_STRING);
- p += sprintf(p, "CLI commands reference:\n");
+ len += snprintf(reply + len, maxlen - len, VERSION_STRING);
+ if (len >= maxlen)
+ goto out;
+ len += snprintf(reply + len, maxlen - len, "CLI commands reference:\n");
+ if (len >= maxlen)
+ goto out;
vector_foreach_slot (handlers, h, i) {
fp = h->fingerprint;
vector_foreach_slot (keys, kw, j) {
if ((kw->code & fp)) {
fp -= kw->code;
- p += sprintf(p, " %s", kw->str);
- p += genhelp_sprint_aliases(p, keys, kw);
-
- if (kw->has_param)
- p += sprintf(p, " $%s", kw->str);
+ len += snprintf(reply + len , maxlen - len,
+ " %s", kw->str);
+ if (len >= maxlen)
+ goto out;
+ len += genhelp_sprint_aliases(reply + len,
+ maxlen - len,
+ keys, kw);
+ if (len >= maxlen)
+ goto out;
+
+ if (kw->has_param) {
+ len += snprintf(reply + len,
+ maxlen - len,
+ " $%s", kw->str);
+ if (len >= maxlen)
+ goto out;
+ }
}
}
- p += sprintf(p, "\n");
+ len += snprintf(reply + len, maxlen - len, "\n");
+ if (len >= maxlen)
+ goto out;
}
+out:
+ return len;
+}
+
+static char *
+genhelp_handler (void)
+{
+ char * reply;
+ char * p = NULL;
+ int maxlen = INITIAL_REPLY_LEN;
+ int again = 1;
+
+ reply = MALLOC(maxlen);
+
+ while (again) {
+ if (!reply)
+ return NULL;
+ p = reply;
+ p += do_genhelp(reply, maxlen);
+ again = ((p - reply) >= maxlen);
+ REALLOC_REPLY(reply, again, maxlen);
+ }
return reply;
}
}
char *
-get_keyparam (vector v, unsigned long code)
+get_keyparam (vector v, uint64_t code)
{
struct key * kw;
int i;
add_handler(LIST+PATHS, NULL);
add_handler(LIST+PATHS+FMT, NULL);
+ add_handler(LIST+PATHS+RAW+FMT, NULL);
+ add_handler(LIST+PATH, NULL);
add_handler(LIST+STATUS, NULL);
add_handler(LIST+DAEMON, NULL);
add_handler(LIST+MAPS, NULL);
add_handler(LIST+MAPS+STATUS, NULL);
add_handler(LIST+MAPS+STATS, NULL);
add_handler(LIST+MAPS+FMT, NULL);
+ add_handler(LIST+MAPS+RAW+FMT, NULL);
add_handler(LIST+MAPS+TOPOLOGY, NULL);
add_handler(LIST+TOPOLOGY, NULL);
add_handler(LIST+MAP+TOPOLOGY, NULL);
}
static int
-key_match_fingerprint (struct key * kw, unsigned long fp)
+key_match_fingerprint (struct key * kw, uint64_t fp)
{
if (!fp)
return 0;
key_generator (const char * str, int state)
{
static int index, len, has_param;
- static unsigned long rlfp;
+ static uint64_t rlfp;
struct key * kw;
int i;
struct handler *h;
* nfp is the candidate fingerprint we try to
* validate against all known command fingerprints.
*/
- unsigned long nfp = rlfp | kw->code;
+ uint64_t nfp = rlfp | kw->code;
vector_foreach_slot(handlers, h, i) {
if (!rlfp || ((h->fingerprint & nfp) == nfp)) {
/*
+#include <stdint.h>
+
enum {
__LIST,
__ADD,
__CONFIG,
__BLACKLIST,
__DEVICES,
- __FMT,
+ __RAW,
__WILDCARDS,
__QUIT,
__SHUTDOWN,
__GETPRSTATUS,
__SETPRSTATUS,
__UNSETPRSTATUS,
+ __FMT,
};
#define LIST (1 << __LIST)
#define CONFIG (1 << __CONFIG)
#define BLACKLIST (1 << __BLACKLIST)
#define DEVICES (1 << __DEVICES)
-#define FMT (1 << __FMT)
+#define RAW (1 << __RAW)
#define COUNT (1 << __COUNT)
#define WILDCARDS (1 << __WILDCARDS)
#define QUIT (1 << __QUIT)
#define SHUTDOWN (1 << __SHUTDOWN)
-#define GETPRSTATUS (1UL << __GETPRSTATUS)
-#define SETPRSTATUS (1UL << __SETPRSTATUS)
-#define UNSETPRSTATUS (1UL << __UNSETPRSTATUS)
+#define GETPRSTATUS (1ULL << __GETPRSTATUS)
+#define SETPRSTATUS (1ULL << __SETPRSTATUS)
+#define UNSETPRSTATUS (1ULL << __UNSETPRSTATUS)
+#define FMT (1ULL << __FMT)
+
+#define INITIAL_REPLY_LEN 1200
-#define INITIAL_REPLY_LEN 1100
+#define REALLOC_REPLY(r, a, m) \
+ do { \
+ if ((a)) { \
+ char *tmp = (r); \
+ (r) = REALLOC((r), (m) * 2); \
+ if ((r)) { \
+ memset((r) + (m), 0, (m)); \
+ (m) *= 2; \
+ } \
+ else \
+ free(tmp); \
+ } \
+ } while (0)
struct key {
char * str;
char * param;
- unsigned long code;
+ uint64_t code;
int has_param;
};
struct handler {
- unsigned long fingerprint;
+ uint64_t fingerprint;
int (*fn)(void *, char **, int *, void *);
};
int alloc_handlers (void);
-int add_handler (unsigned long fp, int (*fn)(void *, char **, int *, void *));
-int set_handler_callback (unsigned long fp, int (*fn)(void *, char **, int *, void *));
+int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *));
+int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *));
int parse_cmd (char * cmd, char ** reply, int * len, void *);
int load_keys (void);
-char * get_keyparam (vector v, unsigned long code);
+char * get_keyparam (vector v, uint64_t code);
void free_keys (vector vec);
void free_handlers (void);
int cli_init (void);
#include "cli.h"
#include "uevent.h"
-#define REALLOC_REPLY(r, a, m) \
- do { \
- if ((a)) { \
- (r) = REALLOC((r), (m) * 2); \
- if ((r)) { \
- memset((r) + (m), 0, (m)); \
- (m) *= 2; \
- } \
- } \
- } while (0)
-
int
-show_paths (char ** r, int * len, struct vectors * vecs, char * style)
+show_paths (char ** r, int * len, struct vectors * vecs, char * style,
+ int pretty)
{
int i;
struct path * pp;
c = reply;
- if (VECTOR_SIZE(vecs->pathvec) > 0)
+ if (pretty && VECTOR_SIZE(vecs->pathvec) > 0)
c += snprint_path_header(c, reply + maxlen - c,
style);
vector_foreach_slot(vecs->pathvec, pp, i)
c += snprint_path(c, reply + maxlen - c,
- style, pp);
+ style, pp, pretty);
+
+ again = ((c - reply) == (maxlen - 1));
+
+ REALLOC_REPLY(reply, again, maxlen);
+ }
+ *r = reply;
+ *len = (int)(c - reply + 1);
+ return 0;
+}
+
+int
+show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
+ char * style)
+{
+ char * c;
+ char * reply;
+ unsigned int maxlen = INITIAL_REPLY_LEN;
+ int again = 1;
+
+ get_path_layout(vecs->pathvec, 1);
+ reply = MALLOC(maxlen);
+
+ while (again) {
+ if (!reply)
+ return 1;
+
+ c = reply;
+
+ c += snprint_path(c, reply + maxlen - c, style, pp, 0);
again = ((c - reply) == (maxlen - 1));
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
- reply = MALLOC(maxlen);
+ c = reply = MALLOC(maxlen);
while (again) {
if (!reply)
c = reply;
c += snprint_defaults(c, reply + maxlen - c);
again = ((c - reply) == maxlen);
- if (again) {
- reply = REALLOC(reply, maxlen * 2);
- if (!reply)
- return 1;
- memset(reply + maxlen, 0, maxlen);
- maxlen *= 2;
+ REALLOC_REPLY(reply, again, maxlen);
+ if (again)
continue;
- }
c += snprint_blacklist(c, reply + maxlen - c);
again = ((c - reply) == maxlen);
- if (again) {
- reply = REALLOC(reply, maxlen * 2);
- if (!reply)
- return 1;
- memset(reply + maxlen, 0, maxlen);
- maxlen *= 2;
+ REALLOC_REPLY(reply, again, maxlen);
+ if (again)
continue;
- }
c += snprint_blacklist_except(c, reply + maxlen - c);
again = ((c - reply) == maxlen);
- if (again) {
- reply = REALLOC(reply, maxlen * 2);
- if (!reply)
- return 1;
- memset(reply + maxlen, 0, maxlen);
- maxlen *= 2;
+ REALLOC_REPLY(reply, again, maxlen);
+ if (again)
continue;
- }
c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable);
again = ((c - reply) == maxlen);
- if (again) {
- reply = REALLOC(reply, maxlen * 2);
- if (!reply)
- return 1;
- memset(reply + maxlen, 0, maxlen);
- maxlen *= 2;
+ REALLOC_REPLY(reply, again, maxlen);
+ if (again)
continue;
- }
- c += snprint_mptable(c, reply + maxlen - c, conf->mptable);
+ c += snprint_overrides(c, reply + maxlen - c, conf->overrides);
again = ((c - reply) == maxlen);
REALLOC_REPLY(reply, again, maxlen);
+ if (again)
+ continue;
+ if (VECTOR_SIZE(conf->mptable) > 0) {
+ c += snprint_mptable(c, reply + maxlen - c,
+ conf->mptable);
+ again = ((c - reply) == maxlen);
+ REALLOC_REPLY(reply, again, maxlen);
+ }
}
*r = reply;
*len = (int)(c - reply + 1);
condlog(3, "list paths (operator)");
- return show_paths(reply, len, vecs, PRINT_PATH_CHECKER);
+ return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1);
}
int
condlog(3, "list paths (operator)");
- return show_paths(reply, len, vecs, fmt);
+ return show_paths(reply, len, vecs, fmt, 1);
+}
+
+int
+cli_list_paths_raw (void * v, char ** reply, int * len, void * data)
+{
+ struct vectors * vecs = (struct vectors *)data;
+ char * fmt = get_keyparam(v, FMT);
+
+ condlog(3, "list paths (operator)");
+
+ return show_paths(reply, len, vecs, fmt, 0);
+}
+
+int
+cli_list_path (void * v, char ** reply, int * len, void * data)
+{
+ struct vectors * vecs = (struct vectors *)data;
+ char * param = get_keyparam(v, PATH);
+ struct path *pp;
+
+ param = convert_dev(param, 1);
+ condlog(3, "%s: list path (operator)", param);
+
+ pp = find_path_by_dev(vecs->pathvec, param);
+
+ return show_path(reply, len, vecs, pp, "%o");
}
int
}
int
-show_maps (char ** r, int *len, struct vectors * vecs, char * style)
+show_maps (char ** r, int *len, struct vectors * vecs, char * style,
+ int pretty)
{
int i;
struct multipath * mpp;
return 1;
c = reply;
- if (VECTOR_SIZE(vecs->mpvec) > 0)
+ if (pretty && VECTOR_SIZE(vecs->mpvec) > 0)
c += snprint_multipath_header(c, reply + maxlen - c,
style);
vector_foreach_slot(vecs->mpvec, mpp, i)
c += snprint_multipath(c, reply + maxlen - c,
- style, mpp);
+ style, mpp, pretty);
again = ((c - reply) == (maxlen - 1));
condlog(3, "list maps (operator)");
- return show_maps(reply, len, vecs, fmt);
+ return show_maps(reply, len, vecs, fmt, 1);
+}
+
+int
+cli_list_maps_raw (void * v, char ** reply, int * len, void * data)
+{
+ struct vectors * vecs = (struct vectors *)data;
+ char * fmt = get_keyparam(v, FMT);
+
+ condlog(3, "list maps (operator)");
+
+ return show_maps(reply, len, vecs, fmt, 0);
}
int
condlog(3, "list maps (operator)");
- return show_maps(reply, len, vecs, PRINT_MAP_NAMES);
+ return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1);
}
int
condlog(3, "list maps status (operator)");
- return show_maps(reply, len, vecs, PRINT_MAP_STATUS);
+ return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1);
}
int
condlog(3, "list maps stats (operator)");
- return show_maps(reply, len, vecs, PRINT_MAP_STATS);
+ return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1);
}
int
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp) {
condlog(0, "%s: path already removed", param);
- return 0;
+ return 1;
}
return ev_remove_path(pp, vecs);
}
char * param = get_keyparam(v, MAP);
int major, minor;
char dev_path[PATH_SIZE];
- char *alias;
- int rc;
+ char *alias, *refwwid;
+ int rc, count = 0;
param = convert_dev(param, 0);
condlog(2, "%s: add map (operator)", param);
- if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param) > 0) {
+ if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0) {
*reply = strdup("blacklisted\n");
*len = strlen(*reply) + 1;
condlog(2, "%s: map blacklisted", param);
- return 0;
- }
- minor = dm_get_minor(param);
- if (minor < 0) {
- condlog(2, "%s: not a device mapper table", param);
- return 0;
- }
- major = dm_get_major(param);
- if (major < 0) {
- condlog(2, "%s: not a device mapper table", param);
- return 0;
+ return 1;
}
- sprintf(dev_path,"dm-%d", minor);
- alias = dm_mapname(major, minor);
+ do {
+ minor = dm_get_minor(param);
+ if (minor < 0)
+ condlog(2, "%s: not a device mapper table", param);
+ major = dm_get_major(param);
+ if (major < 0)
+ condlog(2, "%s: not a device mapper table", param);
+ sprintf(dev_path, "dm-%d", minor);
+ alias = dm_mapname(major, minor);
+ /*if there is no mapname found, we first create the device*/
+ if (!alias && !count) {
+ condlog(2, "%s: mapname not found for %d:%d",
+ param, major, minor);
+ rc = get_refwwid(param, DEV_DEVMAP, vecs->pathvec,
+ &refwwid);
+ if (refwwid) {
+ if (coalesce_paths(vecs, NULL, refwwid, 0))
+ condlog(2, "%s: coalesce_paths failed",
+ param);
+ dm_lib_release();
+ }
+ } /*we attempt to create device only once*/
+ count++;
+ } while (!alias && (count < 2));
+
if (!alias) {
- condlog(2, "%s: mapname not found for %d:%d",
- param, major, minor);
- return 0;
+ condlog(2, "%s: add map failed", param);
+ return 1;
}
rc = ev_add_map(dev_path, alias, vecs);
FREE(alias);
+ FREE(refwwid);
return rc;
}
minor = dm_get_minor(param);
if (minor < 0) {
condlog(2, "%s: not a device mapper table", param);
- return 0;
+ return 1;
}
major = dm_get_major(param);
if (major < 0) {
condlog(2, "%s: not a device mapper table", param);
- return 0;
+ return 1;
}
sprintf(dev_path,"dm-%d", minor);
alias = dm_mapname(major, minor);
if (!alias) {
condlog(2, "%s: mapname not found for %d:%d",
param, major, minor);
- return 0;
+ return 1;
}
rc = ev_remove_map(param, alias, minor, vecs);
FREE(alias);
condlog(0, "%s: invalid map name. cannot reload", mapname);
return 1;
}
+ if (mpp->wait_for_udev) {
+ condlog(2, "%s: device not fully created, failing reload",
+ mpp->alias);
+ return 1;
+ }
return reload_map(vecs, mpp, 0);
}
return 1;
}
+ if (mpp->wait_for_udev) {
+ condlog(2, "%s: device not fully created, failing resize",
+ mpp->alias);
+ return 1;
+ }
+
pgp = VECTOR_SLOT(mpp->pg, 0);
if (!pgp){
{
struct vectors * vecs = (struct vectors *)data;
+ if (need_to_delay_reconfig(vecs)) {
+ conf->delayed_reconfig = 1;
+ condlog(2, "delaying reconfigure (operator)");
+ return 0;
+ }
+
condlog(2, "reconfigure (operator)");
return reconfigure(vecs);
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
- int r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param);
+ int r;
+ struct multipath * mpp;
param = convert_dev(param, 0);
- condlog(2, "%s: suspend (operator)", param);
+ mpp = find_mp_by_alias(vecs->mpvec, param);
+ if (!mpp)
+ return 1;
- if (!r) /* error */
+ if (mpp->wait_for_udev) {
+ condlog(2, "%s: device not fully created, failing suspend",
+ mpp->alias);
return 1;
+ }
- struct multipath * mpp = find_mp_by_alias(vecs->mpvec, param);
+ r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param, 0, 0);
- if (!mpp)
+ condlog(2, "%s: suspend (operator)", param);
+
+ if (!r) /* error */
return 1;
dm_get_info(param, &mpp->dmi);
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
- int r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param);
+ int r;
+ struct multipath * mpp;
param = convert_dev(param, 0);
- condlog(2, "%s: resume (operator)", param);
+ mpp = find_mp_by_alias(vecs->mpvec, param);
+ if (!mpp)
+ return 1;
- if (!r) /* error */
+ if (mpp->wait_for_udev) {
+ condlog(2, "%s: device not fully created, failing resume",
+ mpp->alias);
return 1;
+ }
- struct multipath * mpp = find_mp_by_alias(vecs->mpvec, param);
+ r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param, 0, 0);
- if (!mpp)
+ condlog(2, "%s: resume (operator)", param);
+
+ if (!r) /* error */
return 1;
dm_get_info(param, &mpp->dmi);
int
cli_reassign (void * v, char ** reply, int * len, void * data)
{
+ struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
+ struct multipath *mpp;
param = convert_dev(param, 0);
+ mpp = find_mp_by_alias(vecs->mpvec, param);
+ if (!mpp)
+ return 1;
+
+ if (mpp->wait_for_udev) {
+ condlog(2, "%s: device not fully created, failing reassign",
+ mpp->alias);
+ return 1;
+ }
+
condlog(3, "%s: reset devices (operator)", param);
dm_reassign(param);
int cli_list_paths (void * v, char ** reply, int * len, void * data);
int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data);
+int cli_list_paths_raw (void * v, char ** reply, int * len, void * data);
+int cli_list_path (void * v, char ** reply, int * len, void * data);
int cli_list_status (void * v, char ** reply, int * len, void * data);
int cli_list_daemon (void * v, char ** reply, int * len, void * data);
int cli_list_maps (void * v, char ** reply, int * len, void * data);
int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data);
+int cli_list_maps_raw (void * v, char ** reply, int * len, void * data);
int cli_list_maps_status (void * v, char ** reply, int * len, void * data);
int cli_list_maps_stats (void * v, char ** reply, int * len, void * data);
int cli_list_map_topology (void * v, char ** reply, int * len, void * data);
#include <systemd/sd-daemon.h>
#endif
#include <semaphore.h>
+#include <mpath_cmd.h>
#include <mpath_persist.h>
#include <time.h>
*/
#include <checkers.h>
+#ifdef USE_SYSTEMD
+static int use_watchdog;
+#endif
+
/*
* libmultipath
*/
#include <print.h>
#include <configure.h>
#include <prio.h>
+#include <wwids.h>
#include <pgpolicies.h>
#include <uevent.h>
#include <log.h>
+#include "prioritizers/alua_rtpg.h"
#include "main.h"
#include "pidfile.h"
vector_foreach_slot (mpp->pg, pgp, i){
vector_foreach_slot (pgp->paths, pp, j){
if (pp->state == PATH_UNCHECKED ||
- pp->state == PATH_WILD)
+ pp->state == PATH_WILD ||
+ pp->state == PATH_DELAYED)
continue;
if ((pp->dmstate == PSTATE_FAILED ||
pp->dmstate == PSTATE_UNDEF) &&
}
static int
-flush_map(struct multipath * mpp, struct vectors * vecs)
+flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths)
{
+ int r;
+
+ if (nopaths)
+ r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove);
+ else
+ r = dm_flush_map(mpp->alias);
/*
* clear references to this map before flushing so we can ignore
* the spurious uevent we may generate with the dm_flush_map call below
*/
- if (dm_flush_map(mpp->alias)) {
+ if (r) {
/*
* May not really be an error -- if the map was already flushed
* from the device mapper by dmsetup(8) for instance.
*/
- condlog(0, "%s: can't flush", mpp->alias);
- return 1;
+ if (r == 1)
+ condlog(0, "%s: can't flush", mpp->alias);
+ else {
+ condlog(2, "%s: devmap deferred remove", mpp->alias);
+ mpp->deferred_remove = DEFERRED_REMOVE_IN_PROGRESS;
+ }
+ return r;
}
else {
dm_lib_release();
return 0;
}
+int
+update_map (struct multipath *mpp, struct vectors *vecs)
+{
+ int retries = 3;
+ char params[PARAMS_SIZE] = {0};
+
+retry:
+ condlog(4, "%s: updating new map", mpp->alias);
+ if (adopt_paths(vecs->pathvec, mpp, 1)) {
+ condlog(0, "%s: failed to adopt paths for new map update",
+ mpp->alias);
+ retries = -1;
+ goto fail;
+ }
+ verify_paths(mpp, vecs);
+ mpp->flush_on_last_del = FLUSH_UNDEF;
+ mpp->action = ACT_RELOAD;
+
+ if (setup_map(mpp, params, PARAMS_SIZE)) {
+ condlog(0, "%s: failed to setup new map in update", mpp->alias);
+ retries = -1;
+ goto fail;
+ }
+ if (domap(mpp, params) <= 0 && retries-- > 0) {
+ condlog(0, "%s: map_udate sleep", mpp->alias);
+ sleep(1);
+ goto retry;
+ }
+ dm_lib_release();
+
+fail:
+ if (setup_multipath(vecs, mpp))
+ return 1;
+
+ sync_map_state(mpp);
+
+ if (retries < 0)
+ condlog(0, "%s: failed reload in new map update", mpp->alias);
+ return 0;
+}
+
static int
uev_add_map (struct uevent * uev, struct vectors * vecs)
{
map_present = dm_map_present(alias);
- if (map_present && dm_type(alias, TGT_MPATH) <= 0) {
+ if (map_present && !dm_is_mpath(alias)) {
condlog(4, "%s: not a multipath map", alias);
return 0;
}
mpp = find_mp_by_alias(vecs->mpvec, alias);
if (mpp) {
+ if (mpp->wait_for_udev > 1) {
+ if (update_map(mpp, vecs))
+ /* setup multipathd removed the map */
+ return 1;
+ }
+ if (mpp->wait_for_udev) {
+ mpp->wait_for_udev = 0;
+ if (conf->delayed_reconfig &&
+ !need_to_delay_reconfig(vecs)) {
+ condlog(2, "reconfigure (delayed)");
+ reconfigure(vecs);
+ return 0;
+ }
+ }
/*
* Not really an error -- we generate our own uevent
* if we create a multipath mapped device as a result
/*
* now we can register the map
*/
- if (map_present && (mpp = add_map_without_path(vecs, alias))) {
- sync_map_state(mpp);
- condlog(2, "%s: devmap %s registered", alias, dev);
- return 0;
+ if (map_present) {
+ if ((mpp = add_map_without_path(vecs, alias))) {
+ sync_map_state(mpp);
+ condlog(2, "%s: devmap %s registered", alias, dev);
+ return 0;
+ } else {
+ condlog(2, "%s: uev_add_map failed", dev);
+ return 1;
+ }
}
r = get_refwwid(dev, DEV_DEVMAP, vecs->pathvec, &refwwid);
if (!mpp) {
condlog(2, "%s: devmap not registered, can't remove",
devname);
- return 0;
+ return 1;
}
if (strcmp(mpp->alias, alias)) {
condlog(2, "%s: minor number mismatch (map %d, event %d)",
mpp->alias, mpp->dmi->minor, minor);
- return 0;
+ return 1;
}
- return flush_map(mpp, vecs);
+ return flush_map(mpp, vecs, 0);
}
static int
uev_add_path (struct uevent *uev, struct vectors * vecs)
{
struct path *pp;
- int ret, i;
+ int ret = 0, i;
condlog(2, "%s: add path (uevent)", uev->kernel);
if (strstr(uev->kernel, "..") != NULL) {
pp = find_path_by_dev(vecs->pathvec, uev->kernel);
if (pp) {
+ int r;
+
condlog(0, "%s: spurious uevent, path already in pathvec",
uev->kernel);
- if (pp->mpp)
- return 0;
- if (!strlen(pp->wwid)) {
+ if (!pp->mpp && !strlen(pp->wwid)) {
+ condlog(3, "%s: reinitialize path", uev->kernel);
udev_device_unref(pp->udev);
pp->udev = udev_device_ref(uev->udev);
- ret = pathinfo(pp, conf->hwtable,
- DI_ALL | DI_BLACKLIST);
- if (ret == 2) {
+ r = pathinfo(pp, conf->hwtable,
+ DI_ALL | DI_BLACKLIST);
+ if (r == PATHINFO_OK)
+ ret = ev_add_path(pp, vecs);
+ else if (r == PATHINFO_SKIPPED) {
+ condlog(3, "%s: remove blacklisted path",
+ uev->kernel);
i = find_slot(vecs->pathvec, (void *)pp);
if (i != -1)
vector_del_slot(vecs->pathvec, i);
free_path(pp);
- return 0;
- } else if (ret == 1) {
+ } else {
condlog(0, "%s: failed to reinitialize path",
uev->kernel);
- return 1;
+ ret = 1;
}
}
- } else {
- /*
- * get path vital state
- */
- ret = store_pathinfo(vecs->pathvec, conf->hwtable,
- uev->udev, DI_ALL, &pp);
- if (!pp) {
- if (ret == 2)
- return 0;
- condlog(0, "%s: failed to store path info",
- uev->kernel);
- return 1;
- }
+ return ret;
+ }
+
+ /*
+ * get path vital state
+ */
+ ret = alloc_path_with_pathinfo(conf->hwtable, uev->udev,
+ DI_ALL, &pp);
+ if (!pp) {
+ if (ret == PATHINFO_SKIPPED)
+ return 0;
+ condlog(3, "%s: failed to get path info", uev->kernel);
+ return 1;
+ }
+ ret = store_path(vecs->pathvec, pp);
+ if (!ret) {
pp->checkint = conf->checkint;
+ ret = ev_add_path(pp, vecs);
+ } else {
+ condlog(0, "%s: failed to store path info, "
+ "dropping event",
+ uev->kernel);
+ free_path(pp);
+ ret = 1;
}
- return ev_add_path(pp, vecs);
+ return ret;
}
/*
ev_add_path (struct path * pp, struct vectors * vecs)
{
struct multipath * mpp;
- char empty_buff[WWID_SIZE] = {0};
char params[PARAMS_SIZE] = {0};
int retries = 3;
int start_waiter = 0;
+ int ret;
/*
* need path UID to go any further
*/
- if (memcmp(empty_buff, pp->wwid, WWID_SIZE) == 0) {
+ if (strlen(pp->wwid) == 0) {
condlog(0, "%s: failed to get path uid", pp->dev);
goto fail; /* leave path added to pathvec */
}
- mpp = pp->mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid);
+ mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid);
+ if (mpp && mpp->wait_for_udev) {
+ mpp->wait_for_udev = 2;
+ orphan_path(pp, "waiting for create to complete");
+ return 0;
+ }
+
+ pp->mpp = mpp;
rescan:
if (mpp) {
- if ((!pp->size) || (mpp->size != pp->size)) {
- if (!pp->size)
- condlog(0, "%s: failed to add new path %s, "
- "device size is 0",
- mpp->alias, pp->dev);
- else
- condlog(0, "%s: failed to add new path %s, "
- "device size mismatch",
- mpp->alias, pp->dev);
+ if (mpp->size != pp->size) {
+ condlog(0, "%s: failed to add new path %s, "
+ "device size mismatch",
+ mpp->alias, pp->dev);
int i = find_slot(vecs->pathvec, (void *)pp);
if (i != -1)
vector_del_slot(vecs->pathvec, i);
if (adopt_paths(vecs->pathvec, mpp, 1))
goto fail; /* leave path added to pathvec */
- verify_paths(mpp, vecs, NULL);
+ verify_paths(mpp, vecs);
mpp->flush_on_last_del = FLUSH_UNDEF;
mpp->action = ACT_RELOAD;
- }
- else {
- if (!pp->size) {
- condlog(0, "%s: failed to create new map,"
- " device size is 0 ", pp->dev);
- int i = find_slot(vecs->pathvec, (void *)pp);
- if (i != -1)
- vector_del_slot(vecs->pathvec, i);
- free_path(pp);
- return 1;
+ } else {
+ if (!should_multipath(pp, vecs->pathvec)) {
+ orphan_path(pp, "only one path");
+ return 0;
}
-
condlog(4,"%s: creating new map", pp->dev);
if ((mpp = add_map_with_path(vecs, pp, 1))) {
mpp->action = ACT_CREATE;
/*
* reload the map for the multipath mapped device
*/
- if (domap(mpp, params) <= 0) {
+retry:
+ ret = domap(mpp, params);
+ if (ret <= 0) {
+ if (ret < 0 && retries-- > 0) {
+ condlog(0, "%s: retry domap for addition of new "
+ "path %s", mpp->alias, pp->dev);
+ sleep(1);
+ goto retry;
+ }
condlog(0, "%s: failed in domap for addition of new "
"path %s", mpp->alias, pp->dev);
/*
return 0;
}
else
- return 1;
+ goto fail;
fail_map:
remove_map(mpp, vecs, 1);
mpp->flush_on_last_del = FLUSH_IN_PROGRESS;
dm_queue_if_no_path(mpp->alias, 0);
}
- if (!flush_map(mpp, vecs)) {
+ if (!flush_map(mpp, vecs, 1)) {
condlog(2, "%s: removed map after"
" removing all paths",
alias);
" removal of path %s", mpp->alias, pp->dev);
goto fail;
}
+
+ if (mpp->wait_for_udev) {
+ mpp->wait_for_udev = 2;
+ goto out;
+ }
+
/*
* reload the map
*/
/*
* update our state from kernel
*/
- if (setup_multipath(vecs, mpp)) {
- goto fail;
- }
+ if (setup_multipath(vecs, mpp))
+ return 1;
sync_map_state(mpp);
condlog(2, "%s [%s]: path removed from map %s",
uev_update_path (struct uevent *uev, struct vectors * vecs)
{
int ro, retval = 0;
+ struct path * pp;
+
+ pp = find_path_by_dev(vecs->pathvec, uev->kernel);
+ if (!pp) {
+ condlog(0, "%s: spurious uevent, path not found",
+ uev->kernel);
+ return 1;
+ }
+
+ if (pp->initialized == INIT_REQUESTED_UDEV)
+ return uev_add_path(uev, vecs);
ro = uevent_get_disk_ro(uev);
if (ro >= 0) {
- struct path * pp;
-
condlog(2, "%s: update path write_protect to '%d' (uevent)",
uev->kernel, ro);
- pp = find_path_by_dev(vecs->pathvec, uev->kernel);
- if (!pp) {
- condlog(0, "%s: spurious uevent, path not found",
- uev->kernel);
- return 1;
- }
if (pp->mpp) {
+ if (pp->mpp->wait_for_udev) {
+ pp->mpp->wait_for_udev = 2;
+ return 0;
+ }
+
retval = reload_map(vecs, pp->mpp, 0);
condlog(2, "%s: map %s reloaded (retval %d)",
set_handler_callback(LIST+PATHS, cli_list_paths);
set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt);
+ set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw);
+ set_handler_callback(LIST+PATH, cli_list_path);
set_handler_callback(LIST+MAPS, cli_list_maps);
set_handler_callback(LIST+STATUS, cli_list_status);
set_handler_callback(LIST+DAEMON, cli_list_daemon);
set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status);
set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats);
set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt);
+ set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw);
set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology);
set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology);
set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology);
/*
* caller must have locked the path list before calling that function
*/
-static void
+static int
reinstate_path (struct path * pp, int add_active)
{
+ int ret = 0;
+
if (!pp->mpp)
- return;
+ return 0;
- if (dm_reinstate_path(pp->mpp->alias, pp->dev_t))
+ if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) {
condlog(0, "%s: reinstate failed", pp->dev_t);
- else {
+ ret = 1;
+ } else {
condlog(2, "%s: reinstated", pp->dev_t);
if (add_active)
update_queue_mode_add_path(pp->mpp);
}
+ return ret;
}
static void
return 1;
}
+static void
+missing_uev_wait_tick(struct vectors *vecs)
+{
+ struct multipath * mpp;
+ unsigned int i;
+ int timed_out = 0;
+
+ vector_foreach_slot (vecs->mpvec, mpp, i) {
+ if (mpp->wait_for_udev && --mpp->uev_wait_tick <= 0) {
+ timed_out = 1;
+ condlog(0, "%s: timeout waiting on creation uevent. enabling reloads", mpp->alias);
+ if (mpp->wait_for_udev > 1 && update_map(mpp, vecs)) {
+ /* update_map removed map */
+ i--;
+ continue;
+ }
+ mpp->wait_for_udev = 0;
+ }
+ }
+
+ if (timed_out && conf->delayed_reconfig &&
+ !need_to_delay_reconfig(vecs)) {
+ condlog(2, "reconfigure (delayed)");
+ reconfigure(vecs);
+ }
+}
+
static void
defered_failback_tick (vector mpvec)
{
int newstate;
int new_path_up = 0;
int chkr_new_path_up = 0;
+ int add_active;
+ int disable_reinstate = 0;
int oldchkrstate = pp->chkrstate;
- if (!pp->mpp)
+ if ((pp->initialized == INIT_OK ||
+ pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp)
return 0;
if (pp->tick && --pp->tick)
return 0; /* don't check this path yet */
+ if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV &&
+ pp->retriggers < conf->retrigger_tries) {
+ condlog(2, "%s: triggering change event to reinitialize",
+ pp->dev);
+ pp->initialized = INIT_REQUESTED_UDEV;
+ pp->retriggers++;
+ sysfs_attr_set_value(pp->udev, "uevent", "change",
+ strlen("change"));
+ return 0;
+ }
+
/*
* provision a next check soonest,
* in case we exit abnormaly from here
pp->tick = conf->checkint;
newstate = path_offline(pp);
- if (newstate == PATH_REMOVED) {
- condlog(2, "%s: remove path (checker)", pp->dev);
- ev_remove_path(pp, vecs);
- return 0;
- }
+ /*
+ * Wait for uevent for removed paths;
+ * some LLDDs like zfcp keep paths unavailable
+ * without sending uevents.
+ */
+ if (newstate == PATH_REMOVED)
+ newstate = PATH_DOWN;
+
if (newstate == PATH_UP)
newstate = get_state(pp, 1);
else
return 1;
}
if (!pp->mpp) {
- if (!strlen(pp->wwid) &&
+ if (!strlen(pp->wwid) && pp->initialized != INIT_MISSING_UDEV &&
(newstate == PATH_UP || newstate == PATH_GHOST)) {
condlog(2, "%s: add missing path", pp->dev);
if (pathinfo(pp, conf->hwtable, DI_ALL) == 0) {
pp->dev);
pp->dmstate = PSTATE_UNDEF;
}
+ /* if update_multipath_strings orphaned the path, quit early */
+ if (!pp->mpp)
+ return 0;
+
+ if ((newstate == PATH_UP || newstate == PATH_GHOST) &&
+ pp->wait_checks > 0) {
+ if (pp->mpp && pp->mpp->nr_active > 0) {
+ pp->state = PATH_DELAYED;
+ pp->wait_checks--;
+ return 1;
+ } else
+ pp->wait_checks = 0;
+ }
+
+ /*
+ * don't reinstate failed path, if its in stand-by
+ * and if target supports only implicit tpgs mode.
+ * this will prevent unnecessary i/o by dm on stand-by
+ * paths if there are no other active paths in map.
+ */
+ disable_reinstate = (newstate == PATH_GHOST &&
+ pp->mpp->nr_active == 0 &&
+ pp->tpgs == TPGS_IMPLICIT) ? 1 : 0;
+
pp->chkrstate = newstate;
if (newstate != pp->state) {
int oldstate = pp->state;
* proactively fail path in the DM
*/
if (oldstate == PATH_UP ||
- oldstate == PATH_GHOST)
+ oldstate == PATH_GHOST) {
fail_path(pp, 1);
- else
+ if (pp->mpp->delay_wait_checks > 0 &&
+ pp->watch_checks > 0) {
+ pp->wait_checks = pp->mpp->delay_wait_checks;
+ pp->watch_checks = 0;
+ }
+ }else
fail_path(pp, 0);
/*
* reinstate this path
*/
if (oldstate != PATH_UP &&
- oldstate != PATH_GHOST)
- reinstate_path(pp, 1);
- else
- reinstate_path(pp, 0);
-
+ oldstate != PATH_GHOST) {
+ if (pp->mpp->delay_watch_checks > 0)
+ pp->watch_checks = pp->mpp->delay_watch_checks;
+ add_active = 1;
+ } else {
+ if (pp->watch_checks > 0)
+ pp->watch_checks--;
+ add_active = 0;
+ }
+ if (!disable_reinstate && reinstate_path(pp, add_active)) {
+ condlog(3, "%s: reload map", pp->dev);
+ ev_add_path(pp, vecs);
+ pp->tick = 1;
+ return 0;
+ }
new_path_up = 1;
if (oldchkrstate != PATH_UP && oldchkrstate != PATH_GHOST)
enable_group(pp);
}
else if (newstate == PATH_UP || newstate == PATH_GHOST) {
- if (pp->dmstate == PSTATE_FAILED ||
- pp->dmstate == PSTATE_UNDEF) {
+ if ((pp->dmstate == PSTATE_FAILED ||
+ pp->dmstate == PSTATE_UNDEF) &&
+ !disable_reinstate) {
/* Clear IO errors */
- reinstate_path(pp, 0);
+ if (reinstate_path(pp, 0)) {
+ condlog(3, "%s: reload map", pp->dev);
+ ev_add_path(pp, vecs);
+ pp->tick = 1;
+ return 0;
+ }
} else {
LOG_MSG(4, checker_message(&pp->checker));
if (pp->checkint != conf->max_checkint) {
condlog(4, "%s: delay next check %is",
pp->dev_t, pp->checkint);
}
+ if (pp->watch_checks > 0)
+ pp->watch_checks--;
pp->tick = pp->checkint;
}
}
pp->state = newstate;
+
+ if (pp->mpp->wait_for_udev)
+ return 1;
/*
* path prio refreshing
*/
pthread_testcancel();
condlog(4, "tick");
#ifdef USE_SYSTEMD
- if (conf->watchdog)
+ if (use_watchdog)
sd_notify(0, "WATCHDOG=1");
#endif
if (vecs->pathvec) {
if (vecs->mpvec) {
defered_failback_tick(vecs->mpvec);
retry_count_tick(vecs->mpvec);
+ missing_uev_wait_tick(vecs);
}
if (count)
count--;
struct multipath * mpp;
struct path * pp;
vector mpvec;
- int i;
+ int i, ret;
if (!vecs->pathvec && !(vecs->pathvec = vector_alloc()))
return 1;
/*
* probe for current path (from sysfs) and map (from dm) sets
*/
- path_discovery(vecs->pathvec, conf, DI_ALL);
+ ret = path_discovery(vecs->pathvec, conf, DI_ALL);
+ if (ret < 0)
+ return 1;
vector_foreach_slot (vecs->pathvec, pp, i){
if (filter_path(conf, pp) > 0){
return 0;
}
+int
+need_to_delay_reconfig(struct vectors * vecs)
+{
+ struct multipath *mpp;
+ int i;
+
+ if (!VECTOR_SIZE(vecs->mpvec))
+ return 0;
+
+ vector_foreach_slot(vecs->mpvec, mpp, i) {
+ if (mpp->wait_for_udev)
+ return 1;
+ }
+ return 0;
+}
+
int
reconfigure (struct vectors * vecs)
{
struct config * old = conf;
int retval = 1;
+ running_state = DAEMON_CONFIGURE;
+
/*
* free old map and path vectors ... they use old conf state
*/
if (!load_config(DEFAULT_CONFIGFILE, udev)) {
dm_drv_version(conf->version, TGT_MPATH);
conf->verbosity = old->verbosity;
+ conf->bindings_read_only = old->bindings_read_only;
+ conf->ignore_new_devs = old->ignore_new_devs;
conf->daemon = 1;
configure(vecs, 1);
free_config(old);
retval = 0;
}
+ running_state = DAEMON_RUNNING;
+
return retval;
}
handle_signals(void)
{
if (reconfig_sig && running_state == DAEMON_RUNNING) {
- condlog(2, "reconfigure (signal)");
pthread_cleanup_push(cleanup_lock,
&gvecs->lock);
lock(gvecs->lock);
pthread_testcancel();
- reconfigure(gvecs);
+ if (need_to_delay_reconfig(gvecs)) {
+ conf->delayed_reconfig = 1;
+ condlog(2, "delaying reconfigure (signal)");
+ }
+ else {
+ condlog(2, "reconfigure (signal)");
+ reconfigure(gvecs);
+ }
lock_cleanup_pop(gvecs->lock);
}
if (log_reset_sig) {
#ifdef USE_SYSTEMD
unsigned long checkint;
#endif
- int rc, pid_rc;
+ int rc;
char *envp;
mlockall(MCL_CURRENT | MCL_FUTURE);
udev = udev_new();
setup_thread_attr(&misc_attr, 64 * 1024, 1);
- setup_thread_attr(&uevent_attr, 128 * 1024, 1);
+ setup_thread_attr(&uevent_attr, DEFAULT_UEVENT_STACKSIZE * 1024, 1);
setup_thread_attr(&waiter_attr, 32 * 1024, 1);
if (logsink == 1) {
log_thread_start(&log_attr);
pthread_attr_destroy(&log_attr);
}
+ if (pidfile_create(DEFAULT_PIDFILE, daemon_pid)) {
+ condlog(1, "failed to create pidfile");
+ if (logsink == 1)
+ log_thread_stop();
+ exit(1);
+ }
running_state = DAEMON_START;
set_oom_adj();
conf->daemon = 1;
- udev_set_sync_support(0);
+ dm_udev_set_sync_support(0);
#ifdef USE_SYSTEMD
envp = getenv("WATCHDOG_USEC");
if (envp && sscanf(envp, "%lu", &checkint) == 1) {
conf->checkint = conf->max_checkint / 4;
condlog(3, "enabling watchdog, interval %d max %d",
conf->checkint, conf->max_checkint);
- conf->watchdog = conf->checkint;
+ use_watchdog = conf->checkint;
}
#endif
/*
}
pthread_attr_destroy(&misc_attr);
- /* Startup complete, create logfile */
- pid_rc = pidfile_create(DEFAULT_PIDFILE, daemon_pid);
- /* Ignore errors, we can live without */
-
running_state = DAEMON_RUNNING;
#ifdef USE_SYSTEMD
sd_notify(0, "READY=1\nSTATUS=running");
dm_lib_exit();
/* We're done here */
- if (!pid_rc) {
- condlog(3, "unlink pidfile");
- unlink(DEFAULT_PIDFILE);
- }
+ condlog(3, "unlink pidfile");
+ unlink(DEFAULT_PIDFILE);
condlog(2, "--------shut down-------");
extern int optind;
int arg;
int err;
+ int foreground = 0;
logsink = 1;
running_state = DAEMON_INIT;
if (!conf)
exit(1);
- while ((arg = getopt(argc, argv, ":dsv:k::")) != EOF ) {
+ while ((arg = getopt(argc, argv, ":dsv:k::Bn")) != EOF ) {
switch(arg) {
case 'd':
- logsink = 0;
+ foreground = 1;
+ if (logsink > 0)
+ logsink = 0;
//debug=1; /* ### comment me out ### */
break;
case 'v':
logsink = -1;
break;
case 'k':
- uxclnt(optarg);
+ if (load_config(DEFAULT_CONFIGFILE, udev_new()))
+ exit(1);
+ uxclnt(optarg, conf->uxsock_timeout);
exit(0);
+ case 'B':
+ conf->bindings_read_only = 1;
+ break;
+ case 'n':
+ conf->ignore_new_devs = 1;
+ break;
default:
;
}
char * s = cmd;
char * c = s;
+ if (load_config(DEFAULT_CONFIGFILE, udev_new()))
+ exit(1);
while (optind < argc) {
if (strchr(argv[optind], ' '))
c += snprintf(c, s + CMDSIZE - c, "\"%s\" ", argv[optind]);
optind++;
}
c += snprintf(c, s + CMDSIZE - c, "\n");
- uxclnt(s);
+ uxclnt(s, conf->uxsock_timeout);
exit(0);
}
- if (logsink < 1)
+ if (foreground) {
+ if (!isatty(fileno(stdout)))
+ setbuf(stdout, NULL);
err = 0;
- else
+ daemon_pid = getpid();
+ } else
err = daemonize();
if (err < 0)
void exit_daemon(void);
const char * daemon_status(void);
+int need_to_delay_reconfig (struct vectors *);
int reconfigure (struct vectors *);
int ev_add_path (struct path *, struct vectors *);
int ev_remove_path (struct path *, struct vectors *);
.B -v "level"
Verbosity level. Print additional information while running multipathd. A level of 0 means only print errors. A level of 3 or greater prints debugging information as well.
.TP
+.B -B
+Read-only bindings file. Multipathd will not write to the user_friendly_names
+bindings file. If a user_friendly_name doesn't already exist for a device, it
+will use its WWID as its alias.
+.TP
.B -k
multipathd will enter interactive mode. From this mode, the available commands can be viewed by entering "help". When you are finished entering commands, press CTRL-D to quit.
+.TP
+.B -n
+ignore new devices. Multipathd will not create a multipath device unless the
+wwid for the device is already listed in the wwids file.
.SH COMMANDS
.TP
.B list|show status
Show the number of path checkers in each possible state, the number of monitored paths, and whether multipathd is currently handling a uevent.
.TP
+.B list|show daemon
+Show the current state of the multipathd daemon
+.TP
.B add path $path
Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda).
.TP
.B resume map|multipath $map
Resumes map $map from suspend state.
.TP
+.B reset map|multipath $map
+Reassign existing device-mapper table(s) use use the multipath device, instead
+of its path devices.
+.TP
+.B reload map|multipath $map
+Reload a multipath device.
+.TP
.B fail path $path
Sets path $path into failed state.
.TP
.B restorequeueing map|multipath $map
Restore queuing on multipahted map $map
.TP
+.B forcequeueing daemon
+Forces multipathd into queue_without_daemon mode, so that no_path_retry queueing
+will not be disabled when the daemon stops
+.TP
+.B restorequeueing daemon
+Restores configured queue_without_daemon mode
+.TP
+.B map|multipath $map setprstatus
+Enable persistent reservation management on $map
+.TP
+.B map|multipath $map unsetprstatus
+Disable persistent reservation management on $map
+.TP
+.B map|multipath $map getprstatus
+Get the current persistent reservation management status of $map
+.TP
.B quit|exit
End interactive session.
+.TP
+.B shutdown
+Stop multipathd.
.SH "SYSTEMD INTEGRATION"
When compiled with systemd support two systemd service files are
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/sbin/multipathd
-PIDFILE=/var/run/multipathd.pid
+PIDFILE=/run/multipathd.pid
MPATH_INIT_TIMEOUT=10
ARGS=""
[Unit]
Description=Device-Mapper Multipath Device Controller
Before=iscsi.service iscsid.service lvm2-activation-early.service
-After=syslog.target
+Before=local-fs-pre.target
+After=multipathd.socket
DefaultDependencies=no
+Wants=local-fs-pre.target multipathd.socket blk-availability.service
Conflicts=shutdown.target
[Service]
+[Unit]
+Description=multipathd control socket
+DefaultDependencies=no
+Before=sockets.target
+
[Socket]
ListenStream=@/org/kernel/linux/storage/multipathd
-
-[Install]
-WantedBy=sockets.target
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
+#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <readline/readline.h>
#include <readline/history.h>
+#include <mpath_cmd.h>
#include <uxsock.h>
#include <memory.h>
#include <defaults.h>
/*
* process the client
*/
-static void process(int fd)
+static void process(int fd, unsigned int timeout)
{
char *line;
char *reply;
+ int ret;
cli_init();
rl_readline_name = "multipathd";
rl_completion_entry_function = key_generator;
while ((line = readline("multipathd> "))) {
- size_t len;
size_t llen = strlen(line);
if (!llen) {
if (need_quit(line, llen))
break;
- if (send_packet(fd, line, llen + 1) != 0) break;
- if (recv_packet(fd, &reply, &len) != 0) break;
+ if (send_packet(fd, line) != 0) break;
+ ret = recv_packet(fd, &reply, timeout);
+ if (ret != 0) break;
print_reply(reply);
}
}
-static void process_req(int fd, char * inbuf)
+static void process_req(int fd, char * inbuf, unsigned int timeout)
{
char *reply;
- size_t len;
+ int ret;
- if (send_packet(fd, inbuf, strlen(inbuf) + 1) != 0) {
+ if (send_packet(fd, inbuf) != 0) {
printf("cannot send packet\n");
return;
}
- if (recv_packet(fd, &reply, &len) != 0)
- printf("error receiving packet\n");
- else {
+ ret = recv_packet(fd, &reply, timeout);
+ if (ret < 0) {
+ if (ret == -ETIMEDOUT)
+ printf("timeout receiving packet\n");
+ else
+ printf("error %d receiving packet\n", ret);
+ } else {
printf("%s", reply);
FREE(reply);
}
/*
* entry point
*/
-int uxclnt(char * inbuf)
+int uxclnt(char * inbuf, unsigned int timeout)
{
int fd;
- fd = ux_socket_connect(DEFAULT_SOCKET);
+ fd = mpath_connect();
if (fd == -1)
exit(1);
if (inbuf)
- process_req(fd, inbuf);
+ process_req(fd, inbuf, timeout);
else
- process(fd);
+ process(fd, timeout);
return 0;
}
-int uxclnt(char * inbuf);
+int uxclnt(char * inbuf, unsigned int timeout);
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/poll.h>
+#include <sys/time.h>
#include <signal.h>
#include <checkers.h>
#include <memory.h>
#include <structs_vec.h>
#include <uxsock.h>
#include <defaults.h>
+#include <config.h>
+#include <mpath_cmd.h>
#include "main.h"
#include "cli.h"
struct timespec sleep_time = {5, 0};
struct client {
+ struct list_head node;
int fd;
- struct client *next, *prev;
};
-static struct client *clients;
-static unsigned num_clients;
+LIST_HEAD(clients);
+pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
struct pollfd *polls;
volatile sig_atomic_t reconfig_sig = 0;
volatile sig_atomic_t log_reset_sig = 0;
if (fd == -1)
return;
- /* put it in our linked list */
c = (struct client *)MALLOC(sizeof(*c));
+ if (!c) {
+ close(fd);
+ return;
+ }
memset(c, 0, sizeof(*c));
+ INIT_LIST_HEAD(&c->node);
c->fd = fd;
- c->next = clients;
- if (c->next) c->next->prev = c;
- clients = c;
- num_clients++;
+
+ /* put it in our linked list */
+ pthread_mutex_lock(&client_lock);
+ list_add_tail(&c->node, &clients);
+ pthread_mutex_unlock(&client_lock);
}
/*
*/
static void dead_client(struct client *c)
{
+ pthread_mutex_lock(&client_lock);
+ list_del_init(&c->node);
+ pthread_mutex_unlock(&client_lock);
close(c->fd);
- if (c->prev) c->prev->next = c->next;
- if (c->next) c->next->prev = c->prev;
- if (c == clients) clients = c->next;
+ c->fd = -1;
FREE(c);
- num_clients--;
}
void free_polls (void)
FREE(polls);
}
+void check_timeout(struct timeval start_time, char *inbuf,
+ unsigned int timeout)
+{
+ struct timeval diff_time, end_time;
+
+ if (start_time.tv_sec && gettimeofday(&end_time, NULL) == 0) {
+ timersub(&end_time, &start_time, &diff_time);
+ unsigned long msecs;
+
+ msecs = diff_time.tv_sec * 1000 +
+ diff_time.tv_usec / 1000;
+ if (msecs > timeout)
+ condlog(2, "cli cmd '%s' timeout reached "
+ "after %lu.%06lu secs", inbuf,
+ diff_time.tv_sec, diff_time.tv_usec);
+ }
+}
+
void uxsock_cleanup(void *arg)
{
cli_exit();
/*
* entry point
*/
-void * uxsock_listen(int (*uxsock_trigger)(char *, char **, int *, void *),
- void * trigger_data)
+void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data)
{
int ux_sock;
- size_t len;
- int rlen;
+ int rlen, timeout;
char *inbuf;
char *reply;
sigset_t mask;
ux_sock = ux_socket_listen(DEFAULT_SOCKET);
- if (ux_sock == -1)
- exit(1);
+ if (ux_sock == -1) {
+ condlog(1, "could not create uxsock: %d", errno);
+ return NULL;
+ }
+
+ if (!conf) {
+ condlog(1, "configuration changed");
+ return NULL;
+ }
+
+ timeout = conf->uxsock_timeout;
pthread_cleanup_push(uxsock_cleanup, NULL);
sigdelset(&mask, SIGHUP);
sigdelset(&mask, SIGUSR1);
while (1) {
- struct client *c;
- int i, poll_count;
+ struct pollfd *new;
+ struct client *c, *tmp;
+ int i, poll_count, num_clients;
+
+ /*
+ * Store configuration timeout;
+ * configuration might change during
+ * the call to 'reconfigure'.
+ */
+ if (conf)
+ timeout = conf->uxsock_timeout;
/* setup for a poll */
- polls = REALLOC(polls, (1+num_clients) * sizeof(*polls));
+ pthread_mutex_lock(&client_lock);
+ num_clients = 0;
+ list_for_each_entry(c, &clients, node) {
+ num_clients++;
+ }
+ new = REALLOC(polls, (1+num_clients) * sizeof(*polls));
+ /* If we can't allocate poliing space for the new client,
+ * close it */
+ if (!new) {
+ if (!num_clients) {
+ condlog(1, "can't listen for new clients");
+ return NULL;
+ }
+ dead_client(list_entry(clients.prev,
+ typeof(struct client), node));
+ }
+ else
+ polls = new;
polls[0].fd = ux_sock;
polls[0].events = POLLIN;
/* setup the clients */
- for (i=1, c = clients; c; i++, c = c->next) {
+ i = 1;
+ list_for_each_entry(c, &clients, node) {
polls[i].fd = c->fd;
polls[i].events = POLLIN;
+ i++;
}
+ pthread_mutex_unlock(&client_lock);
/* most of our life is spent in this call */
poll_count = ppoll(polls, i, &sleep_time, &mask);
continue;
/* see if a client wants to speak to us */
- for (i=1, c = clients; c; i++) {
- struct client *next = c->next;
-
+ for (i = 1; i < num_clients + 1; i++) {
if (polls[i].revents & POLLIN) {
- if (recv_packet(c->fd, &inbuf, &len) != 0) {
+ struct timeval start_time;
+
+ c = NULL;
+ pthread_mutex_lock(&client_lock);
+ list_for_each_entry(tmp, &clients, node) {
+ if (tmp->fd == polls[i].fd) {
+ c = tmp;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&client_lock);
+ if (!c) {
+ condlog(3, "cli%d: invalid fd %d",
+ i, polls[i].fd);
+ continue;
+ }
+ if (gettimeofday(&start_time, NULL) != 0)
+ start_time.tv_sec = 0;
+ if (recv_packet(c->fd, &inbuf, timeout) != 0) {
dead_client(c);
} else {
- inbuf[len - 1] = 0;
condlog(4, "Got request [%s]", inbuf);
uxsock_trigger(inbuf, &reply, &rlen,
trigger_data);
if (reply) {
- if (send_packet(c->fd, reply,
- rlen) != 0) {
+ if (send_packet(c->fd,
+ reply) != 0) {
dead_client(c);
}
condlog(4, "Reply [%d bytes]",
FREE(reply);
reply = NULL;
}
+ check_timeout(start_time, inbuf,
+ timeout);
FREE(inbuf);
}
}
- c = next;
}
/* see if we got a new client */
#ifndef _UXLSNR_H
#define _UXLSNR_H
-void * uxsock_listen(int (*uxsock_trigger)
- (char *, char **, int *, void *),
- void * trigger_data);
+typedef int (uxsock_trigger_fn)(char *, char **, int *, void *);
+
+void * uxsock_listen(uxsock_trigger_fn uxsock_trigger,
+ void * trigger_data);
extern volatile sig_atomic_t reconfig_sig;
extern volatile sig_atomic_t log_reset_sig;
+
#endif