$(BUILDDIRS):
$(MAKE) -C $@
-multipath multipathd mpathpersist: libmultipath
-mpathpersist: libmpathpersist
+libmultipath libdmmp: libmpathcmd
+libmpathpersist multipath multipathd: libmultipath
+mpathpersist multipathd: libmpathpersist
+
+libmultipath/checkers.install \
+ libmultipath/prioritizers.install \
+ libmultipath/foreign.install: libmultipath.install
$(BUILDDIRS.clean):
$(MAKE) -C ${@:.clean=} clean
test: all
$(MAKE) -C tests
+valgrind-test: all
+ $(MAKE) -C tests valgrind
+
.PHONY: TAGS
TAGS:
etags -a libmultipath/*.c
ifndef SYSTEMD
ifeq ($(shell pkg-config --modversion libsystemd >/dev/null 2>&1 && echo 1), 1)
- SYSTEMD = $(shell pkg-config --modversion libsystemd)
+ SYSTEMD = $(shell pkg-config --modversion libsystemd | awk '{print $$1}')
else
ifeq ($(shell systemctl --version >/dev/null 2>&1 && echo 1), 1)
SYSTEMD = $(shell systemctl --version 2> /dev/null | \
STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
-WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered,)
+WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
-OPTFLAGS = -O2 -g -pipe -Werror -Wall -Wextra -Wformat=2 -Werror=implicit-int \
+OPTFLAGS := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4
+WARNFLAGS := -Werror -Wall -Wextra -Wformat=2 -Werror=implicit-int \
-Werror=implicit-function-declaration -Werror=format-security \
- $(WNOCLOBBERED) \
- -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) \
- $(STACKPROT) --param=ssp-buffer-size=4
+ $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS)
CPPFLAGS := -Wp,-D_FORTIFY_SOURCE=2
-CFLAGS := $(OPTFLAGS) -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
- -MMD -MP $(CFLAGS)
+CFLAGS := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \
+ -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
+ -MMD -MP
BIN_CFLAGS = -fPIE -DPIE
LIB_CFLAGS = -fPIC
SHARED_FLAGS = -shared
-LDFLAGS = -Wl,-z,relro -Wl,-z,now
+LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now
BIN_LDFLAGS = -pie
# Check whether a function with name $1 has been declared in header file $2.
+++ /dev/null
- multipath-tools for Linux <http://christophe.varoqui.free.fr/>
-
-
-This package provides the following binaries to drive the Device Mapper
-multipathing driver:
-
-multipath - Device mapper target autoconfig.
-multipathd - Multipath daemon.
-mpathpersist - Manages SCSI persistent reservations on dm multipath devices.
-kpartx - Create device maps from partition tables.
-
-
-Releases
-========
-Tarballs are not generated anymore, to get a specific release do:
-git clone https://git.opensvc.com/multipath-tools/.git
-cd multipath-tools
-git tag
-git archive --format=tar.gz --prefix=multipath-tools-X.Y.Z/ X.Y.Z > ../multipath-tools-X.Y.Z.tar.gz
-
-Alternatively it may be obtained from gitweb, go to:
-https://git.opensvc.com/?p=multipath-tools/.git;a=tags
-select a release-tag and then click on "snapshot". Or get it with
-wget "https://git.opensvc.com/?p=multipath-tools/.git;a=snapshot;sf=tgz;h=refs/tags/X.Y.Z" -O multipath-tools-X.Y.Z.tar.gz
-
-
-Source code
-===========
-To get latest devel code: git clone https://git.opensvc.com/multipath-tools/.git
-Gitweb: https://git.opensvc.com/?p=multipath-tools/.git
-
-
-Add storage devices
-===================
-Follow the instructions in the libmultipath/hwtable.c header.
-
-
-Mailing list (subscribers-only)
-============
-To subscribe and archives: https://www.redhat.com/mailman/listinfo/dm-devel
-Searchable: https://marc.info/?l=dm-devel
-
-
-Changelog
-=========
-pre-0.4.5: https://web.archive.org/web/20070309224034/http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog
-post-0.4.5: https://git.opensvc.com/?p=multipath-tools/.git;a=log
-
-
-Maintainer
-==========
-Christophe Varoqui <christophe.varoqui@opensvc.com>
-Device-mapper development mailing list <dm-devel@redhat.com>
-
-Licence
-=======
-The multipath-tools source code is covered by several different
-licences. Refer to the individual source files for details.
-Source files which do not specify a licence are shipped under
-LGPL-2.0 (see LICENSES/LGPL-2.0).
--- /dev/null
+multipath-tools for Linux
+*************************
+
+https://github.com/opensvc/multipath-tools
+
+This package provides the following binaries to drive the Device Mapper multipathing driver:
+
+* multipath - Device mapper target autoconfig.
+* multipathd - Multipath daemon.
+* mpathpersist - Manages SCSI persistent reservations on dm multipath devices.
+* kpartx - Create device maps from partition tables.
+
+
+Releases
+========
+
+To get a specific X.Y.Z release, use one of the following method:
+
+
+Git
+---
+
+ git clone https://github.com/opensvc/multipath-tools.git
+ cd multipath-tools
+ git tag
+ git archive --format=tar.gz --prefix=multipath-tools-X.Y.Z/ X.Y.Z > ../multipath-tools-X.Y.Z.tar.gz
+
+
+Direct download
+---------------
+
+ wget "https://github.com/opensvc/multipath-tools/archive/X.Y.Z.tar.gz" -O multipath-tools-X.Y.Z.tar.gz
+
+
+Browser
+-------
+
+Go to: https://github.com/opensvc/multipath-tools/tags
+Select a release-tag and then click on "zip" or "tar.gz".
+
+
+Source code
+===========
+
+To get latest devel code:
+
+ git clone https://github.com/opensvc/multipath-tools.git
+
+Github page: https://github.com/opensvc/multipath-tools
+
+
+Add storage devices
+===================
+
+Follow the instructions in the `libmultipath/hwtable.c` header.
+
+
+Mailing list
+============
+
+(subscribers-only)
+To subscribe and archives: https://www.redhat.com/mailman/listinfo/dm-devel
+Searchable: https://marc.info/?l=dm-devel
+
+
+Changelog
+=========
+
+pre-0.4.5: https://web.archive.org/web/20070309224034/http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog
+post-0.4.5: https://github.com/opensvc/multipath-tools/commits/master
+
+
+Maintainer
+==========
+
+Christophe Varoqui <christophe.varoqui@opensvc.com>
+Device-mapper development mailing list <dm-devel@redhat.com>
+
+
+Licence
+=======
+
+The multipath-tools source code is covered by several different licences.
+Refer to the individual source files for details.
+Source files which do not specify a licence are shipped under LGPL-2.0
+(see `LICENSES/LGPL-2.0`).
+
#include "kpartx.h"
#include <stdio.h>
+#define BSD_LABEL_OFFSET 64
#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */
#define XBSD_MAXPARTITIONS 16
#define BSD_FS_UNUSED 0
return -1;
l = (struct bsd_disklabel *) bp;
- if (l->d_magic != BSD_DISKMAGIC)
- return -1;
+ if (l->d_magic != BSD_DISKMAGIC) {
+ /*
+ * BSD disklabels can also start 64 bytes offset from the
+ * start of the first sector
+ */
+ bp = getblock(fd, offset);
+ if (bp == NULL)
+ return -1;
+
+ l = (struct bsd_disklabel *)(bp + 64);
+ if (l->d_magic != BSD_DISKMAGIC)
+ return -1;
+ }
max_partitions = 16;
if (l->d_npartitions < max_partitions)
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
sprintf(pathname, "/dev/.kpartx-node-%u-%u",
(unsigned int)major(dev), (unsigned int)minor(dev));
- if ((fd_dasd = open(pathname, O_RDONLY)) == -1) {
+ if ((fd_dasd = open(pathname, O_RDONLY | O_DIRECT)) == -1) {
/* Devicenode does not exist. Try to create one */
if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) {
/* Couldn't create a device node */
return -1;
}
- fd_dasd = open(pathname, O_RDONLY);
+ fd_dasd = open(pathname, O_RDONLY | O_DIRECT);
/*
* The file will vanish when the last process (we)
* has ceased to access it.
* Get volume label, extract name and type.
*/
- if (!(data = (unsigned char *)malloc(blocksize)))
+ if (aligned_malloc((void **)&data, blocksize, NULL))
goto out;
if (dm_get_opencount(name)) {
if (rd->verbose)
- printf("%s is in use. Not removing", name);
+ printf("%s is in use. Not removing\n", name);
return 1;
}
if (!dm_simplecmd(DM_DEVICE_REMOVE, name, 0, 0)) {
if (!count) return NULL;
- pte = (gpt_entry *)malloc(count);
- if (!pte)
+ if (aligned_malloc((void **)&pte, get_sector_size(fd), &count))
return NULL;
memset(pte, 0, count);
alloc_read_gpt_header(int fd, uint64_t lba)
{
gpt_header *gpt;
- gpt = (gpt_header *)
- malloc(sizeof (gpt_header));
- if (!gpt)
+ size_t size = sizeof (gpt_header);
+ if (aligned_malloc((void **)&gpt, get_sector_size(fd), &size))
return NULL;
- memset(gpt, 0, sizeof (*gpt));
- if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) {
+ memset(gpt, 0, size);
+ if (!read_lba(fd, lba, gpt, size)) {
free(gpt);
return NULL;
}
gpt_header *pgpt = NULL, *agpt = NULL;
gpt_entry *pptes = NULL, *aptes = NULL;
legacy_mbr *legacymbr = NULL;
+ size_t size = sizeof(legacy_mbr);
uint64_t lastlba;
if (!gpt || !ptes)
return 0;
}
/* This will be added to the EFI Spec. per Intel after v1.02. */
- legacymbr = malloc(sizeof (*legacymbr));
- if (legacymbr) {
- memset(legacymbr, 0, sizeof (*legacymbr));
- read_lba(fd, 0, (uint8_t *) legacymbr,
- sizeof (*legacymbr));
+ if (aligned_malloc((void **)&legacymbr, get_sector_size(fd),
+ &size) == 0) {
+ memset(legacymbr, 0, size);
+ read_lba(fd, 0, (uint8_t *) legacymbr, size);
good_pmbr = is_pmbr_valid(legacymbr);
free(legacymbr);
legacymbr=NULL;
* cva, 2002-10-26
*/
+#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
-#define READ_SIZE 1024
#define MAXTYPES 64
#define MAXSLICES 256
#define DM_TARGET "linear"
return 0;
}
+static void *
+xmalloc (size_t size) {
+ void *t;
+
+ if (size == 0)
+ return NULL;
+
+ t = malloc (size);
+
+ if (t == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ exit(1);
+ }
+
+ return t;
+}
+
int
main(int argc, char **argv){
int i, j, m, n, op, off, arg, c, d, ro=0;
mapname = device + off;
if (delim == NULL) {
- delim = malloc(DELIM_SIZE);
+ delim = xmalloc(DELIM_SIZE);
memset(delim, 0, DELIM_SIZE);
set_delimiter(mapname, delim);
}
- fd = open(device, O_RDONLY);
+ fd = open(device, O_RDONLY | O_DIRECT);
if (fd == -1) {
perror(device);
if (!dm_simplecmd(DM_DEVICE_REMOVE,
partname, 1, 0)) {
+ fprintf(stderr, "failed to remove %s",
+ partname);
r++;
continue;
}
return r;
}
-void *
-xmalloc (size_t size) {
- void *t;
-
- if (size == 0)
- return NULL;
-
- t = malloc (size);
-
- if (t == NULL) {
- fprintf(stderr, "Out of memory\n");
- exit(1);
- }
-
- return t;
-}
-
/*
* sseek: seek to specified sector
*/
static int
-sseek(int fd, unsigned int secnr) {
+sseek(int fd, unsigned int secnr, int secsz) {
off64_t in, out;
- in = ((off64_t) secnr << 9);
+ in = ((off64_t) secnr * secsz);
out = 1;
if ((out = lseek64(fd, in, SEEK_SET)) != in)
return 0;
}
+int
+aligned_malloc(void **mem_p, size_t align, size_t *size_p)
+{
+ static size_t pgsize = 0;
+ size_t size;
+ int err;
+
+ if (!mem_p || !align || (size_p && !*size_p))
+ return EINVAL;
+
+ if (!pgsize)
+ pgsize = getpagesize();
+
+ if (size_p)
+ size = ((*size_p + align - 1) / align) * align;
+ else
+ size = pgsize;
+
+ err = posix_memalign(mem_p, pgsize, size);
+ if (!err && size_p)
+ *size_p = size;
+ return err;
+}
+
+/* always in sector size blocks */
static
struct block {
unsigned int secnr;
struct block *next;
} *blockhead;
+/* blknr is always in 512 byte blocks */
char *
-getblock (int fd, unsigned int secnr) {
+getblock (int fd, unsigned int blknr) {
+ int secsz = get_sector_size(fd);
+ unsigned int blks_per_sec = secsz / 512;
+ unsigned int secnr = blknr / blks_per_sec;
+ unsigned int blk_off = (blknr % blks_per_sec) * 512;
struct block *bp;
for (bp = blockhead; bp; bp = bp->next)
if (bp->secnr == secnr)
- return bp->block;
+ return bp->block + blk_off;
- if (sseek(fd, secnr))
+ if (sseek(fd, secnr, secsz))
return NULL;
bp = xmalloc(sizeof(struct block));
bp->secnr = secnr;
bp->next = blockhead;
blockhead = bp;
- bp->block = (char *) xmalloc(READ_SIZE);
+ if (aligned_malloc((void **)&bp->block, secsz, NULL)) {
+ fprintf(stderr, "aligned_malloc failed\n");
+ exit(1);
+ }
- if (read(fd, bp->block, READ_SIZE) != READ_SIZE) {
+ if (read(fd, bp->block, secsz) != secsz) {
fprintf(stderr, "read error, sector %d\n", secnr);
- bp->block = NULL;
+ blockhead = bp->next;
+ return NULL;
}
- return bp->block;
+ return bp->block + blk_off;
}
int
#ifndef _KPARTX_H
#define _KPARTX_H
+#include <stddef.h>
#include <stdint.h>
#include <sys/ioctl.h>
extern ptreader read_sun_pt;
extern ptreader read_ps3_pt;
+int aligned_malloc(void **mem_p, size_t align, size_t *size_p);
char *getblock(int fd, unsigned int secnr);
static inline unsigned int
KERNEL!="dm-*", GOTO="kpartx_end"
ACTION!="add|change", GOTO="kpartx_end"
ENV{DM_UUID}!="?*", GOTO="kpartx_end"
+ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", GOTO="kpartx_end"
# Create dm tables for partitions on multipath devices.
ENV{DM_UUID}!="mpath-?*", GOTO="mpath_kpartx_end"
# DM_SUBSYSTEM_UDEV_FLAG1 is the "skip_kpartx" flag.
-# For events not generated by libdevmapper, we need to fetch it from db.
-ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
+# For events not generated by libdevmapper, we need to fetch it from db:
+# - "change" events with DM_ACTIVATION!="1" (e.g. partition table changes)
+# - "add" events for which rules are not disabled ("coldplug" case)
+ENV{DM_ACTIVATION}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
+ACTION=="add", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
ENV{DM_SUBSYSTEM_UDEV_FLAG1}=="1", GOTO="mpath_kpartx_end"
# 11-dm-mpath.rules sets MPATH_UNCHANGED for events that can be ignored.
#include <stdint.h>
#include <string.h>
#include <assert.h>
+#include <stdbool.h>
#include <json.h>
#include "libdmmp/libdmmp.h"
do { \
json_type j_type = json_type_null; \
json_object *j_obj_tmp = NULL; \
- if (json_object_object_get_ex(j_obj, key, &j_obj_tmp) != TRUE) { \
+ if (json_object_object_get_ex(j_obj, key, &j_obj_tmp) != true) { \
_error(ctx, "Invalid JSON output from multipathd IPC: " \
"key '%s' not found", key); \
rc = DMMP_ERR_IPC_ERROR; \
return 0;
}
-static int
-updatepaths (struct multipath * mpp)
-{
- int i, j;
- struct pathgroup * pgp;
- struct path * pp;
- struct config *conf;
-
- if (!mpp->pg)
- return 0;
-
- vector_foreach_slot (mpp->pg, pgp, i){
- if (!pgp->paths)
- continue;
-
- vector_foreach_slot (pgp->paths, pp, j){
- if (!strlen(pp->dev)){
- /*
- * path is not in sysfs anymore
- */
- pp->state = PATH_DOWN;
- continue;
- }
- pp->mpp = mpp;
- if (pp->udev == NULL) {
- pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
- if (pp->udev == NULL) {
- pp->state = PATH_DOWN;
- continue;
- }
- conf = get_multipath_config();
- pathinfo(pp, conf, DI_SYSFS|DI_CHECKER);
- put_multipath_config(conf);
- continue;
- }
- if (pp->state == PATH_UNCHECKED ||
- pp->state == PATH_WILD) {
- conf = get_multipath_config();
- pathinfo(pp, conf, DI_CHECKER);
- put_multipath_config(conf);
- }
- }
- }
- return 0;
-}
-
int
mpath_prin_activepath (struct multipath *mpp, int rq_servact,
struct prin_resp * resp, int noisy)
{
int i;
struct multipath *mpp;
- char params[PARAMS_SIZE], status[PARAMS_SIZE];
vector_foreach_slot (curmp, mpp, i){
/*
if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
continue;
- dm_get_map(mpp->alias, &mpp->size, params);
- condlog(3, "params = %s", params);
- dm_get_status(mpp->alias, status);
- condlog(3, "status = %s", status);
- disassemble_map (pathvec, params, mpp, 0);
-
- /*
- * disassemble_map() can add new paths to pathvec.
- * If not in "fast list mode", we need to fetch information
- * about them
- */
- updatepaths(mpp);
- disassemble_status (status, mpp);
-
+ if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
+ update_multipath_status(mpp) != DMP_OK) {
+ condlog(1, "error parsing map %s", mpp->wwid);
+ remove_map(mpp, pathvec, curmp, PURGE_VEC);
+ i--;
+ }
}
return MPATH_PR_SUCCESS ;
}
all_tg_pt = (mpp->all_tg_pt == ALL_TG_PT_ON ||
paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK);
- active_pathcount = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST);
+ active_pathcount = count_active_paths(mpp);
if (active_pathcount == 0) {
condlog (0, "%s: no path available", mpp->wwid);
if (!mpp)
return MPATH_PR_DMMP_ERROR;
- active_pathcount = pathcount (mpp, PATH_UP) + pathcount (mpp, PATH_GHOST);
+ active_pathcount = count_active_paths(mpp);
struct threadinfo thread[active_pathcount];
memset(thread, 0, sizeof(thread));
#include <stdio.h>
#include <stdlib.h>
+#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
return status;
}
+/*
+ * Helper macro to avoid overflow of prout_param_descriptor in
+ * format_transportids(). Data must not be written past
+ * MPATH_MAX_PARAM_LEN bytes from struct prout_param_descriptor.
+ */
+#define check_overflow(ofs, n, start, label) \
+ do { \
+ if ((ofs) + (n) + \
+ offsetof(struct prout_param_descriptor, private_buffer) \
+ > MPATH_MAX_PARAM_LEN) \
+ { \
+ (ofs) = (start); \
+ goto label; \
+ } \
+ } while(0)
+
uint32_t format_transportids(struct prout_param_descriptor *paramp)
{
unsigned int i = 0, len;
uint32_t buff_offset = 4;
- memset(paramp->private_buffer, 0, MPATH_MAX_PARAM_LEN);
+ memset(paramp->private_buffer, 0, sizeof(paramp->private_buffer));
for (i=0; i < paramp->num_transportid; i++ )
{
+ uint32_t start_offset = buff_offset;
+
+ check_overflow(buff_offset, 1, start_offset, end_loop);
paramp->private_buffer[buff_offset] = (uint8_t)((paramp->trnptid_list[i]->format_code & 0xff)|
(paramp->trnptid_list[i]->protocol_id & 0xff));
buff_offset += 1;
switch(paramp->trnptid_list[i]->protocol_id)
{
case MPATH_PROTOCOL_ID_FC:
+ check_overflow(buff_offset, 7 + 8 + 8,
+ start_offset, end_loop);
buff_offset += 7;
memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->n_port_name, 8);
buff_offset +=8 ;
buff_offset +=8 ;
break;
case MPATH_PROTOCOL_ID_SAS:
+ check_overflow(buff_offset, 3 + 12,
+ start_offset, end_loop);
buff_offset += 3;
memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->sas_address, 8);
buff_offset += 12;
break;
case MPATH_PROTOCOL_ID_ISCSI:
- buff_offset += 1;
len = (paramp->trnptid_list[i]->iscsi_name[1] & 0xff)+2;
+ check_overflow(buff_offset, 1 + len,
+ start_offset, end_loop);
+ buff_offset += 1;
memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->iscsi_name,len);
buff_offset += len ;
break;
}
}
+end_loop:
buff_offset -= 4;
paramp->private_buffer[0] = (unsigned char)((buff_offset >> 24) & 0xff);
paramp->private_buffer[1] = (unsigned char)((buff_offset >> 16) & 0xff);
uint32_t additional_length, k, tid_len_len = 0;
char tempbuff[MPATH_MAX_PARAM_LEN];
struct prin_fulldescr fdesc;
+ static const unsigned int pbuf_size =
+ sizeof(pr_buff->prin_descriptor.prin_readfd.private_buffer);
convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readfd.prgeneration);
convert_be32_to_cpu(&pr_buff->prin_descriptor.prin_readfd.number_of_descriptor);
}
additional_length = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor;
- if (additional_length > MPATH_MAX_PARAM_LEN) {
+ if (additional_length > pbuf_size) {
condlog(3, "PRIN length %u exceeds max length %d", additional_length,
- MPATH_MAX_PARAM_LEN);
+ pbuf_size);
return;
}
memset(&fdesc, 0, sizeof(struct prin_fulldescr));
- memcpy( tempbuff, pr_buff->prin_descriptor.prin_readfd.private_buffer,MPATH_MAX_PARAM_LEN );
- memset(&pr_buff->prin_descriptor.prin_readfd.private_buffer, 0, MPATH_MAX_PARAM_LEN);
+ memcpy( tempbuff, pr_buff->prin_descriptor.prin_readfd.private_buffer,
+ pbuf_size);
+ memset(&pr_buff->prin_descriptor.prin_readfd.private_buffer, 0,
+ pbuf_size);
p =(unsigned char *)tempbuff;
ppbuff = (char *)pr_buff->prin_descriptor.prin_readfd.private_buffer;
mx_resp_len = 0;
break;
}
+ if (mx_resp_len > MPATH_MAX_PARAM_LEN)
+ mx_resp_len = MPATH_MAX_PARAM_LEN;
return mx_resp_len;
}
CFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE
endif
+ifneq ($(call check_func,dm_task_get_errno,/usr/include/libdevmapper.h),0)
+ CFLAGS += -DLIBDM_API_GET_ERRNO
+endif
+
ifneq ($(call check_func,dm_task_set_cookie,/usr/include/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_COOKIE
endif
CFLAGS += -DLIBDM_API_DEFERRED
endif
+ifneq ($(call check_func,dm_hold_control_dev,/usr/include/libdevmapper.h),0)
+ CFLAGS += -DLIBDM_API_HOLD_CONTROL
+endif
+
OBJS = memory.o parser.o vector.o devmapper.o callout.o \
hwtable.o blacklist.o util.o dmparser.o config.o \
structs.o discovery.o propsel.o dict.o \
switchgroup.o uxsock.o print.o alias.o log_pthread.o \
log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \
lock.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \
- io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o
+ io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o \
+ libsg.o valid.o
all: $(LIBS)
*/
#include <stdlib.h>
#include <errno.h>
+#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include "vector.h"
#include "checkers.h"
#include "structs.h"
+#include "config.h"
+#include "util.h"
+#include "errno.h"
/*
* See the file COPYING included with this distribution for more details.
*/
+#define BINDINGS_FILE_HEADER \
+"# Multipath bindings, Version : 1.0\n" \
+"# NOTE: this file is automatically maintained by the multipath program.\n" \
+"# You should not need to edit this file in normal circumstances.\n" \
+"#\n" \
+"# Format:\n" \
+"# alias wwid\n" \
+"#\n"
+
+static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
+
int
valid_alias(const char *alias)
{
rewind(f);
while (fgets(buf, LINE_MAX, f)) {
const char *alias, *wwid;
- char *c;
+ char *c, *saveptr;
int curr_id;
line_nr++;
c = strpbrk(buf, "#\n\r");
if (c)
*c = '\0';
- alias = strtok(buf, " \t");
+ alias = strtok_r(buf, " \t", &saveptr);
if (!alias) /* blank line */
continue;
curr_id = scan_devname(alias, prefix);
biggest_id = curr_id;
if (curr_id > id && curr_id < smallest_bigger_id)
smallest_bigger_id = curr_id;
- wwid = strtok(NULL, " \t");
+ wwid = strtok_r(NULL, " \t", &saveptr);
if (!wwid){
condlog(3,
"Ignoring malformed line %u in bindings file",
buff[0] = '\0';
while (fgets(line, LINE_MAX, f)) {
- char *c;
+ char *c, *saveptr;
const char *alias, *wwid;
line_nr++;
c = strpbrk(line, "#\n\r");
if (c)
*c = '\0';
- alias = strtok(line, " \t");
+ alias = strtok_r(line, " \t", &saveptr);
if (!alias) /* blank line */
continue;
- wwid = strtok(NULL, " \t");
+ wwid = strtok_r(NULL, " \t", &saveptr);
if (!wwid){
condlog(3,
"Ignoring malformed line %u in bindings file",
}
if (strcmp(alias, map_alias) == 0){
condlog(3, "Found matching alias [%s] in bindings file."
- "\nSetting wwid to %s", alias, wwid);
+ " Setting wwid to %s", alias, wwid);
strlcpy(buff, wwid, WWID_SIZE);
return 0;
}
char buff[WWID_SIZE];
FILE *f;
- fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
+ fd = open_file(file, &can_write, bindings_file_header);
if (fd < 0)
return NULL;
return NULL;
}
- fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
+ fd = open_file(file, &can_write, bindings_file_header);
if (fd < 0)
return NULL;
return -1;
}
- fd = open_file(file, &unused, BINDINGS_FILE_HEADER);
+ fd = open_file(file, &unused, bindings_file_header);
if (fd < 0)
return -1;
fclose(f);
return 0;
}
+
+struct binding {
+ char *alias;
+ char *wwid;
+};
+
+static void _free_binding(struct binding *bdg)
+{
+ free(bdg->wwid);
+ free(bdg->alias);
+ free(bdg);
+}
+
+/*
+ * Perhaps one day we'll implement this more efficiently, thus use
+ * an abstract type.
+ */
+typedef struct _vector Bindings;
+
+static void free_bindings(Bindings *bindings)
+{
+ struct binding *bdg;
+ int i;
+
+ vector_foreach_slot(bindings, bdg, i)
+ _free_binding(bdg);
+ vector_reset(bindings);
+}
+
+enum {
+ BINDING_EXISTS,
+ BINDING_CONFLICT,
+ BINDING_ADDED,
+ BINDING_DELETED,
+ BINDING_NOTFOUND,
+ BINDING_ERROR,
+};
+
+static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
+{
+ struct binding *bdg;
+ int i, cmp = 0;
+
+ /*
+ * Keep the bindings array sorted by alias.
+ * Optimization: Search backwards, assuming that the bindings file is
+ * sorted already.
+ */
+ vector_foreach_slot_backwards(bindings, bdg, i) {
+ if ((cmp = strcmp(bdg->alias, alias)) <= 0)
+ break;
+ }
+
+ /* Check for exact match */
+ if (i >= 0 && cmp == 0)
+ return strcmp(bdg->wwid, wwid) ?
+ BINDING_CONFLICT : BINDING_EXISTS;
+
+ i++;
+ bdg = calloc(1, sizeof(*bdg));
+ if (bdg) {
+ bdg->wwid = strdup(wwid);
+ bdg->alias = strdup(alias);
+ if (bdg->wwid && bdg->alias &&
+ vector_insert_slot(bindings, i, bdg))
+ return BINDING_ADDED;
+ else
+ _free_binding(bdg);
+ }
+
+ return BINDING_ERROR;
+}
+
+static int write_bindings_file(const Bindings *bindings, int fd)
+{
+ struct binding *bnd;
+ char line[LINE_MAX];
+ int i;
+
+ if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
+ != sizeof(BINDINGS_FILE_HEADER) - 1)
+ return -1;
+
+ vector_foreach_slot(bindings, bnd, i) {
+ int len;
+
+ len = snprintf(line, sizeof(line), "%s %s\n",
+ bnd->alias, bnd->wwid);
+
+ if (len < 0 || (size_t)len >= sizeof(line)) {
+ condlog(1, "%s: line overflow", __func__);
+ return -1;
+ }
+
+ if (write(fd, line, len) != len)
+ return -1;
+ }
+ return 0;
+}
+
+static int fix_bindings_file(const struct config *conf,
+ const Bindings *bindings)
+{
+ int rc;
+ long fd;
+ char tempname[PATH_MAX];
+
+ if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
+ return -1;
+ if ((fd = mkstemp(tempname)) == -1) {
+ condlog(1, "%s: mkstemp: %m", __func__);
+ return -1;
+ }
+ pthread_cleanup_push(close_fd, (void*)fd);
+ rc = write_bindings_file(bindings, fd);
+ pthread_cleanup_pop(1);
+ if (rc == -1) {
+ condlog(1, "failed to write new bindings file %s",
+ tempname);
+ unlink(tempname);
+ return rc;
+ }
+ if ((rc = rename(tempname, conf->bindings_file)) == -1)
+ condlog(0, "%s: rename: %m", __func__);
+ else
+ condlog(1, "updated bindings file %s", conf->bindings_file);
+ return rc;
+}
+
+static int _check_bindings_file(const struct config *conf, FILE *file,
+ Bindings *bindings)
+{
+ int rc = 0;
+ unsigned int linenr = 0;
+ char *line = NULL;
+ size_t line_len = 0;
+ ssize_t n;
+
+ pthread_cleanup_push(cleanup_free_ptr, &line);
+ while ((n = getline(&line, &line_len, file)) >= 0) {
+ char *c, *alias, *wwid, *saveptr;
+ const char *mpe_wwid;
+
+ linenr++;
+ c = strpbrk(line, "#\n\r");
+ if (c)
+ *c = '\0';
+ alias = strtok_r(line, " \t", &saveptr);
+ if (!alias) /* blank line */
+ continue;
+ wwid = strtok_r(NULL, " \t", &saveptr);
+ if (!wwid) {
+ condlog(1, "invalid line %d in bindings file, missing WWID",
+ linenr);
+ continue;
+ }
+ c = strtok_r(NULL, " \t", &saveptr);
+ if (c)
+ /* This is non-fatal */
+ condlog(1, "invalid line %d in bindings file, extra args \"%s\"",
+ linenr, c);
+
+ mpe_wwid = get_mpe_wwid(conf->mptable, alias);
+ if (mpe_wwid && strcmp(mpe_wwid, wwid)) {
+ condlog(0, "ERROR: alias \"%s\" for WWID %s in bindings file "
+ "on line %u conflicts with multipath.conf entry for %s",
+ alias, wwid, linenr, mpe_wwid);
+ rc = -1;
+ continue;
+ }
+
+ switch (add_binding(bindings, alias, wwid)) {
+ case BINDING_CONFLICT:
+ condlog(0, "ERROR: multiple bindings for alias \"%s\" in "
+ "bindings file on line %u, discarding binding to WWID %s",
+ alias, linenr, wwid);
+ rc = -1;
+ break;
+ case BINDING_EXISTS:
+ condlog(2, "duplicate line for alias %s in bindings file on line %u",
+ alias, linenr);
+ break;
+ case BINDING_ERROR:
+ condlog(2, "error adding binding %s -> %s",
+ alias, wwid);
+ break;
+ default:
+ break;
+ }
+ }
+ pthread_cleanup_pop(1);
+ return rc;
+}
+
+static void cleanup_fclose(void *p)
+{
+ fclose(p);
+}
+
+/*
+ * check_alias_settings(): test for inconsistent alias configuration
+ *
+ * It's a fatal configuration error if the same alias is assigned to
+ * multiple WWIDs. In the worst case, it can cause data corruption
+ * by mangling devices with different WWIDs into the same multipath map.
+ * This function tests the configuration from multipath.conf and the
+ * bindings file for consistency, drops inconsistent multipath.conf
+ * alias settings, and rewrites the bindings file if necessary, dropping
+ * conflicting lines (if user_friendly_names is on, multipathd will
+ * fill in the deleted lines with a newly generated alias later).
+ * Note that multipath.conf is not rewritten. Use "multipath -T" for that.
+ *
+ * Returns: 0 in case of success, -1 if the configuration was bad
+ * and couldn't be fixed.
+ */
+int check_alias_settings(const struct config *conf)
+{
+ int can_write;
+ int rc = 0, i, fd;
+ Bindings bindings = {.allocated = 0, };
+ struct mpentry *mpe;
+
+ pthread_cleanup_push_cast(free_bindings, &bindings);
+ vector_foreach_slot(conf->mptable, mpe, i) {
+ if (!mpe->wwid || !mpe->alias)
+ continue;
+ if (add_binding(&bindings, mpe->alias, mpe->wwid) ==
+ BINDING_CONFLICT) {
+ condlog(0, "ERROR: alias \"%s\" bound to multiple wwids in multipath.conf, "
+ "discarding binding to %s",
+ mpe->alias, mpe->wwid);
+ free(mpe->alias);
+ mpe->alias = NULL;
+ }
+ }
+ /* This clears the bindings */
+ pthread_cleanup_pop(1);
+
+ pthread_cleanup_push_cast(free_bindings, &bindings);
+ fd = open_file(conf->bindings_file, &can_write, BINDINGS_FILE_HEADER);
+ if (fd != -1) {
+ FILE *file = fdopen(fd, "r");
+
+ if (file != NULL) {
+ pthread_cleanup_push(cleanup_fclose, file);
+ rc = _check_bindings_file(conf, file, &bindings);
+ pthread_cleanup_pop(1);
+ if (rc == -1 && can_write && !conf->bindings_read_only)
+ rc = fix_bindings_file(conf, &bindings);
+ else if (rc == -1)
+ condlog(0, "ERROR: bad settings in read-only bindings file %s",
+ conf->bindings_file);
+ } else {
+ condlog(1, "failed to fdopen %s: %m",
+ conf->bindings_file);
+ close(fd);
+ }
+ }
+ pthread_cleanup_pop(1);
+ return rc;
+}
-#define BINDINGS_FILE_HEADER \
-"# Multipath bindings, Version : 1.0\n" \
-"# NOTE: this file is automatically maintained by the multipath program.\n" \
-"# You should not need to edit this file in normal circumstances.\n" \
-"#\n" \
-"# Format:\n" \
-"# alias wwid\n" \
-"#\n"
+#ifndef _ALIAS_H
+#define _ALIAS_H
int valid_alias(const char *alias);
char *get_user_friendly_alias(const char *wwid, const char *file,
char *use_existing_alias (const char *wwid, const char *file,
const char *alias_old,
const char *prefix, int bindings_read_only);
+
+struct config;
+int check_alias_settings(const struct config *);
+
+#endif /* _ALIAS_H */
#include "structs_vec.h"
#include "print.h"
-int store_ble(vector blist, char * str, int origin)
+char *check_invert(char *str, bool *invert)
+{
+ if (str[0] == '!') {
+ *invert = true;
+ return str + 1;
+ }
+ if (str[0] == '\\' && str[1] == '!') {
+ *invert = false;
+ return str + 1;
+ }
+ *invert = false;
+ return str;
+}
+
+int store_ble(vector blist, const char *str, int origin)
{
struct blentry * ble;
+ char *regex_str;
+ char *strdup_str = NULL;
if (!str)
return 0;
+ strdup_str = strdup(str);
+ if (!strdup_str)
+ return 1;
+
if (!blist)
goto out;
if (!ble)
goto out;
- if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB))
+ regex_str = check_invert(strdup_str, &ble->invert);
+ if (regcomp(&ble->regex, regex_str, REG_EXTENDED|REG_NOSUB))
goto out1;
if (!vector_alloc_slot(blist))
goto out1;
- ble->str = str;
+ ble->str = strdup_str;
ble->origin = origin;
vector_set_slot(blist, ble);
return 0;
out1:
FREE(ble);
out:
- FREE(str);
+ FREE(strdup_str);
return 1;
}
int alloc_ble_device(vector blist)
{
- struct blentry_device * ble = MALLOC(sizeof(struct blentry_device));
+ struct blentry_device *ble;
+ if (!blist)
+ return 1;
+
+ ble = MALLOC(sizeof(struct blentry_device));
if (!ble)
return 1;
- if (!blist || !vector_alloc_slot(blist)) {
+ if (!vector_alloc_slot(blist)) {
FREE(ble);
return 1;
}
return 0;
}
-int set_ble_device(vector blist, char * vendor, char * product, int origin)
+int set_ble_device(vector blist, const char *vendor, const char *product, int origin)
{
struct blentry_device * ble;
+ char *regex_str;
+ char *vendor_str = NULL;
+ char *product_str = NULL;
if (!blist)
return 1;
return 1;
if (vendor) {
- if (regcomp(&ble->vendor_reg, vendor,
- REG_EXTENDED|REG_NOSUB)) {
- FREE(vendor);
- if (product)
- FREE(product);
- return 1;
- }
- ble->vendor = vendor;
+ vendor_str = STRDUP(vendor);
+ if (!vendor_str)
+ goto out;
+
+ regex_str = check_invert(vendor_str, &ble->vendor_invert);
+ if (regcomp(&ble->vendor_reg, regex_str, REG_EXTENDED|REG_NOSUB))
+ goto out;
+
+ ble->vendor = vendor_str;
}
if (product) {
- if (regcomp(&ble->product_reg, product,
- REG_EXTENDED|REG_NOSUB)) {
- FREE(product);
- if (vendor) {
- ble->vendor = NULL;
- FREE(vendor);
- }
- return 1;
- }
- ble->product = product;
- }
- ble->origin = origin;
- return 0;
-}
+ product_str = STRDUP(product);
+ if (!product_str)
+ goto out1;
-int
-_blacklist_exceptions (vector elist, const char * str)
-{
- int i;
- struct blentry * ele;
+ regex_str = check_invert(product_str, &ble->product_invert);
+ if (regcomp(&ble->product_reg, regex_str, REG_EXTENDED|REG_NOSUB))
+ goto out1;
- vector_foreach_slot (elist, ele, i) {
- if (!regexec(&ele->regex, str, 0, NULL, 0))
- return 1;
+ ble->product = product_str;
}
+ ble->origin = origin;
return 0;
+out1:
+ if (vendor) {
+ regfree(&ble->vendor_reg);
+ ble->vendor = NULL;
+ }
+out:
+ free(vendor_str);
+ free(product_str);
+ return 1;
}
-int
-_blacklist (vector blist, const char * str)
+static int
+match_reglist (const struct _vector *blist, const char *str)
{
int i;
struct blentry * ble;
vector_foreach_slot (blist, ble, i) {
- if (!regexec(&ble->regex, str, 0, NULL, 0))
- return 1;
- }
- return 0;
-}
-
-int
-_blacklist_exceptions_device(const struct _vector *elist, const char * vendor,
- const char * product)
-{
- int i;
- struct blentry_device * ble;
-
- vector_foreach_slot (elist, ble, i) {
- 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)))
+ if (!!regexec(&ble->regex, str, 0, NULL, 0) == ble->invert)
return 1;
}
return 0;
}
-int
-_blacklist_device (const struct _vector *blist, const char * vendor,
- const char * product)
+static int
+match_reglist_device (const struct _vector *blist, const char *vendor,
+ const char * product)
{
int i;
struct blentry_device * ble;
if (!ble->vendor && !ble->product)
continue;
if ((!ble->vendor ||
- !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
+ !!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) ==
+ ble->vendor_invert) &&
(!ble->product ||
- !regexec(&ble->product_reg, product, 0, NULL, 0)))
+ !!regexec(&ble->product_reg, product, 0, NULL, 0) ==
+ ble->product_invert))
return 1;
}
return 0;
}
static int
-find_blacklist_device (const struct _vector *blist, const char * vendor,
- const char * product)
+find_blacklist_device (const struct _vector *blist, const char *vendor,
+ const char *product)
{
int i;
struct blentry_device * ble;
{
struct blentry * ble;
struct hwentry *hwe;
- char * str;
int i;
- str = STRDUP("^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]");
- if (!str)
- return 1;
- if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
- return 1;
-
- 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, "!^(sd[a-z]|dasd[a-z]|nvme[0-9])", ORIGIN_DEFAULT))
return 1;
- str = STRDUP("(SCSI_IDENT_|ID_WWN)");
- if (!str)
- return 1;
- if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT))
+ if (store_ble(conf->elist_property, "(SCSI_IDENT_|ID_WWN)", ORIGIN_DEFAULT))
return 1;
vector_foreach_slot (conf->hwtable, hwe, i) {
return 1;
ble = VECTOR_SLOT(conf->blist_device,
VECTOR_SIZE(conf->blist_device) - 1);
- if (set_ble_device(conf->blist_device,
- STRDUP(hwe->vendor),
- STRDUP(hwe->bl_product),
+ if (set_ble_device(conf->blist_device, hwe->vendor, hwe->bl_product,
ORIGIN_DEFAULT)) {
FREE(ble);
vector_del_slot(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1);
condlog(lvl, "%s: %s %s", dev, (M), (S))
static void
-log_filter (const char *dev, char *vendor, char *product, char *wwid,
- const char *env, const char *protocol, int r, int lvl)
+log_filter (const char *dev, const char *vendor, const char *product,
+ const char *wwid, const char *env, const char *protocol,
+ int r, int lvl)
{
/*
* Try to sort from most likely to least.
}
int
-filter_device (vector blist, vector elist, char * vendor, char * product,
- char * dev)
+filter_device (const struct _vector *blist, const struct _vector *elist,
+ const char *vendor, const char * product, const char *dev)
{
int r = MATCH_NOTHING;
if (vendor && product) {
- if (_blacklist_exceptions_device(elist, vendor, product))
+ if (match_reglist_device(elist, vendor, product))
r = MATCH_DEVICE_BLIST_EXCEPT;
- else if (_blacklist_device(blist, vendor, product))
+ else if (match_reglist_device(blist, vendor, product))
r = MATCH_DEVICE_BLIST;
}
}
int
-filter_devnode (vector blist, vector elist, char * dev)
+filter_devnode (const struct _vector *blist, const struct _vector *elist,
+ const char *dev)
{
int r = MATCH_NOTHING;
if (dev) {
- if (_blacklist_exceptions(elist, dev))
+ if (match_reglist(elist, dev))
r = MATCH_DEVNODE_BLIST_EXCEPT;
- else if (_blacklist(blist, dev))
+ else if (match_reglist(blist, dev))
r = MATCH_DEVNODE_BLIST;
}
}
int
-filter_wwid (vector blist, vector elist, char * wwid, char * dev)
+filter_wwid (const struct _vector *blist, const struct _vector *elist,
+ const char *wwid, const char *dev)
{
int r = MATCH_NOTHING;
if (wwid) {
- if (_blacklist_exceptions(elist, wwid))
+ if (match_reglist(elist, wwid))
r = MATCH_WWID_BLIST_EXCEPT;
- else if (_blacklist(blist, wwid))
+ else if (match_reglist(blist, wwid))
r = MATCH_WWID_BLIST;
}
}
int
-filter_protocol(vector blist, vector elist, struct path * pp)
+filter_protocol(const struct _vector *blist, const struct _vector *elist,
+ const struct path *pp)
{
char buf[PROTOCOL_BUF_SIZE];
int r = MATCH_NOTHING;
if (pp) {
snprint_path_protocol(buf, sizeof(buf), pp);
- if (_blacklist_exceptions(elist, buf))
+ if (match_reglist(elist, buf))
r = MATCH_PROTOCOL_BLIST_EXCEPT;
- else if (_blacklist(blist, buf))
+ else if (match_reglist(blist, buf))
r = MATCH_PROTOCOL_BLIST;
}
}
int
-filter_path (struct config * conf, struct path * pp)
+filter_path (const struct config *conf, const struct path *pp)
{
int r;
}
int
-filter_property(struct config *conf, struct udev_device *udev, int lvl,
- const char *uid_attribute)
+filter_property(const struct config *conf, struct udev_device *udev,
+ int lvl, const char *uid_attribute)
{
const char *devname = udev_device_get_sysname(udev);
struct udev_list_entry *list_entry;
if (check_missing_prop && !strcmp(env, uid_attribute))
uid_attr_seen = true;
- if (_blacklist_exceptions(conf->elist_property, env)) {
+ if (match_reglist(conf->elist_property, env)) {
r = MATCH_PROPERTY_BLIST_EXCEPT;
break;
}
- if (_blacklist(conf->blist_property, env)) {
+ if (match_reglist(conf->blist_property, env)) {
r = MATCH_PROPERTY_BLIST;
break;
}
struct blentry {
char * str;
regex_t regex;
+ bool invert;
int origin;
};
char * product;
regex_t vendor_reg;
regex_t product_reg;
+ bool vendor_invert;
+ bool product_invert;
int origin;
};
int setup_default_blist (struct config *);
int alloc_ble_device (vector);
-int filter_devnode (vector, vector, char *);
-int filter_wwid (vector, vector, char *, char *);
-int filter_device (vector, vector, char *, char *, char *);
-int filter_path (struct config *, struct path *);
-int filter_property(struct config *, struct udev_device *, int, const char*);
-int filter_protocol(vector, vector, struct path *);
-int store_ble (vector, char *, int);
-int set_ble_device (vector, char *, char *, int);
+int filter_devnode (const struct _vector *, const struct _vector *,
+ const char *);
+int filter_wwid (const struct _vector *, const struct _vector *,
+ const char *, const char *);
+int filter_device (const struct _vector *, const struct _vector *,
+ const char *, const char *, const char *);
+int filter_path (const struct config *, const struct path *);
+int filter_property(const struct config *, struct udev_device *,
+ int, const char*);
+int filter_protocol(const struct _vector *, const struct _vector *,
+ const struct path *);
+int store_ble (vector, const char *, int);
+int set_ble_device (vector, const char *, const char *, int);
void free_blacklist (vector);
void free_blacklist_device (vector);
void merge_blacklist(vector);
short msgtable_size;
};
-char *checker_state_names[] = {
- "wild",
- "unchecked",
- "down",
- "up",
- "shaky",
- "ghost",
- "pending",
- "timeout",
- "removed",
- "delayed",
+static const char *checker_state_names[PATH_MAX_STATE] = {
+ [PATH_WILD] = "wild",
+ [PATH_UNCHECKED] = "unchecked",
+ [PATH_DOWN] = "down",
+ [PATH_UP] = "up",
+ [PATH_SHAKY] = "shaky",
+ [PATH_GHOST] = "ghost",
+ [PATH_PENDING] = "pending",
+ [PATH_TIMEOUT] = "timeout",
+ [PATH_REMOVED] = "removed",
+ [PATH_DELAYED] = "delayed",
};
static LIST_HEAD(checkers);
const char *checker_state_name(int i)
{
+ if (i < 0 || i >= PATH_MAX_STATE) {
+ condlog (2, "invalid state index = %d", i);
+ return INVALID;
+ }
return checker_state_names[i];
}
* During this time, it is marked as "delayed"
*/
enum path_check_state {
- PATH_WILD,
+ PATH_WILD = 0,
PATH_UNCHECKED,
PATH_DOWN,
PATH_UP,
#define READSECTOR0 "readsector0"
#define CCISS_TUR "cciss_tur"
#define NONE "none"
+#define INVALID "invalid"
#define ASYNC_TIMEOUT_SEC 30
all: $(LIBS)
-libcheckdirectio.so: libsg.o directio.o
+libcheckdirectio.so: directio.o
$(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -laio
-libcheck%.so: libsg.o %.o
+libcheck%.so: %.o
$(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^
install:
clean: dep_clean
$(RM) core *.a *.o *.gz *.so
-OBJS := $(LIBS:libcheck%.so=%.o) libsg.o directio.o
+OBJS := $(LIBS:libcheck%.so=%.o)
.SECONDARY: $(OBJS)
include $(wildcard $(OBJS:.o=.d))
{
struct io_event events[128];
int i, nr, got_events = 0;
- struct timespec zero_timeout = {0};
+ struct timespec zero_timeout = { .tv_sec = 0, };
struct timespec *timep = timeout;
do {
vector_foreach_slot_backwards (hwtable, tmp, i) {
if (hwe_regmatch(tmp, vendor, product, revision))
continue;
- if (vector_alloc_slot(result) != NULL) {
+ if (vector_alloc_slot(result)) {
vector_set_slot(result, tmp);
n++;
}
return NULL;
}
-char *get_mpe_wwid(vector mptable, char *alias)
+const char *get_mpe_wwid(const struct _vector *mptable, const char *alias)
{
int i;
struct mpentry * mpe;
if (conf->config_dir)
FREE(conf->config_dir);
+ if (conf->enable_foreign)
+ FREE(conf->enable_foreign);
free_blacklist(conf->blist_devnode);
free_blacklist(conf->blist_wwid);
pthread_cleanup_pop(1);
}
+#ifdef USE_SYSTEMD
static void set_max_checkint_from_watchdog(struct config *conf)
{
-#ifdef USE_SYSTEMD
char *envp = getenv("WATCHDOG_USEC");
unsigned long checkint;
condlog(3, "enabling watchdog, interval %ld", checkint);
conf->use_watchdog = true;
}
-#endif
}
+#endif
struct config *
load_config (char * file)
/*
* fill the voids left in the config file
*/
+#ifdef USE_SYSTEMD
set_max_checkint_from_watchdog(conf);
+#endif
if (conf->max_checkint == 0) {
if (conf->checkint == CHECKINT_UNDEF)
conf->checkint = DEFAULT_CHECKINT;
CMD_ADD_WWID,
CMD_USABLE_PATHS,
CMD_DUMP_CONFIG,
+ CMD_FLUSH_ONE,
+ CMD_FLUSH_ALL,
};
enum force_reload_types {
unsigned int max_checkint;
bool use_watchdog;
int pgfailback;
- int remove;
int rr_weight;
int no_path_retry;
int user_friendly_names;
unsigned int dev_loss;
int log_checker_err;
int allow_queueing;
+ int allow_usb_devices;
int find_multipaths;
uid_t uid;
gid_t gid;
int ghost_delay;
int find_multipaths_timeout;
int marginal_pathgroups;
+ int skip_delegate;
unsigned int version[3];
unsigned int sequence_nr;
const char * vendor, const char * product, const char *revision,
vector result);
struct mpentry * find_mpe (vector mptable, char * wwid);
-char * get_mpe_wwid (vector mptable, char * alias);
+const char *get_mpe_wwid (const struct _vector *mptable, const char *alias);
struct hwentry * alloc_hwe (void);
struct mpentry * alloc_mpe (void);
struct pathgroup * pgp;
struct config *conf;
int i, n_paths, marginal_pathgroups;
+ char *save_attr;
/*
* don't bother if devmap size is unknown
return 1;
}
- /*
- * free features, selector, and hwhandler properties if they are being reused
- */
- free_multipath_attributes(mpp);
if (mpp->disable_queueing && VECTOR_SIZE(mpp->paths) != 0)
mpp->disable_queueing = 0;
select_pgfailback(conf, mpp);
select_pgpolicy(conf, mpp);
+
+ /*
+ * If setup_map() is called from e.g. from reload_map() or resize_map(),
+ * make sure that we don't corrupt attributes.
+ */
+ save_attr = steal_ptr(mpp->selector);
select_selector(conf, mpp);
+ if (!mpp->selector)
+ mpp->selector = save_attr;
+ else
+ free(save_attr);
+
select_no_path_retry(conf, mpp);
select_retain_hwhandler(conf, mpp);
+
+ save_attr = steal_ptr(mpp->features);
select_features(conf, mpp);
+ if (!mpp->features)
+ mpp->features = save_attr;
+ else
+ free(save_attr);
+
+ save_attr = steal_ptr(mpp->hwhandler);
select_hwhandler(conf, mpp);
+ if (!mpp->hwhandler)
+ mpp->hwhandler = save_attr;
+ else
+ free(save_attr);
+
select_rr_weight(conf, mpp);
select_minio(conf, mpp);
select_mode(conf, mpp);
marginal_pathgroups = conf->marginal_pathgroups;
pthread_cleanup_pop(1);
+ if (!mpp->features || !mpp->hwhandler || !mpp->selector) {
+ condlog(0, "%s: map select failed", mpp->alias);
+ return 1;
+ }
+
if (marginal_path_check_enabled(mpp))
start_io_err_stat_thread(vecs);
{
struct udev_enumerate *part_enum;
struct udev_list_entry *item;
+ const char *devtype;
part_enum = udev_enumerate_new(udev);
if (!part_enum)
if (!part)
continue;
- if (!strcmp("partition", udev_device_get_devtype(part))) {
+ devtype = udev_device_get_devtype(part);
+ if (devtype && !strcmp("partition", devtype)) {
condlog(4, "%s: triggering %s event for %s", __func__,
action, syspath);
sysfs_attr_set_value(part, "uevent", action, len);
}
static void
-select_action (struct multipath * mpp, vector curmp, int force_reload)
+select_reload_action(struct multipath *mpp, const struct multipath *cmpp,
+ const char *reason)
+{
+ struct udev_device *mpp_ud;
+ const char *env;
+
+ /*
+ * MPATH_DEVICE_READY != 1 can mean two things:
+ * (a) no usable paths
+ * (b) device was never fully processed (e.g. udev killed)
+ * If we are in this code path (startup or forced reconfigure),
+ * (b) can mean that upper layers like kpartx have never been
+ * run for this map. Thus force udev reload.
+ */
+
+ mpp_ud = get_udev_for_mpp(cmpp);
+ env = udev_device_get_property_value(mpp_ud, "MPATH_DEVICE_READY");
+ if ((!env || strcmp(env, "1")) && count_active_paths(mpp) > 0)
+ mpp->force_udev_reload = 1;
+ udev_device_unref(mpp_ud);
+ mpp->action = ACT_RELOAD;
+ condlog(3, "%s: set ACT_RELOAD (%s%s)", mpp->alias,
+ mpp->force_udev_reload ? "forced, " : "",
+ reason);
+}
+
+void select_action (struct multipath *mpp, const struct _vector *curmp,
+ int force_reload)
{
struct multipath * cmpp;
struct multipath * cmpp_by_name;
}
if (!cmpp) {
- condlog(2, "%s: remove (wwid changed)", mpp->alias);
- dm_flush_map(mpp->alias);
- strlcpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE);
- drop_multipath(curmp, cmpp_by_name->wwid, KEEP_PATHS);
+ condlog(1, "%s: can't use alias \"%s\" used by %s, falling back to WWID",
+ mpp->wwid, mpp->alias, cmpp_by_name->wwid);
+ /* We can do this because wwid wasn't found */
+ free(mpp->alias);
+ mpp->alias = strdup(mpp->wwid);
mpp->action = ACT_CREATE;
- condlog(3, "%s: set ACT_CREATE (map wwid change)",
+ condlog(3, "%s: set ACT_CREATE (map does not exist, name changed)",
mpp->alias);
return;
}
return;
}
- if (pathcount(mpp, PATH_UP) == 0) {
- mpp->action = ACT_IMPOSSIBLE;
- condlog(3, "%s: set ACT_IMPOSSIBLE (no usable path)",
- mpp->alias);
- return;
- }
if (force_reload) {
mpp->force_udev_reload = 1;
mpp->action = ACT_RELOAD;
if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
!!strstr(mpp->features, "queue_if_no_path") !=
!!strstr(cmpp->features, "queue_if_no_path")) {
- mpp->action = ACT_RELOAD;
- condlog(3, "%s: set ACT_RELOAD (no_path_retry change)",
- mpp->alias);
+ select_reload_action(mpp, cmpp, "no_path_retry change");
return;
}
if ((mpp->retain_hwhandler != RETAIN_HWHANDLER_ON ||
(strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) ||
strncmp(cmpp->hwhandler, mpp->hwhandler,
strlen(mpp->hwhandler)))) {
- mpp->action = ACT_RELOAD;
- condlog(3, "%s: set ACT_RELOAD (hwhandler change)",
- mpp->alias);
+ select_reload_action(mpp, cmpp, "hwhandler change");
return;
}
!!strstr(mpp->features, "retain_attached_hw_handler") !=
!!strstr(cmpp->features, "retain_attached_hw_handler") &&
get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) {
- mpp->action = ACT_RELOAD;
- condlog(3, "%s: set ACT_RELOAD (retain_hwhandler change)",
- mpp->alias);
+ select_reload_action(mpp, cmpp, "retain_hwhandler change");
return;
}
remove_feature(&cmpp_feat, "queue_if_no_path");
remove_feature(&cmpp_feat, "retain_attached_hw_handler");
if (strncmp(mpp_feat, cmpp_feat, PARAMS_SIZE)) {
- mpp->action = ACT_RELOAD;
- condlog(3, "%s: set ACT_RELOAD (features change)",
- mpp->alias);
+ select_reload_action(mpp, cmpp, "features change");
+ FREE(cmpp_feat);
+ FREE(mpp_feat);
+ return;
}
}
FREE(cmpp_feat);
if (!cmpp->selector || strncmp(cmpp->selector, mpp->selector,
strlen(mpp->selector))) {
- mpp->action = ACT_RELOAD;
- condlog(3, "%s: set ACT_RELOAD (selector change)",
- mpp->alias);
+ select_reload_action(mpp, cmpp, "selector change");
return;
}
if (cmpp->minio != mpp->minio) {
- mpp->action = ACT_RELOAD;
- condlog(3, "%s: set ACT_RELOAD (minio change, %u->%u)",
- mpp->alias, cmpp->minio, mpp->minio);
+ select_reload_action(mpp, cmpp, "minio change");
return;
}
if (!cmpp->pg || VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) {
- mpp->action = ACT_RELOAD;
- condlog(3, "%s: set ACT_RELOAD (path group number change)",
- mpp->alias);
+ select_reload_action(mpp, cmpp, "path group number change");
return;
}
if (pgcmp(mpp, cmpp)) {
- mpp->action = ACT_RELOAD;
- condlog(3, "%s: set ACT_RELOAD (path group topology change)",
- mpp->alias);
+ select_reload_action(mpp, cmpp, "path group topology change");
return;
}
if (cmpp->nextpg != mpp->bestpg) {
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;
+ if (mpp->action == ACT_CREATE && dm_map_present(mpp->alias)) {
+ char wwid[WWID_SIZE];
+
+ if (dm_get_uuid(mpp->alias, wwid, sizeof(wwid)) == 0) {
+ if (!strncmp(mpp->wwid, wwid, sizeof(wwid))) {
+ condlog(3, "%s: map already present",
+ mpp->alias);
+ mpp->action = ACT_RELOAD;
+ } else {
+ condlog(0, "%s: map \"%s\" already present with WWID %s, skipping",
+ mpp->wwid, mpp->alias, wwid);
+ condlog(0, "please check alias settings in config and bindings file");
+ mpp->action = ACT_REJECT;
+ }
+ }
}
switch (mpp->action) {
} else {
/* multipath daemon mode */
mpp->stat_map_loads++;
- condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias,
+ condlog(4, "%s: load table [0 %llu %s %s]", mpp->alias,
mpp->size, TGT_MPATH, params);
/*
* Required action is over, reset for the stateful daemon.
vector pathvec = vecs->pathvec;
struct config *conf;
int allow_queueing;
- uint64_t *size_mismatch_seen;
+ struct bitfield *size_mismatch_seen;
/* ignore refwwid if it's empty */
if (refwwid && !strlen(refwwid))
if (VECTOR_SIZE(pathvec) == 0)
return CP_OK;
- size_mismatch_seen = calloc((VECTOR_SIZE(pathvec) - 1) / 64 + 1,
- sizeof(uint64_t));
+ size_mismatch_seen = alloc_bitfield(VECTOR_SIZE(pathvec));
if (size_mismatch_seen == NULL)
return CP_FAIL;
}
/* 2. if path already coalesced, or seen and discarded */
- if (pp1->mpp || is_bit_set_in_array(k, size_mismatch_seen))
+ if (pp1->mpp || is_bit_set_in_bitfield(k, size_mismatch_seen))
continue;
/* 3. if path has disappeared */
if (!mpp->paths) {
condlog(0, "%s: skip coalesce (no paths)", mpp->alias);
- remove_map(mpp, vecs, 0);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
continue;
}
"Discard", pp2->dev, pp2->size,
mpp->size);
mpp->action = ACT_REJECT;
- set_bit_in_array(i, size_mismatch_seen);
+ set_bit_in_bitfield(i, size_mismatch_seen);
}
}
- verify_paths(mpp, vecs);
+ verify_paths(mpp);
params[0] = '\0';
if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
- remove_map(mpp, vecs, 0);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
continue;
}
condlog(2, "%s: %s map",
mpp->alias, (mpp->action == ACT_CREATE)?
"ignoring" : "removing");
- remove_map(mpp, vecs, 0);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
continue;
} else /* if (r == DOMAP_RETRY && !is_daemon) */ {
ret = CP_RETRY;
vector_set_slot(newmp, mpp);
}
else
- remove_map(mpp, vecs, 0);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec,
+ KEEP_VEC);
}
}
/*
vector_del_slot(newmp, i);
i--;
- remove_map(mpp, vecs, 0);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
if (dm_flush_map(alias))
condlog(2, "%s: remove failed (dead)",
return ud;
}
-/*
- * returns:
- * 0 - success
- * 1 - failure
- * 2 - blacklist
- */
-int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
- vector pathvec, char **wwid)
+static int _get_refwwid(enum mpath_cmds cmd, const char *dev,
+ enum devtypes dev_type,
+ vector pathvec, struct config *conf, char **wwid)
{
int ret = 1;
struct path * pp;
char buff[FILE_NAME_SIZE];
- char * refwwid = NULL, tmpwwid[WWID_SIZE];
+ const char *refwwid = NULL;
+ char tmpwwid[WWID_SIZE];
+ struct udev_device *udevice;
int flags = DI_SYSFS | DI_WWID;
- struct config *conf;
- int invalid = 0;
if (!wwid)
- return 1;
+ return PATHINFO_FAILED;
*wwid = NULL;
if (dev_type == DEV_NONE)
- return 1;
+ return PATHINFO_FAILED;
if (cmd != CMD_REMOVE_WWID)
flags |= DI_BLACKLIST;
- if (dev_type == DEV_DEVNODE) {
+ switch (dev_type) {
+ case DEV_DEVNODE:
if (basenamecpy(dev, buff, FILE_NAME_SIZE) == 0) {
condlog(1, "basename failed for '%s' (%s)",
dev, buff);
- return 1;
+ return PATHINFO_FAILED;
}
- pp = find_path_by_dev(pathvec, buff);
- if (!pp) {
- struct udev_device *udevice =
- get_udev_device(buff, dev_type);
-
- if (!udevice)
- return 1;
+ /* dev is used in common code below */
+ dev = buff;
+ pp = find_path_by_dev(pathvec, dev);
+ goto common;
- conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
- ret = store_pathinfo(pathvec, conf, udevice,
- flags, &pp);
- pthread_cleanup_pop(1);
- udev_device_unref(udevice);
- if (!pp) {
- if (ret == 1)
- condlog(0, "%s: can't store path info",
- dev);
- return ret;
- }
- }
- conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
- if (pp->udev && pp->uid_attribute &&
- filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
- invalid = 1;
- pthread_cleanup_pop(1);
- if (invalid)
- return 2;
-
- refwwid = pp->wwid;
- goto out;
- }
+ case DEV_DEVT:
+ pp = find_path_by_devt(pathvec, dev);
+ goto common;
- if (dev_type == DEV_DEVT) {
- strchop(dev);
- if (devt2devname(buff, FILE_NAME_SIZE, dev)) {
- condlog(0, "%s: cannot find block device\n", dev);
- return 1;
- }
- pp = find_path_by_dev(pathvec, buff);
+ case DEV_UEVENT:
+ pp = NULL;
+ /* For condlog below, dev is unused in get_udev_device() */
+ dev = "environment";
+ common:
if (!pp) {
- struct udev_device *udevice =
- get_udev_device(dev, dev_type);
+ udevice = get_udev_device(dev, dev_type);
- if (!udevice)
- return 1;
+ if (!udevice) {
+ condlog(0, "%s: cannot find block device", dev);
+ return PATHINFO_FAILED;
+ }
- conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
ret = store_pathinfo(pathvec, conf, udevice,
flags, &pp);
- pthread_cleanup_pop(1);
udev_device_unref(udevice);
if (!pp) {
- if (ret == 1)
- condlog(0, "%s can't store path info",
- buff);
+ if (ret == PATHINFO_FAILED)
+ condlog(0, "%s: can't store path info",
+ dev);
return ret;
}
}
- conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
if (pp->udev && pp->uid_attribute &&
filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
- invalid = 1;
- pthread_cleanup_pop(1);
- if (invalid)
- return 2;
+ return PATHINFO_SKIPPED;
refwwid = pp->wwid;
- goto out;
- }
-
- if (dev_type == DEV_UEVENT) {
- struct udev_device *udevice = get_udev_device(dev, dev_type);
-
- if (!udevice)
- return 1;
-
- conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
- ret = store_pathinfo(pathvec, conf, udevice,
- flags, &pp);
- pthread_cleanup_pop(1);
- udev_device_unref(udevice);
- if (!pp) {
- if (ret == 1)
- condlog(0, "%s: can't store path info", dev);
- return ret;
- }
- conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
- if (pp->udev && pp->uid_attribute &&
- filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
- invalid = 1;
- pthread_cleanup_pop(1);
- if (invalid)
- return 2;
- refwwid = pp->wwid;
- goto out;
- }
-
- if (dev_type == DEV_DEVMAP) {
+ break;
- conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
+ case DEV_DEVMAP:
if (((dm_get_uuid(dev, tmpwwid, WWID_SIZE)) == 0)
- && (strlen(tmpwwid))) {
+ && (strlen(tmpwwid)))
refwwid = tmpwwid;
- goto check;
- }
- /*
- * may be a binding
- */
- if (get_user_friendly_wwid(dev, tmpwwid,
- conf->bindings_file) == 0) {
+ /* or may be a binding */
+ else if (get_user_friendly_wwid(dev, tmpwwid,
+ conf->bindings_file) == 0)
refwwid = tmpwwid;
- goto check;
- }
- /*
- * or may be an alias
- */
- refwwid = get_mpe_wwid(conf->mptable, dev);
+ /* or may be an alias */
+ else {
+ refwwid = get_mpe_wwid(conf->mptable, dev);
- /*
- * or directly a wwid
- */
- if (!refwwid)
- refwwid = dev;
+ /* or directly a wwid */
+ if (!refwwid)
+ refwwid = dev;
+ }
-check:
if (refwwid && strlen(refwwid) &&
filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid,
NULL) > 0)
- invalid = 1;
- pthread_cleanup_pop(1);
- if (invalid)
- return 2;
+ return PATHINFO_SKIPPED;
+ break;
+ default:
+ break;
}
-out:
+
if (refwwid && strlen(refwwid)) {
*wwid = STRDUP(refwwid);
- return 0;
+ return PATHINFO_OK;
}
- return 1;
+ return PATHINFO_FAILED;
}
-int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
- int is_daemon)
-{
- char params[PARAMS_SIZE] = {0};
- struct path *pp;
- int i, r;
-
- update_mpp_paths(mpp, vecs->pathvec);
- if (refresh) {
- vector_foreach_slot (mpp->paths, pp, i) {
- struct config *conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
- r = pathinfo(pp, conf, DI_PRIO);
- pthread_cleanup_pop(1);
- if (r) {
- condlog(2, "%s: failed to refresh pathinfo",
- mpp->alias);
- return 1;
- }
- }
- }
- if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
- condlog(0, "%s: failed to setup map", mpp->alias);
- return 1;
- }
- select_action(mpp, vecs->mpvec, 1);
+/*
+ * Returns: PATHINFO_OK, PATHINFO_FAILED, or PATHINFO_SKIPPED (see pathinfo())
+ */
+int get_refwwid(enum mpath_cmds cmd, const char *dev, enum devtypes dev_type,
+ vector pathvec, char **wwid)
- r = domap(mpp, params, is_daemon);
- if (r == DOMAP_FAIL || r == DOMAP_RETRY) {
- condlog(3, "%s: domap (%u) failure "
- "for reload map", mpp->alias, r);
- return 1;
- }
+{
+ int ret;
+ struct config *conf = get_multipath_config();
- return 0;
+ pthread_cleanup_push(put_multipath_config, conf);
+ ret = _get_refwwid(cmd, dev, dev_type, pathvec, conf, wwid);
+ pthread_cleanup_pop(1);
+ return ret;
}
CP_RETRY,
};
-#define FLUSH_ONE 1
-#define FLUSH_ALL 2
-
struct vectors;
int setup_map (struct multipath * mpp, char * params, int params_size,
struct vectors *vecs );
+void select_action (struct multipath *mpp, const struct _vector *curmp,
+ int force_reload);
int domap (struct multipath * mpp, char * params, int is_daemon);
int reinstate_paths (struct multipath *mpp);
int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload, enum mpath_cmds cmd);
-int get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
+int get_refwwid (enum mpath_cmds cmd, const char *dev, enum devtypes dev_type,
vector pathvec, char **wwid);
-int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon);
struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath);
#define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF
#define DEFAULT_DELAY_CHECKS NU_NO
#define DEFAULT_ERR_CHECKS NU_NO
+/* half of minimum value for marginal_path_err_sample_time */
+#define IOTIMEOUT_SEC 60
#define DEFAULT_UEVENT_STACKSIZE 256
#define DEFAULT_RETRIGGER_DELAY 10
#define DEFAULT_RETRIGGER_TRIES 3
#define DEFAULT_FIND_MULTIPATHS_TIMEOUT -10
#define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1
#define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF
-/* Enable all foreign libraries by default */
-#define DEFAULT_ENABLE_FOREIGN ""
+/* Enable no foreign libraries by default */
+#define DEFAULT_ENABLE_FOREIGN "NONE"
#define CHECKINT_UNDEF UINT_MAX
#define DEFAULT_CHECKINT 5
+#define DEV_LOSS_TMO_UNSET 0U
#define MAX_DEV_LOSS_TMO UINT_MAX
#define DEFAULT_PIDFILE "/" RUN_DIR "/multipathd.pid"
#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
+#include <syslog.h>
#include <sys/sysmacros.h>
#include <linux/dm-ioctl.h>
#define MAX_WAIT 5
#define LOOPS_PER_SEC 5
+static pthread_once_t dm_initialized = PTHREAD_ONCE_INIT;
+
static int dm_conf_verbosity;
#ifdef LIBDM_API_DEFERRED
dm_write_log (int level, const char *file, int line, const char *f, ...)
{
va_list ap;
- int thres;
- if (level > 6)
- level = 6;
+ /*
+ * libdm uses the same log levels as syslog,
+ * except that EMERG/ALERT are not used
+ */
+ if (level > LOG_DEBUG)
+ level = LOG_DEBUG;
- thres = dm_conf_verbosity;
- if (thres <= 3 || level > thres)
+ if (level > dm_conf_verbosity)
return;
va_start(ap, f);
vfprintf(stderr, f, ap);
fprintf(stderr, "\n");
} else {
- condlog(level, "libdevmapper: %s(%i): ", file, line);
- log_safe(level + 3, f, ap);
+ condlog(level >= LOG_ERR ? level - LOG_ERR : 0,
+ "libdevmapper: %s(%i): ", file, line);
+ log_safe(level, f, ap);
}
va_end(ap);
void dm_init(int v)
{
- dm_conf_verbosity = v;
+ /*
+ * This maps libdm's standard loglevel _LOG_WARN (= 4), which is rather
+ * quiet in practice, to multipathd's default verbosity 2
+ */
+ dm_conf_verbosity = v + 2;
dm_log_init(&dm_write_log);
- dm_log_init_verbose(v + 3);
}
static int
{
char version[64];
int v[3];
-#if defined(LIBDM_API_DEFERRED)
+#if defined(LIBDM_API_HOLD_CONTROL)
+ int minv[3] = {1, 2, 111};
+#elif defined(LIBDM_API_GET_ERRNO)
+ int minv[3] = {1, 2, 99};
+#elif defined(LIBDM_API_DEFERRED)
int minv[3] = {1, 2, 89};
#elif defined(DM_SUBSYSTEM_UDEV_FLAG0)
int minv[3] = {1, 2, 82};
dm_task_no_open_count(dmt);
if (!dm_task_run(dmt)) {
+ dm_log_error(2, DM_DEVICE_LIST_VERSIONS, dmt);
condlog(0, "Can not communicate with kernel DM");
goto out;
}
return 1;
}
-static int dm_prereq(unsigned int *v)
+int dm_prereq(unsigned int *v)
{
if (dm_lib_prereq())
return 1;
libmp_dm_udev_sync = !!on;
}
-void libmp_dm_init(void)
+static void libmp_dm_init(void)
{
struct config *conf;
int verbosity;
memcpy(conf->version, version, sizeof(version));
put_multipath_config(conf);
dm_init(verbosity);
+#ifdef LIBDM_API_HOLD_CONTROL
+ dm_hold_control_dev(1);
+#endif
dm_udev_set_sync_support(libmp_dm_udev_sync);
}
+static void _do_skip_libmp_dm_init(void)
+{
+}
+
+void skip_libmp_dm_init(void)
+{
+ pthread_once(&dm_initialized, _do_skip_libmp_dm_init);
+}
+
struct dm_task*
libmp_dm_task_create(int task)
{
- static pthread_once_t dm_initialized = PTHREAD_ONCE_INIT;
-
pthread_once(&dm_initialized, libmp_dm_init);
return dm_task_create(task);
}
goto out;
r = dm_task_run (dmt);
+ if (!r)
+ dm_log_error(2, task, dmt);
if (udev_wait_flag)
dm_udev_wait(cookie);
char *prefixed_uuid = NULL;
uint32_t cookie = 0;
+ if (task == DM_DEVICE_CREATE && strlen(mpp->wwid) == 0) {
+ condlog(1, "%s: refusing to create map with empty WWID",
+ mpp->alias);
+ return 0;
+ }
+
/* Need to add this here to allow 0 to be passed in udev_flags */
udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
dm_task_set_ro(dmt);
if (task == DM_DEVICE_CREATE) {
- if (strlen(mpp->wwid) > 0) {
- prefixed_uuid = MALLOC(UUID_PREFIX_LEN +
- strlen(mpp->wwid) + 1);
- if (!prefixed_uuid) {
- condlog(0, "cannot create prefixed uuid : %s",
- strerror(errno));
- goto addout;
- }
- sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
- if (!dm_task_set_uuid(dmt, prefixed_uuid))
- goto freeout;
+ prefixed_uuid = MALLOC(UUID_PREFIX_LEN +
+ strlen(mpp->wwid) + 1);
+ if (!prefixed_uuid) {
+ condlog(0, "cannot create prefixed uuid : %s",
+ strerror(errno));
+ goto addout;
}
+ sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
+ if (!dm_task_set_uuid(dmt, prefixed_uuid))
+ goto freeout;
dm_task_skip_lockfs(dmt);
#ifdef LIBDM_API_FLUSH
dm_task_no_flush(dmt);
if (mpp->attribute_flags & (1 << ATTR_GID) &&
!dm_task_set_gid(dmt, mpp->gid))
goto freeout;
- condlog(4, "%s: %s [0 %llu %s %s]", mpp->alias,
+ condlog(2, "%s: %s [0 %llu %s %s]", mpp->alias,
task == DM_DEVICE_RELOAD ? "reload" : "addmap", mpp->size,
target, params);
goto freeout;
r = dm_task_run (dmt);
+ if (!r)
+ dm_log_error(2, task, dmt);
if (task == DM_DEVICE_CREATE)
dm_udev_wait(cookie);
/* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */
return (mpp->skip_kpartx == SKIP_KPARTX_ON ?
MPATH_UDEV_NO_KPARTX_FLAG : 0) |
- ((count_active_paths(mpp) == 0 || mpp->ghost_delay_tick > 0) ?
+ ((count_active_pending_paths(mpp) == 0 ||
+ mpp->ghost_delay_tick > 0) ?
MPATH_UDEV_NO_PATHS_FLAG : 0) |
(reload && !mpp->force_udev_reload ?
MPATH_UDEV_RELOAD_FLAG : 0);
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_INFO, dmt);
goto out;
+ }
if (!dm_task_get_info(dmt, info))
goto out;
int dm_get_map(const char *name, unsigned long long *size, char *outparams)
{
- int r = 1;
+ int r = DMP_ERR;
struct dm_task *dmt;
uint64_t start, length;
char *target_type = NULL;
char *params = NULL;
if (!(dmt = libmp_dm_task_create(DM_DEVICE_TABLE)))
- return 1;
+ return r;
if (!dm_task_set_name(dmt, name))
goto out;
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ errno = 0;
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_TABLE, dmt);
+ if (dm_task_get_errno(dmt) == ENXIO)
+ r = DMP_NOT_FOUND;
goto out;
+ }
+ r = DMP_NOT_FOUND;
/* Fetch 1st target */
- dm_get_next_target(dmt, NULL, &start, &length,
- &target_type, ¶ms);
+ if (dm_get_next_target(dmt, NULL, &start, &length,
+ &target_type, ¶ms) != NULL)
+ /* more than one target */
+ goto out;
if (size)
*size = length;
if (!outparams) {
- r = 0;
+ r = DMP_OK;
goto out;
}
if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
- r = 0;
+ r = DMP_OK;
out:
dm_task_destroy(dmt);
return r;
if (!dm_task_set_name (dmt, name))
goto uuidout;
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_INFO, dmt);
goto uuidout;
+ }
uuidtmp = dm_task_get_uuid(dmt);
if (uuidtmp)
int dm_get_status(const char *name, char *outstatus)
{
- int r = 1;
+ int r = DMP_ERR;
struct dm_task *dmt;
uint64_t start, length;
char *target_type = NULL;
char *status = NULL;
if (!(dmt = libmp_dm_task_create(DM_DEVICE_STATUS)))
- return 1;
+ return r;
if (!dm_task_set_name(dmt, name))
goto out;
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ errno = 0;
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_STATUS, dmt);
+ if (dm_task_get_errno(dmt) == ENXIO)
+ r = DMP_NOT_FOUND;
goto out;
+ }
+ r = DMP_NOT_FOUND;
/* Fetch 1st target */
- dm_get_next_target(dmt, NULL, &start, &length,
- &target_type, &status);
+ if (dm_get_next_target(dmt, NULL, &start, &length,
+ &target_type, &status) != NULL)
+ goto out;
+
+ if (!target_type || strcmp(target_type, TGT_MPATH) != 0)
+ goto out;
+
if (!status) {
condlog(2, "get null status.");
goto out;
}
if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE)
- r = 0;
+ r = DMP_OK;
out:
- if (r)
+ if (r != DMP_OK)
condlog(0, "%s: error getting map status string", name);
dm_task_destroy(dmt);
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_TABLE, dmt);
goto out;
+ }
/* Fetch 1st target */
if (dm_get_next_target(dmt, NULL, &start, &length,
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_TABLE, dmt);
goto out_task;
+ }
if (!dm_task_get_info(dmt, &info))
goto out_task;
return r;
}
+/*
+ * Return
+ * 1 : map with uuid exists
+ * 0 : map with uuid doesn't exist
+ * -1 : error
+ */
+int
+dm_map_present_by_uuid(const char *uuid)
+{
+ struct dm_task *dmt;
+ struct dm_info info;
+ char prefixed_uuid[WWID_SIZE + UUID_PREFIX_LEN];
+ int r = -1;
+
+ if (!uuid || uuid[0] == '\0')
+ return 0;
+
+ if (safe_sprintf(prefixed_uuid, UUID_PREFIX "%s", uuid))
+ goto out;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+ goto out;
+
+ dm_task_no_open_count(dmt);
+
+ if (!dm_task_set_uuid(dmt, prefixed_uuid))
+ goto out_task;
+
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_INFO, dmt);
+ goto out_task;
+ }
+
+ if (!dm_task_get_info(dmt, &info))
+ goto out_task;
+
+ r = !!info.exists;
+
+out_task:
+ dm_task_destroy(dmt);
+out:
+ if (r < 0)
+ condlog(3, "%s: dm command failed in %s: %s", uuid,
+ __FUNCTION__, strerror(errno));
+ return r;
+}
+
static int
dm_dev_t (const char * mapname, char * dev_t, int len)
{
if (!dm_task_set_name(dmt, mapname))
goto out;
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_INFO, dmt);
goto out;
+ }
if (!dm_task_get_info(dmt, &info))
goto out;
return 1;
if (need_suspend &&
- !dm_get_map(mapname, &mapsize, params) &&
+ dm_get_map(mapname, &mapsize, params) == DMP_OK &&
strstr(params, "queue_if_no_path")) {
if (!dm_queue_if_no_path(mapname, 0))
queue_if_no_path = 1;
#endif
-int dm_flush_maps (int retries)
+int dm_flush_maps (int need_suspend, int retries)
{
- int r = 0;
+ int r = 1;
struct dm_task *dmt;
struct dm_names *names;
unsigned next = 0;
if (!(dmt = libmp_dm_task_create (DM_DEVICE_LIST)))
- return 0;
+ return r;
dm_task_no_open_count(dmt);
- if (!dm_task_run (dmt))
+ if (!dm_task_run (dmt)) {
+ dm_log_error(3, DM_DEVICE_LIST, dmt);
goto out;
+ }
if (!(names = dm_task_get_names (dmt)))
goto out;
+ r = 0;
if (!names->dev)
goto out;
do {
- r |= dm_suspend_and_flush_map(names->name, retries);
+ if (need_suspend)
+ r |= dm_suspend_and_flush_map(names->name, retries);
+ else
+ r |= dm_flush_map(names->name);
next = names->next;
names = (void *) names + next;
} while (next);
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(2, DM_DEVICE_TARGET_MSG, dmt);
goto out;
+ }
r = 0;
out:
if (!mpp->alias)
goto out;
- if (dm_get_map(name, &mpp->size, NULL))
+ if (dm_get_map(name, &mpp->size, NULL) != DMP_OK)
goto out;
dm_get_uuid(name, mpp->wwid, WWID_SIZE);
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_LIST, dmt);
goto out;
+ }
if (!(names = dm_task_get_names(dmt)))
goto out;
if (!mpp)
goto out;
- if (!vector_alloc_slot(mp))
+ if (!vector_alloc_slot(mp)) {
+ free_multipath(mpp, KEEP_PATHS);
goto out;
+ }
vector_set_slot(mp, mpp);
mpp = NULL;
}
if (!r) {
+ dm_log_error(2, DM_DEVICE_STATUS, dmt);
condlog(0, "%i:%i: timeout fetching map name", major, minor);
goto bad;
}
map = dm_task_get_name(dmt);
if (map && strlen(map))
- response = STRDUP((const char *)dm_task_get_name(dmt));
+ response = STRDUP((const char *)map);
dm_task_destroy(dmt);
return response;
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_LIST, dmt);
goto out;
+ }
if (!(names = dm_task_get_names(dmt)))
goto out;
/*
* and we can fetch the map table from the kernel
*/
- !dm_get_map(names->name, &size, ¶ms[0]) &&
+ dm_get_map(names->name, &size, ¶ms[0]) == DMP_OK &&
/*
* and the table maps over the multipath map
return 1;
if (do_get_info(mapname, *dmi) != 0) {
- memset(*dmi, 0, sizeof(struct dm_info));
FREE(*dmi);
*dmi = NULL;
return 1;
if (!dm_task_set_cookie(dmt, &cookie, udev_flags))
goto out;
r = dm_task_run(dmt);
+ if (!r)
+ dm_log_error(2, DM_DEVICE_RENAME, dmt);
dm_udev_wait(cookie);
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_TABLE, dmt);
goto out;
+ }
if (!(reload_dmt = libmp_dm_task_create(DM_DEVICE_RELOAD)))
goto out;
if (!dm_task_set_name(reload_dmt, name))
dm_task_no_open_count(reload_dmt);
if (!dm_task_run(reload_dmt)) {
+ dm_log_error(3, DM_DEVICE_RELOAD, reload_dmt);
condlog(3, "%s: failed to reassign targets", name);
goto out_reload;
}
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_DEPS, dmt);
goto out;
+ }
if (!dm_task_get_info(dmt, &info))
goto out;
}
r = dm_task_run(dmt);
+ if (!r)
+ dm_log_error(3, DM_DEVICE_SET_GEOMETRY, dmt);
out:
dm_task_destroy(dmt);
#define UUID_PREFIX "mpath-"
#define UUID_PREFIX_LEN (sizeof(UUID_PREFIX) - 1)
+enum {
+ DMP_ERR,
+ DMP_OK,
+ DMP_NOT_FOUND,
+};
+
void dm_init(int verbosity);
-void libmp_dm_init(void);
+int dm_prereq(unsigned int *v);
+void skip_libmp_dm_init(void);
void libmp_udev_set_sync_support(int on);
struct dm_task *libmp_dm_task_create(int task);
int dm_drv_version (unsigned int * version);
int dm_addmap_create (struct multipath *mpp, char *params);
int dm_addmap_reload (struct multipath *mpp, char *params, int flush);
int dm_map_present (const char *);
+int dm_map_present_by_uuid(const char *uuid);
int dm_get_map(const char *, unsigned long long *, char *);
int dm_get_status(const char *, char *);
int dm_type(const char *, char *);
#define dm_suspend_and_flush_map(mapname, retries) \
_dm_flush_map(mapname, 1, 0, 1, retries)
int dm_cancel_deferred_remove(struct multipath *mpp);
-int dm_flush_maps (int retries);
+int dm_flush_maps (int need_suspend, int retries);
int dm_fail_path(const char * mapname, char * path);
int dm_reinstate_path(const char * mapname, char * path);
int dm_queue_if_no_path(const char *mapname, int enable);
((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2])) \
)
+#ifndef LIBDM_API_GET_ERRNO
+#include <errno.h>
+#define dm_task_get_errno(x) errno
+#endif
+
+#define dm_log_error(lvl, cmd, dmt) \
+ condlog(lvl, "%s: libdm task=%d error: %s", __func__, \
+ cmd, strerror(dm_task_get_errno(dmt))) \
+
#endif /* _DEVMAPPER_H */
set_uint(vector strvec, void *ptr)
{
unsigned int *uint_ptr = (unsigned int *)ptr;
- char *buff, *eptr;
- long res;
+ char *buff, *eptr, *p;
+ unsigned long res;
int rc;
buff = set_value(strvec);
if (!buff)
return 1;
- res = strtol(buff, &eptr, 10);
+ p = buff;
+ while (isspace(*p))
+ p++;
+ res = strtoul(p, &eptr, 10);
if (eptr > buff)
while (isspace(*eptr))
eptr++;
- if (*buff == '\0' || *eptr != '\0' || res < 0 || res > UINT_MAX) {
+ if (*buff == '\0' || *eptr != '\0' || !isdigit(*p) || res > UINT_MAX) {
condlog(1, "%s: invalid value for %s: \"%s\"",
__func__, (char*)VECTOR_SLOT(strvec, 0), buff);
rc = 1;
declare_def_handler(checker_timeout, set_int)
declare_def_snprint(checker_timeout, print_nonzero)
+declare_def_handler(allow_usb_devices, set_yes_no)
+declare_def_snprint(allow_usb_devices, print_yes_no)
+
declare_def_handler(flush_on_last_del, set_yes_no_undef)
declare_def_snprint_defint(flush_on_last_del, print_yes_no_undef, DEFAULT_FLUSH)
declare_ovr_handler(flush_on_last_del, set_yes_no_undef)
if (!strcmp(buff, "infinity"))
*uint_ptr = MAX_DEV_LOSS_TMO;
else if (sscanf(buff, "%u", uint_ptr) != 1)
- *uint_ptr = 0;
+ *uint_ptr = DEV_LOSS_TMO_UNSET;
FREE(buff);
return 0;
int
print_dev_loss(char * buff, int len, unsigned long v)
{
- if (!v)
+ if (v == DEV_LOSS_TMO_UNSET)
return 0;
if (v >= MAX_DEV_LOSS_TMO)
return snprintf(buff, len, "\"infinity\"");
static int \
ble_ ## option ## _handler (struct config *conf, vector strvec) \
{ \
- char * buff; \
+ char *buff; \
+ int rc; \
\
if (!conf->option) \
return 1; \
if (!buff) \
return 1; \
\
- return store_ble(conf->option, buff, ORIGIN_CONFIG); \
+ rc = store_ble(conf->option, buff, ORIGIN_CONFIG); \
+ free(buff); \
+ return rc; \
}
#define declare_ble_device_handler(name, option, vend, prod) \
ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec) \
{ \
char * buff; \
+ int rc; \
\
if (!conf->option) \
return 1; \
if (!buff) \
return 1; \
\
- return set_ble_device(conf->option, vend, prod, ORIGIN_CONFIG); \
+ rc = set_ble_device(conf->option, vend, prod, ORIGIN_CONFIG); \
+ free(buff); \
+ return rc; \
}
declare_ble_handler(blist_devnode)
install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry);
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("allow_usb_devices", &def_allow_usb_devices_handler, &snprint_def_allow_usb_devices);
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_user_friendly_names_handler, &snprint_def_user_friendly_names);
#include "unaligned.h"
#include "prioritizers/alua_rtpg.h"
#include "foreign.h"
+#include "configure.h"
+#include "print.h"
struct vpd_vendor_page vpd_vendor_pages[VPD_VP_ARRAY_SIZE] = {
[VPD_VP_UNDEF] = { 0x00, "undef" },
if (safe_sprintf(pp->dev, "%s", devname)) {
condlog(0, "pp->dev too small");
+ err = 1;
} else {
pp->udev = udev_device_ref(udevice);
err = pathinfo(pp, conf, flag | DI_BLACKLIST);
path_discover (vector pathvec, struct config * conf,
struct udev_device *udevice, int flag)
{
- struct path * pp;
- const char * devname;
-
- devname = udev_device_get_sysname(udevice);
- if (!devname)
- return PATHINFO_FAILED;
-
- pp = find_path_by_dev(pathvec, devname);
- if (!pp) {
- char devt[BLK_DEV_SIZE];
- dev_t devnum = udev_device_get_devnum(udevice);
+ struct path *pp;
+ char devt[BLK_DEV_SIZE];
+ dev_t devnum = udev_device_get_devnum(udevice);
- snprintf(devt, BLK_DEV_SIZE, "%d:%d",
- major(devnum), minor(devnum));
- pp = find_path_by_devt(pathvec, devt);
- if (!pp)
- return store_pathinfo(pathvec, conf,
- udevice, flag | DI_BLACKLIST,
- NULL);
- }
- return pathinfo(pp, conf, flag);
+ snprintf(devt, BLK_DEV_SIZE, "%d:%d",
+ major(devnum), minor(devnum));
+ pp = find_path_by_devt(pathvec, devt);
+ if (!pp)
+ return store_pathinfo(pathvec, conf,
+ udevice, flag | DI_BLACKLIST,
+ NULL);
+ else
+ /*
+ * Don't use DI_BLACKLIST on paths already in pathvec. We rely
+ * on the caller to pre-populate the pathvec with valid paths
+ * only.
+ */
+ return pathinfo(pp, conf, flag);
}
static void cleanup_udev_enumerate_ptr(void *arg)
struct udev_device *parent, *tgtdev;
int host, channel, tgtid = -1;
- parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_device");
+ if (!pp->udev)
+ return 1;
+ parent = udev_device_get_parent_with_subsystem_devtype(pp->udev,
+ "scsi", "scsi_device");
if (!parent)
return 1;
/* Check for SAS */
tgtdev = udev_device_get_parent(parent);
while (tgtdev) {
tgtname = udev_device_get_sysname(tgtdev);
- if (sscanf(tgtname, "end_device-%d:%d",
+ if (tgtname && sscanf(tgtname, "end_device-%d:%d",
&host, &tgtid) == 2)
break;
tgtdev = udev_device_get_parent(tgtdev);
while (tgtdev) {
value = udev_device_get_subsystem(tgtdev);
if (value && !strcmp(value, "usb")) {
- pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
+ pp->sg_id.proto_id = SCSI_PROTOCOL_USB;
tgtname = udev_device_get_sysname(tgtdev);
strlcpy(node, tgtname, NODE_NAME_SIZE);
- condlog(3, "%s: skip USB device %s", pp->dev, node);
- return 1;
+ return 0;
}
tgtdev = udev_device_get_parent(tgtdev);
}
/* Check for FibreChannel */
tgtdev = udev_device_get_parent(parent);
value = udev_device_get_sysname(tgtdev);
- if (sscanf(value, "rport-%d:%d-%d",
+ if (value && sscanf(value, "rport-%d:%d-%d",
&host, &channel, &tgtid) == 3) {
tgtdev = udev_device_new_from_subsystem_sysname(udev,
"fc_remote_ports", value);
if (tgtdev) {
- condlog(3, "SCSI target %d:%d:%d -> "
+ condlog(4, "SCSI target %d:%d:%d -> "
"FC rport %d:%d-%d",
pp->sg_id.host_no, pp->sg_id.channel,
pp->sg_id.scsi_id, host, channel,
*/
value = udev_device_get_sysname(parent);
+ if (!value) {
+ udev_device_unref(hostdev);
+ return 1;
+ }
+
strncpy(pci_name, value, SLOT_NAME_SIZE);
udev_device_unref(hostdev);
return 0;
struct udev_device *rport_dev = NULL;
char value[16], *eptr;
char rport_id[32];
- unsigned long long tmo = 0;
+ unsigned int tmo;
int ret;
sprintf(rport_id, "rport-%d:%d-%d",
"error %d", rport_id, -ret);
goto out;
}
- tmo = strtoull(value, &eptr, 0);
- if (value == eptr || tmo == ULLONG_MAX) {
+ tmo = strtoul(value, &eptr, 0);
+ if (value == eptr) {
condlog(0, "%s: Cannot parse dev_loss_tmo "
"attribute '%s'", rport_id, value);
goto out;
}
} else if (mpp->dev_loss > DEFAULT_DEV_LOSS_TMO &&
mpp->no_path_retry != NO_PATH_RETRY_QUEUE) {
- condlog(3, "%s: limiting dev_loss_tmo to %d, since "
+ condlog(2, "%s: limiting dev_loss_tmo to %d, since "
"fast_io_fail is not set",
rport_id, DEFAULT_DEV_LOSS_TMO);
mpp->dev_loss = DEFAULT_DEV_LOSS_TMO;
rport_id, value, -ret);
}
}
- if (mpp->dev_loss > 0) {
+ if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
snprintf(value, 16, "%u", mpp->dev_loss);
ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
value, strlen(value));
condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
pp->sg_id.channel, pp->sg_id.scsi_id, session_id);
- if (mpp->dev_loss) {
+ if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
condlog(3, "%s: ignoring dev_loss_tmo on iSCSI", pp->dev);
}
if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id);
- if (mpp->dev_loss) {
+ if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
snprintf(value, 11, "%u", mpp->dev_loss);
if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
value, strlen(value)) <= 0)
struct path *pp;
int i;
unsigned int dev_loss_tmo = mpp->dev_loss;
+ struct path *err_path = NULL;
if (mpp->no_path_retry > 0) {
uint64_t no_path_retry_tmo =
no_path_retry_tmo = MAX_DEV_LOSS_TMO;
if (no_path_retry_tmo > dev_loss_tmo)
dev_loss_tmo = no_path_retry_tmo;
- condlog(3, "%s: update dev_loss_tmo to %u",
- mpp->alias, dev_loss_tmo);
} else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) {
dev_loss_tmo = MAX_DEV_LOSS_TMO;
- condlog(3, "%s: update dev_loss_tmo to %u",
- mpp->alias, dev_loss_tmo);
}
- mpp->dev_loss = dev_loss_tmo;
- if (mpp->dev_loss && mpp->fast_io_fail > 0 &&
+ if (mpp->dev_loss != DEV_LOSS_TMO_UNSET &&
+ mpp->dev_loss != dev_loss_tmo) {
+ condlog(2, "%s: Using dev_loss_tmo=%u instead of %u because of no_path_retry setting",
+ mpp->alias, dev_loss_tmo, mpp->dev_loss);
+ mpp->dev_loss = dev_loss_tmo;
+ }
+ if (mpp->dev_loss != DEV_LOSS_TMO_UNSET &&
+ mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
(unsigned int)mpp->fast_io_fail >= mpp->dev_loss) {
condlog(3, "%s: turning off fast_io_fail (%d is not smaller than dev_loss_tmo)",
mpp->alias, mpp->fast_io_fail);
mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF;
}
- if (!mpp->dev_loss && mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+ if (mpp->dev_loss == DEV_LOSS_TMO_UNSET &&
+ mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
return 0;
vector_foreach_slot(mpp->paths, pp, i) {
- if (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP)
+ if (pp->bus != SYSFS_BUS_SCSI) {
+ if (!err_path)
+ err_path = pp;
+ continue;
+ }
+
+ switch (pp->sg_id.proto_id) {
+ case SCSI_PROTOCOL_FCP:
sysfs_set_rport_tmo(mpp, pp);
- if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI)
+ continue;
+ case SCSI_PROTOCOL_ISCSI:
sysfs_set_session_tmo(mpp, pp);
- if (pp->sg_id.proto_id == SCSI_PROTOCOL_SAS)
+ continue;
+ case SCSI_PROTOCOL_SAS:
sysfs_set_nexus_loss_tmo(mpp, pp);
+ continue;
+ default:
+ if (!err_path)
+ err_path = pp;
+ }
+ }
+
+ if (err_path) {
+ char proto_buf[32];
+
+ snprint_path_protocol(proto_buf, sizeof(proto_buf), err_path);
+ condlog(2, "%s: setting dev_loss_tmo is unsupported for protocol %s",
+ mpp->alias, proto_buf);
}
return 0;
}
int tpgs;
unsigned int timeout;
+
+ if (pp->bus != SYSFS_BUS_SCSI) {
+ pp->tpgs = TPGS_NONE;
+ return;
+ }
+
if (sysfs_get_timeout(pp, &timeout) <= 0)
timeout = DEF_TIMEOUT;
}
if (len >= out_len) {
- condlog(2, "vpd pg80 overflow, %lu/%lu bytes required",
+ condlog(2, "vpd pg80 overflow, %zu/%zu bytes required",
len + 1, out_len);
len = out_len - 1;
}
len = sprintf(out, "%d", vpd_type);
if (2 * vpd_len >= out_len - len) {
- condlog(1, "%s: WWID overflow, type %d, %lu/%lu bytes required",
+ condlog(1, "%s: WWID overflow, type %d, %zu/%zu bytes required",
__func__, vpd_type,
2 * vpd_len + len + 1, out_len);
vpd_len = (out_len - len - 1) / 2;
len += sprintf(out + len,
"%02x", vpd[i]);
} else if (vpd_type == 0x8 && vpd_len < 4) {
- condlog(1, "%s: VPD length %lu too small for designator type 8",
+ condlog(1, "%s: VPD length %zu too small for designator type 8",
__func__, vpd_len);
return -EINVAL;
} else if (vpd_type == 0x8) {
while (len > 2 && vpd[len - 2] == '\0')
--len;
if (len > out_len - 1) {
- condlog(1, "%s: WWID overflow, type 8/%c, %lu/%lu bytes required",
+ condlog(1, "%s: WWID overflow, type 8/%c, %zu/%zu bytes required",
__func__, out[0], len + 1, out_len);
len = out_len - 1;
}
while ((p = memchr(vpd, ' ', vpd_len))) {
p_len = p - vpd;
if (len + p_len > out_len - 1) {
- condlog(1, "%s: WWID overflow, type 1, %lu/%lu bytes required",
+ condlog(1, "%s: WWID overflow, type 1, %zu/%zu bytes required",
__func__, len + p_len, out_len);
p_len = out_len - len - 1;
}
p_len = vpd_len;
if (p_len > 0 && len < out_len - 1) {
if (len + p_len > out_len - 1) {
- condlog(1, "%s: WWID overflow, type 1, %lu/%lu bytes required",
+ condlog(1, "%s: WWID overflow, type 1, %zu/%zu bytes required",
__func__, len + p_len + 1, out_len);
p_len = out_len - len - 1;
}
memset(out, 0x0, out_len);
if (in_len <= 4 || (in[4] > 3 && in_len < 44)) {
- condlog(3, "HP/3PAR vendor specific VPD page length too short: %lu", in_len);
+ condlog(3, "HP/3PAR vendor specific VPD page length too short: %zu", in_len);
return -EINVAL;
}
if (in[4] <= 3) /* revision must be > 3 to have Vomlume Name */
return -ENODATA;
len = get_unaligned_be32(&in[40]);
if (len > out_len || len + 44 > in_len) {
- condlog(3, "HP/3PAR vendor specific Volume name too long: %lu",
+ condlog(3, "HP/3PAR vendor specific Volume name too long: %zu",
len);
return -EINVAL;
}
}
static int
-scsi_sysfs_pathinfo (struct path * pp, vector hwtable)
+scsi_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
{
struct udev_device *parent;
const char *attr_path = NULL;
}
static int
-nvme_sysfs_pathinfo (struct path * pp, vector hwtable)
+nvme_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
{
struct udev_device *parent;
const char *attr_path = NULL;
const char *attr;
- attr_path = udev_device_get_sysname(pp->udev);
+ if (pp->udev)
+ attr_path = udev_device_get_sysname(pp->udev);
if (!attr_path)
return PATHINFO_FAILED;
}
static int
-ccw_sysfs_pathinfo (struct path * pp, vector hwtable)
+ccw_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
{
struct udev_device *parent;
char attr_buff[NAME_SIZE];
* host / bus / target / lun
*/
attr_path = udev_device_get_sysname(parent);
+ if (!attr_path)
+ return PATHINFO_FAILED;
pp->sg_id.lun = 0;
if (sscanf(attr_path, "%i.%i.%x",
&pp->sg_id.host_no,
}
static int
-cciss_sysfs_pathinfo (struct path * pp, vector hwtable)
+cciss_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
{
const char * attr_path = NULL;
struct udev_device *parent;
return PATH_DOWN;
}
-int
-sysfs_pathinfo(struct path * pp, vector hwtable)
+static int
+sysfs_pathinfo(struct path *pp, const struct _vector *hwtable)
{
int r = common_sysfs_pathinfo(pp);
} else {
condlog(3, "%s: no %s attribute", pp->dev,
uid_attribute);
- len = -EINVAL;
+ len = -ENODATA;
}
return len;
}
}
} else if (pp->bus == SYSFS_BUS_NVME) {
char value[256];
+
+ if (!pp->udev)
+ return -1;
len = sysfs_attr_get_value(pp->udev, "wwid", value,
sizeof(value));
if (len <= 0)
if (udev_available) {
len = get_udev_uid(pp, pp->uid_attribute, udev);
- if (len <= 0)
- condlog(1,
- "%s: failed to get udev uid: %s",
- pp->dev, strerror(-len));
- else
- origin = "udev";
+ origin = "udev";
+ if (len == 0)
+ condlog(1, "%s: empty udev uid", pp->dev);
}
if ((!udev_available || (len <= 0 && allow_fallback))
&& has_uid_fallback(pp)) {
if (!pp || !conf)
return PATHINFO_FAILED;
+ /* Treat removed paths as if they didn't exist */
+ if (pp->initialized == INIT_REMOVED)
+ return PATHINFO_FAILED;
+
/*
* For behavior backward-compatibility with multipathd,
* the blacklisting by filter_property|devnode() is not
return PATHINFO_SKIPPED;
}
- if (filter_devnode(conf->blist_devnode,
+ if (strlen(pp->dev) != 0 && filter_devnode(conf->blist_devnode,
conf->elist_devnode,
pp->dev) > 0)
return PATHINFO_SKIPPED;
if (rc != PATHINFO_OK)
return rc;
+
+ if (pp->bus == SYSFS_BUS_SCSI &&
+ pp->sg_id.proto_id == SCSI_PROTOCOL_USB &&
+ !conf->allow_usb_devices) {
+ condlog(3, "%s: skip USB device %s", pp->dev,
+ pp->tgt_node_name);
+ return PATHINFO_SKIPPED;
+ }
}
if (mask & DI_BLACKLIST && mask & DI_SYSFS) {
pp->fd = open(udev_device_get_devnode(pp->udev), O_RDONLY);
if (pp->fd < 0) {
- condlog(4, "Couldn't open node for %s: %s",
+ condlog(4, "Couldn't open device node for %s: %s",
pp->dev, strerror(errno));
goto blank;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
#include "checkers.h"
#include "vector.h"
#define WORD_SIZE 64
static int
-merge_words(char **dst, char *word)
+merge_words(char **dst, const char *word)
{
char * p = *dst;
int len, dstlen;
int i, j;
int minio;
int nr_priority_groups, initial_pg_nr;
- char * p, * f;
+ char * p;
const char *const end = params + len;
char no_path_retry[] = "queue_if_no_path";
char retain_hwhandler[] = "retain_attached_hw_handler";
get_linux_version_code() < KERNEL_VERSION(4, 3, 0))
add_feature(&mp->features, retain_hwhandler);
- f = STRDUP(mp->features);
-
- APPEND(p, end, "%s %s %i %i", f, mp->hwhandler, nr_priority_groups,
- initial_pg_nr);
+ /* mp->features must not be NULL */
+ APPEND(p, end, "%s %s %i %i", mp->features, mp->hwhandler,
+ nr_priority_groups, initial_pg_nr);
vector_foreach_slot (mp->pg, pgp, i) {
pgp = VECTOR_SLOT(mp->pg, i);
}
}
- FREE(f);
condlog(4, "%s: assembled map [%s]", mp->alias, params);
return 0;
err:
- FREE(f);
return 1;
}
#undef APPEND
-int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
- int is_daemon)
+/*
+ * Caution callers: If this function encounters yet unkown path devices, it
+ * adds them uninitialized to the mpp.
+ * Call update_pathvec_from_dm() after this function to make sure
+ * all data structures are in a sane state.
+ */
+int disassemble_map(const struct _vector *pathvec,
+ const char *params, struct multipath *mpp)
{
char * word;
- char * p;
+ const char *p;
int i, j, k;
int num_features = 0;
int num_hwhandler = 0;
struct path * pp;
struct pathgroup * pgp;
+ assert(pathvec != NULL);
p = params;
condlog(4, "%s: disassemble map [%s]", mpp->alias, params);
if (!pgp)
goto out;
- if (add_pathgroup(mpp, pgp))
+ if (add_pathgroup(mpp, pgp)) {
+ free_pathgroup(pgp, KEEP_PATHS);
goto out;
+ }
p += get_word(p, &word);
FREE(word);
for (j = 0; j < num_paths; j++) {
- char devname[FILE_NAME_SIZE];
-
pp = NULL;
p += get_word(p, &word);
if (!word)
goto out;
- if (devt2devname(devname, FILE_NAME_SIZE, word)) {
- condlog(2, "%s: cannot find block device",
- word);
- devname[0] = '\0';
- }
-
- if (pathvec) {
- if (strlen(devname))
- pp = find_path_by_dev(pathvec, devname);
- else
- pp = find_path_by_devt(pathvec, word);
- }
+ pp = find_path_by_devt(pathvec, word);
if (!pp) {
pp = alloc_path();
goto out1;
strlcpy(pp->dev_t, word, BLK_DEV_SIZE);
- strlcpy(pp->dev, devname, FILE_NAME_SIZE);
- if (strlen(mpp->wwid)) {
- strlcpy(pp->wwid, mpp->wwid,
- WWID_SIZE);
- }
- /* Only call this in multipath client mode */
- if (!is_daemon && store_path(pathvec, pp))
- goto out1;
- } else {
- if (!strlen(pp->wwid) &&
- strlen(mpp->wwid))
- strlcpy(pp->wwid, mpp->wwid,
- WWID_SIZE);
- }
- FREE(word);
-
- if (store_path(pgp->paths, pp))
- goto out;
-
- /*
- * Update wwid for multipaths which are not setup
- * in the get_dm_mpvec() code path
- */
- if (!strlen(mpp->wwid))
- strlcpy(mpp->wwid, pp->wwid, WWID_SIZE);
- /*
- * Update wwid for paths which may not have been
- * active at the time the getuid callout was run
- */
- else if (!strlen(pp->wwid))
- strlcpy(pp->wwid, mpp->wwid, WWID_SIZE);
+ if (store_path(pgp->paths, pp)) {
+ free_path(pp);
+ goto out1;
+ }
+ } else if (store_path(pgp->paths, pp))
+ goto out1;
- /*
- * Do not allow in-use patch to change wwid
- */
- else if (strcmp(pp->wwid, mpp->wwid) != 0) {
- condlog(0, "%s: path wwid appears to have changed. Using map wwid.\n", pp->dev_t);
- strlcpy(pp->wwid, mpp->wwid, WWID_SIZE);
- }
+ FREE(word);
pgp->id ^= (long)pp;
pp->pgindex = i + 1;
return 1;
}
-int disassemble_status(char *params, struct multipath *mpp)
+int disassemble_status(const char *params, struct multipath *mpp)
{
- char * word;
- char * p;
+ char *word;
+ const char *p;
int i, j, k;
int num_feature_args;
int num_hwhandler_args;
&def_minio) == 1 &&
def_minio != mpp->minio)
mpp->minio = def_minio;
+ FREE(word);
} else
p += get_word(p, NULL);
}
int assemble_map (struct multipath *, char *, int);
-int disassemble_map (vector, char *, struct multipath *, int);
-int disassemble_status (char *, struct multipath *);
+int disassemble_map (const struct _vector *, const char *, struct multipath *);
+int disassemble_status (const char *, struct multipath *);
goto dl_err;
}
- if (vector_alloc_slot(foreigns) == NULL) {
+ if (!vector_alloc_slot(foreigns)) {
goto dl_err;
}
int buflen = MAX_LINE_LEN * MAX_LINES;
char *buf = NULL, *tmp = NULL;
- buf = malloc(buflen);
- buf[0] = '\0';
+ buf = calloc(1, buflen);
+
while (buf != NULL) {
char *c = buf;
struct nvme_pathgroup *pg;
char real[PATH_MAX];
const char *ppath;
+ const char *psyspath;
int i;
ppath = realpath(syspath, real);
vector_foreach_slot(&map->pgvec, pg, i) {
struct nvme_path *path = nvme_pg_to_path(pg);
- if (!strcmp(ppath,
- udev_device_get_syspath(path->udev)))
+ psyspath = udev_device_get_syspath(path->udev);
+ if (psyspath && !strcmp(ppath, psyspath))
return path;
}
condlog(4, "%s: %s: %s not found", __func__, THIS, ppath);
struct udev_list_entry *item;
struct udev_device *blkdev = NULL;
struct udev_enumerate *enm = udev_enumerate_new(ctx->udev);
+ const char *devtype;
if (enm == NULL)
return NULL;
udev_list_entry_get_name(item));
if (tmp == NULL)
continue;
- if (!strcmp(udev_device_get_devtype(tmp), "disk")) {
+
+ devtype = udev_device_get_devtype(tmp);
+ if (devtype && !strcmp(devtype, "disk")) {
blkdev = tmp;
break;
} else
test_ana_support(map, path->ctl);
path->pg.gen.ops = &nvme_pg_ops;
- if (vector_alloc_slot(&path->pg.pathvec) == NULL) {
+ if (!vector_alloc_slot(&path->pg.pathvec)) {
cleanup_nvme_path(path);
continue;
}
vector_set_slot(&path->pg.pathvec, path);
- if (vector_alloc_slot(&map->pgvec) == NULL) {
+ if (!vector_alloc_slot(&map->pgvec)) {
cleanup_nvme_path(path);
continue;
}
map->subsys = subsys;
map->gen.ops = &nvme_map_ops;
- if (vector_alloc_slot(ctx->mpvec) == NULL) {
+ if (!vector_alloc_slot(ctx->mpvec)) {
cleanup_nvme_map(map);
return FOREIGN_ERR;
}
{
struct udev_device *subsys;
int rc;
+ const char *devtype;
condlog(5, "%s called for \"%s\"", __func__, THIS);
if (ud == NULL)
return FOREIGN_ERR;
- if (strcmp("disk", udev_device_get_devtype(ud)))
+ if ((devtype = udev_device_get_devtype(ud)) == NULL ||
+ strcmp("disk", devtype))
return FOREIGN_IGNORED;
subsys = udev_device_get_parent_with_subsystem_devtype(ud,
.prio_name = PRIO_ALUA,
},
{
- /* MSA 1040, 1050, 2040 and 2050 families */
+ /* MSA 1040, 1050, 1060, 2040, 2050 and 2060 families */
.vendor = "HP",
- .product = "MSA [12]0[45]0 SA[NS]",
+ .product = "MSA [12]0[456]0 SA[NS]",
.pgpolicy = GROUP_BY_PRIO,
.pgfailback = -FAILBACK_IMMEDIATE,
.no_path_retry = 18,
.pgpolicy = MULTIBUS,
.no_path_retry = 10,
},
+ {
+ /*
+ * ETERNUS AB/HB
+ * Maintainer: NetApp RDAC team <ng-eseries-upstream-maintainers@netapp.com>
+ */
+ .vendor = "FUJITSU",
+ .product = "ETERNUS_AHB",
+ .bl_product = "Universal Xport",
+ .pgpolicy = GROUP_BY_PRIO,
+ .checker_name = RDAC,
+ .features = "2 pg_init_retries 50",
+ .hwhandler = "1 rdac",
+ .prio_name = PRIO_RDAC,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .no_path_retry = 30,
+ },
/*
* Hitachi Vantara
*
.no_path_retry = (300 / DEFAULT_CHECKINT),
.prio_name = PRIO_ALUA,
},
- /*
- * Lenovo
- */
- {
- /*
+ /*
+ * Lenovo
+ */
+ {
+ /*
* DE Series
*
* Maintainer: NetApp RDAC team <ng-eseries-upstream-maintainers@netapp.com>
*/
- .vendor = "LENOVO",
- .product = "DE_Series",
- .bl_product = "Universal Xport",
- .pgpolicy = GROUP_BY_PRIO,
- .checker_name = RDAC,
- .features = "2 pg_init_retries 50",
- .hwhandler = "1 rdac",
- .prio_name = PRIO_RDAC,
- .pgfailback = -FAILBACK_IMMEDIATE,
- .no_path_retry = 30,
- },
+ .vendor = "LENOVO",
+ .product = "DE_Series",
+ .bl_product = "Universal Xport",
+ .pgpolicy = GROUP_BY_PRIO,
+ .checker_name = RDAC,
+ .features = "2 pg_init_retries 50",
+ .hwhandler = "1 rdac",
+ .prio_name = PRIO_RDAC,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .no_path_retry = 30,
+ },
/*
* NetApp
*/
.prio_name = PRIO_ALUA,
},
/*
+ * MacroSAN Technologies
+ */
+ {
+ /* MS family */
+ .vendor = "MacroSAN",
+ .product = "LU",
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .prio_name = PRIO_ALUA,
+ .no_path_retry = 30,
+ },
+ /*
* EOL
*/
{
#include "time-util.h"
#include "io_err_stat.h"
-#define IOTIMEOUT_SEC 60
#define TIMEOUT_NO_IO_NSEC 10000000 /*10ms = 10000000ns*/
#define FLAKY_PATHFAIL_THRESHOLD 2
#define CONCUR_NR_EVENT 32
struct timespec curr_time;
if (uatomic_read(&io_err_thread_running) == 0)
- return 1;
+ return 0;
if (path->io_err_disable_reinstate) {
io_err_stat_log(3, "%s: reinstate is already disabled",
path->dev);
- return 1;
+ return 0;
}
if (path->io_err_pathfail_cnt < 0)
- return 1;
+ return 0;
if (!path->mpp)
- return 1;
- if (path->mpp->marginal_path_double_failed_time <= 0 ||
- path->mpp->marginal_path_err_sample_time <= 0 ||
- path->mpp->marginal_path_err_recheck_gap_time <= 0 ||
- path->mpp->marginal_path_err_rate_threshold < 0) {
- io_err_stat_log(4, "%s: parameter not set", path->mpp->alias);
- return 1;
- }
- if (path->mpp->marginal_path_err_sample_time < (2 * IOTIMEOUT_SEC)) {
- io_err_stat_log(2, "%s: marginal_path_err_sample_time should not less than %d",
- path->mpp->alias, 2 * IOTIMEOUT_SEC);
- return 1;
- }
+ return 0;
+
+ if (!marginal_path_check_enabled(path->mpp))
+ return 0;
+
/*
* The test should only be started for paths that have failed
* repeatedly in a certain time frame, so that we have reason
static const char quote_marker[] = { '\0', '"', '\0' };
bool is_quote(const char* token)
{
- return !memcmp(token, quote_marker, sizeof(quote_marker));
+ return token[0] == quote_marker[0] &&
+ token[1] == quote_marker[1] &&
+ token[2] == quote_marker[2];
}
vector
if (!vector_alloc_slot(strvec))
goto out;
+ vector_set_slot(strvec, NULL);
start = cp;
if (*cp == '"' && !(in_string && *(cp + 1) == '"')) {
cp++;
(isspace((int) *cp) || !isascii((int) *cp)))
&& *cp != '\0')
cp++;
- if (*cp == '\0' || *cp == '!' || *cp == '#')
+ if (*cp == '\0' ||
+ (!in_string && (*cp == '!' || *cp == '#'))) {
return strvec;
+ }
}
out:
vector_free(strvec);
goto oom;
}
if (*alloc != '\0')
- strncat(alloc, " ", 1);
+ strncat(alloc, " ", len - strlen(alloc));
strncat(alloc, str, len - strlen(alloc) - 1);
}
return alloc;
int
validate_config_strvec(vector strvec, char *file)
{
- char *str;
+ char *str = NULL;
int i;
- str = VECTOR_SLOT(strvec, 0);
+ if (strvec && VECTOR_SIZE(strvec) > 0)
+ str = VECTOR_SLOT(strvec, 0);
+
if (str == NULL) {
condlog(0, "can't parse option on line %d of %s",
line_nr, file);
- return -1;
+ return -1;
}
if (*str == '}') {
if (VECTOR_SIZE(strvec) > 1)
return -1;
}
if (is_sublevel_keyword(str)) {
- str = VECTOR_SLOT(strvec, 1);
+ str = VECTOR_SIZE(strvec) > 1 ? VECTOR_SLOT(strvec, 1) : NULL;
if (str == NULL)
condlog(0, "missing '{' on line %d of %s",
line_nr, file);
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);
+ str = VECTOR_SIZE(strvec) > 1 ? VECTOR_SLOT(strvec, 1) : NULL;
if (str == NULL) {
condlog(0, "missing value for option '%s' on line %d of %s",
(char *)VECTOR_SLOT(strvec, 0), line_nr, file);
bool (*path_match_fn)(struct path *, struct path *))
{
int i, j;
- int * bitmap;
+ struct bitfield *bitmap;
struct path * pp;
struct pathgroup * pgp;
struct path * pp2;
/* init the bitmap */
- bitmap = (int *)MALLOC(VECTOR_SIZE(paths) * sizeof (int));
+ bitmap = alloc_bitfield(VECTOR_SIZE(paths));
if (!bitmap)
goto out;
for (i = 0; i < VECTOR_SIZE(paths); i++) {
- if (bitmap[i])
+ if (is_bit_set_in_bitfield(i, bitmap))
continue;
pp = VECTOR_SLOT(paths, i);
if (store_path(pgp->paths, pp))
goto out1;
- bitmap[i] = 1;
+ set_bit_in_bitfield(i, bitmap);
for (j = i + 1; j < VECTOR_SIZE(paths); j++) {
- if (bitmap[j])
+ if (is_bit_set_in_bitfield(j, bitmap))
continue;
pp2 = VECTOR_SLOT(paths, j);
if (store_path(pgp->paths, pp2))
goto out1;
- bitmap[j] = 1;
+ set_bit_in_bitfield(j, bitmap);
}
}
}
#include "debug.h"
#include "discovery.h"
#include "util.h"
+#include "foreign.h"
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
#define MIN(x,y) (((x) > (y)) ? (y) : (x))
return snprintf(buff, len, "scsi:adt");
case SCSI_PROTOCOL_ATA:
return snprintf(buff, len, "scsi:ata");
+ case SCSI_PROTOCOL_USB:
+ return snprintf(buff, len, "scsi:usb");
case SCSI_PROTOCOL_UNSPEC:
default:
return snprintf(buff, len, "scsi:unspec");
}
c = reply + snprint_defaults(conf, reply, maxlen);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
c += snprint_blacklist(conf, c, reply + maxlen - c);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
c += snprint_blacklist_except(conf, c, reply + maxlen - c);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
c += snprint_hwtable(conf, c, reply + maxlen - c,
hwtable ? hwtable : conf->hwtable);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
c += snprint_overrides(conf, c, reply + maxlen - c,
conf->overrides);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
if (VECTOR_SIZE(conf->mptable) > 0 ||
c += snprint_mptable(conf, c, reply + maxlen - c,
mpvec);
- if ((c - reply) < maxlen) {
+ if (c < reply + maxlen) {
if (len)
*len = c - reply;
return reply;
return fwd;
}
-int snprint_devices(struct config *conf, char * buff, int len,
+int snprint_devices(struct config *conf, char *buff, size_t len,
const struct vectors *vecs)
{
- DIR *blkdir;
- struct dirent *blkdev;
- struct stat statbuf;
- char devpath[PATH_MAX];
- int threshold = MAX_LINE_LEN;
- int fwd = 0;
+ size_t fwd = 0;
int r;
+ struct udev_enumerate *enm;
+ struct udev_list_entry *item, *first;
struct path * pp;
- if (!(blkdir = opendir("/sys/block")))
+ enm = udev_enumerate_new(udev);
+ if (!enm)
return 1;
+ udev_enumerate_add_match_subsystem(enm, "block");
- if ((len - fwd - threshold) <= 0) {
- closedir(blkdir);
- return len;
- }
fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n");
+ r = udev_enumerate_scan_devices(enm);
+ if (r < 0)
+ goto out;
- while ((blkdev = readdir(blkdir)) != NULL) {
- if ((strcmp(blkdev->d_name,".") == 0) ||
- (strcmp(blkdev->d_name,"..") == 0))
- continue;
-
- if (safe_sprintf(devpath, "/sys/block/%s", blkdev->d_name))
- continue;
-
- if (stat(devpath, &statbuf) < 0)
- continue;
+ first = udev_enumerate_get_list_entry(enm);
+ udev_list_entry_foreach(item, first) {
+ const char *path, *devname, *status;
+ struct udev_device *u_dev;
- if (S_ISDIR(statbuf.st_mode) == 0)
- continue;
+ path = udev_list_entry_get_name(item);
+ u_dev = udev_device_new_from_syspath(udev, path);
+ devname = udev_device_get_sysname(u_dev);
- if ((len - fwd - threshold) <= 0) {
- closedir(blkdir);
- return len;
- }
+ fwd += snprintf(buff + fwd, len - fwd, " %s", devname);
+ if (fwd >= len)
+ break;
- fwd += snprintf(buff + fwd, len - fwd, " %s",
- blkdev->d_name);
- pp = find_path_by_dev(vecs->pathvec, blkdev->d_name);
+ pp = find_path_by_dev(vecs->pathvec, devname);
if (!pp) {
- r = filter_devnode(conf->blist_devnode,
- conf->elist_devnode, blkdev->d_name);
- if (r > 0)
- fwd += snprintf(buff + fwd, len - fwd,
- " devnode blacklisted, unmonitored");
- else if (r <= 0)
- fwd += snprintf(buff + fwd, len - fwd,
- " devnode whitelisted, unmonitored");
+ const char *hidden;
+
+ hidden = udev_device_get_sysattr_value(u_dev,
+ "hidden");
+ if (hidden && !strcmp(hidden, "1"))
+ status = "hidden, unmonitored";
+ else if (is_claimed_by_foreign(u_dev))
+ status = "foreign, monitored";
+ else {
+ r = filter_devnode(conf->blist_devnode,
+ conf->elist_devnode,
+ devname);
+ if (r > 0)
+ status = "devnode blacklisted, unmonitored";
+ else
+ status = "devnode whitelisted, unmonitored";
+ }
} else
- fwd += snprintf(buff + fwd, len - fwd,
- " devnode whitelisted, monitored");
- fwd += snprintf(buff + fwd, len - fwd, "\n");
+ status = " devnode whitelisted, monitored";
+
+ fwd += snprintf(buff + fwd, len - fwd, " %s\n", status);
+ udev_device_unref(u_dev);
+ if (fwd >= len)
+ break;
}
- closedir(blkdir);
+out:
+ udev_enumerate_unref(enm);
if (fwd >= len)
return len;
int snprint_blacklist_report (struct config *, char *, int);
int snprint_wildcards (char *, int);
int snprint_status (char *, int, const struct vectors *);
-int snprint_devices (struct config *, char *, int, const struct vectors *);
+int snprint_devices (struct config *, char *, size_t, const struct vectors *);
int snprint_path_serial (char *, size_t, const struct path *);
int snprint_host_wwnn (char *, size_t, const struct path *);
int snprint_host_wwpn (char *, size_t, const struct path *);
all: $(LIBS)
-libpriopath_latency.so: path_latency.o ../checkers/libsg.o
+libpriopath_latency.so: path_latency.o
$(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -lm
libprio%.so: %.o
int do_inquiry(const struct path *pp, int evpd, unsigned int codepage,
void *resp, int resplen, unsigned int timeout)
{
- struct udev_device *ud;
+ struct udev_device *ud = NULL;
- ud = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi",
+ if (pp->udev)
+ ud = udev_device_get_parent_with_subsystem_devtype(pp->udev,
+ "scsi",
"scsi_device");
if (ud != NULL) {
int rc;
#define RTPG_FOR_EACH_PORT_GROUP(p, g) \
for( \
g = &(p->data[0]); \
- (((char *) g) - ((char *) p)) < get_unaligned_be32(p->length); \
+ ((char *) g) < ((char *) p) + get_unaligned_be32(p->length); \
g = (struct rtpg_tpg_dscr *) ( \
((char *) g) + \
sizeof(struct rtpg_tpg_dscr) + \
__do_set_from_vec(struct hwentry, var, (src)->hwe, dest)
#define do_set_from_hwe(var, src, dest, msg) \
- if (__do_set_from_hwe(var, src, dest)) { \
+ if (!src->hwe) { \
+ condlog(0, "BUG: do_set_from_hwe called with hwe == NULL"); \
+ } else if (__do_set_from_hwe(var, src, dest)) { \
origin = msg; \
goto out; \
}
if (check_rdac(pp)) {
ckr_name = RDAC;
goto out;
- } else if (path_get_tpgs(pp) != TPGS_NONE) {
+ }
+ path_get_tpgs(pp);
+ if (pp->tpgs != TPGS_NONE && pp->tpgs != TPGS_UNDEF) {
ckr_name = TUR;
goto out;
}
mp_set_ovr(dev_loss);
mp_set_hwe(dev_loss);
mp_set_conf(dev_loss);
- mp->dev_loss = 0;
+ mp->dev_loss = DEV_LOSS_TMO_UNSET;
return 0;
out:
print_dev_loss(buff, 12, mp->dev_loss);
mp_set_conf(marginal_path_err_sample_time);
mp_set_default(marginal_path_err_sample_time, DEFAULT_ERR_CHECKS);
out:
+ if (mp->marginal_path_err_sample_time > 0 &&
+ mp->marginal_path_err_sample_time < 2 * IOTIMEOUT_SEC) {
+ condlog(2, "%s: configuration error: marginal_path_err_sample_time must be >= %d",
+ mp->alias, 2 * IOTIMEOUT_SEC);
+ mp->marginal_path_err_sample_time = 2 * IOTIMEOUT_SEC;
+ }
if (print_off_int_undef(buff, 12, mp->marginal_path_err_sample_time)
!= 0)
condlog(3, "%s: marginal_path_err_sample_time = %s %s",
pp = (struct path *)MALLOC(sizeof(struct path));
if (pp) {
+ pp->initialized = INIT_NEW;
pp->sg_id.host_no = -1;
pp->sg_id.channel = -1;
pp->sg_id.scsi_id = -1;
}
void
-free_path (struct path * pp)
+uninitialize_path(struct path *pp)
{
if (!pp)
return;
+ pp->dmstate = PSTATE_UNDEF;
+ pp->uid_attribute = NULL;
+ pp->getuid = NULL;
+
if (checker_selected(&pp->checker))
checker_put(&pp->checker);
if (prio_selected(&pp->prio))
prio_put(&pp->prio);
- if (pp->fd >= 0)
+ if (pp->fd >= 0) {
close(pp->fd);
+ pp->fd = -1;
+ }
+}
+
+void
+free_path (struct path * pp)
+{
+ if (!pp)
+ return;
+
+ uninitialize_path(pp);
if (pp->udev) {
udev_device_unref(pp->udev);
mpp->dmi = NULL;
}
+ if (!free_paths && mpp->pg) {
+ struct pathgroup *pgp;
+ struct path *pp;
+ int i, j;
+
+ /*
+ * Make sure paths carry no reference to this mpp any more
+ */
+ vector_foreach_slot(mpp->pg, pgp, i) {
+ vector_foreach_slot(pgp->paths, pp, j)
+ if (pp->mpp == mpp)
+ pp->mpp = NULL;
+ }
+ }
+
free_pathvec(mpp->paths, free_paths);
free_pgvec(mpp->pg, free_paths);
FREE_PTR(mpp->mpcontext);
err++;
}
if (!strlen(pp->dev)) {
- condlog(2, "%s: Empty device name", pp->dev_t);
+ condlog(3, "%s: Empty device name", pp->dev_t);
err++;
}
return NULL;
}
-int pathcountgr(const struct pathgroup *pgp, int state)
+static int do_pathcount(const struct multipath *mpp, const int *states,
+ unsigned int nr_states)
{
+ struct pathgroup *pgp;
struct path *pp;
int count = 0;
- int i;
+ unsigned int i, j, k;
- vector_foreach_slot (pgp->paths, pp, i)
- if ((pp->state == state) || (state == PATH_WILD))
- count++;
+ if (!mpp->pg || !nr_states)
+ return count;
+ vector_foreach_slot (mpp->pg, pgp, i) {
+ vector_foreach_slot (pgp->paths, pp, j) {
+ for (k = 0; k < nr_states; k++) {
+ if (pp->state == states[k]) {
+ count++;
+ break;
+ }
+ }
+ }
+ }
return count;
}
int pathcount(const struct multipath *mpp, int state)
{
- struct pathgroup *pgp;
- int count = 0;
- int i;
-
- if (mpp->pg) {
- vector_foreach_slot (mpp->pg, pgp, i)
- count += pathcountgr(pgp, state);
- }
- return count;
+ return do_pathcount(mpp, &state, 1);
}
int count_active_paths(const struct multipath *mpp)
return count;
}
+int count_active_pending_paths(const struct multipath *mpp)
+{
+ int states[] = {PATH_UP, PATH_GHOST, PATH_PENDING};
+
+ return do_pathcount(mpp, states, 3);
+}
+
int pathcmp(const struct pathgroup *pgp, const struct pathgroup *cpgp)
{
int i, j;
YNU_YES,
};
-#define _FIND_MULTIPATHS_F (1 << 1)
-#define _FIND_MULTIPATHS_I (1 << 2)
-#define _FIND_MULTIPATHS_N (1 << 3)
-/*
- * _FIND_MULTIPATHS_F must have the same value as YNU_YES.
- * Generate a compile time error if that isn't the case.
- */
-extern char ___error1___[-(_FIND_MULTIPATHS_F != YNU_YES)];
-
-#define find_multipaths_on(conf) \
- (!!((conf)->find_multipaths & _FIND_MULTIPATHS_F))
-#define ignore_wwids_on(conf) \
- (!!((conf)->find_multipaths & _FIND_MULTIPATHS_I))
-#define ignore_new_devs_on(conf) \
- (!!((conf)->find_multipaths & _FIND_MULTIPATHS_N))
-
enum find_multipaths_states {
FIND_MULTIPATHS_UNDEF = YNU_UNDEF,
FIND_MULTIPATHS_OFF = YNU_NO,
- FIND_MULTIPATHS_ON = _FIND_MULTIPATHS_F,
- FIND_MULTIPATHS_GREEDY = _FIND_MULTIPATHS_I,
- FIND_MULTIPATHS_SMART = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_I,
- FIND_MULTIPATHS_STRICT = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_N,
+ FIND_MULTIPATHS_ON = YNU_YES,
+ FIND_MULTIPATHS_GREEDY,
+ FIND_MULTIPATHS_SMART,
+ FIND_MULTIPATHS_STRICT,
__FIND_MULTIPATHS_LAST,
};
SCSI_PROTOCOL_SAS = 6,
SCSI_PROTOCOL_ADT = 7, /* Media Changers */
SCSI_PROTOCOL_ATA = 8,
+ SCSI_PROTOCOL_USB = 9, /* USB Attached SCSI (UAS), and others */
SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
};
INIT_MISSING_UDEV,
INIT_REQUESTED_UDEV,
INIT_OK,
+ /*
+ * INIT_REMOVED: supposed to be removed from pathvec, but still
+ * mapped by some multipath map because of map reload failure.
+ */
+ INIT_REMOVED,
};
enum prkey_sources {
struct path * alloc_path (void);
struct pathgroup * alloc_pathgroup (void);
struct multipath * alloc_multipath (void);
+void uninitialize_path(struct path *pp);
void free_path (struct path *);
void free_pathvec (vector vec, enum free_path_mode free_paths);
void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths);
struct path * find_path_by_dev (const struct _vector *pathvec, const char *dev);
struct path * first_path (const struct multipath *mpp);
-int pathcountgr (const struct pathgroup *, int);
int pathcount (const struct multipath *, int);
int count_active_paths(const struct multipath *);
+int count_active_pending_paths(const struct multipath *);
int pathcmp (const struct pathgroup *, const struct pathgroup *);
int add_feature (char **, const char *);
int remove_feature (char **, const char *);
struct pathgroup * pgp;
struct path * pp;
int i,j;
+ bool store_failure = false;
if (!mpp || !mpp->pg)
return 0;
vector_foreach_slot (mpp->pg, pgp, i) {
vector_foreach_slot (pgp->paths, pp, j) {
- if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
- (find_path_by_devt(pathvec, pp->dev_t)) &&
- store_path(mpp->paths, pp))
- return 1;
+ if (!find_path_by_devt(mpp->paths, pp->dev_t)) {
+ struct path *pp1;
+
+ /*
+ * Avoid adding removed paths to the map again
+ * when we reload it. Such paths may exist if
+ * domap fails in ev_remove_path().
+ */
+ pp1 = find_path_by_devt(pathvec, pp->dev_t);
+ if (pp1 && pp->initialized != INIT_REMOVED &&
+ store_path(mpp->paths, pp))
+ store_failure = true;
+ }
}
}
- return 0;
+
+ return store_failure;
+}
+
+static bool guess_mpp_wwid(struct multipath *mpp)
+{
+ int i, j;
+ struct pathgroup *pgp;
+ struct path *pp;
+
+ if (strlen(mpp->wwid) || !mpp->pg)
+ return true;
+
+ vector_foreach_slot(mpp->pg, pgp, i) {
+ if (!pgp->paths)
+ continue;
+ vector_foreach_slot(pgp->paths, pp, j) {
+ if (pp->initialized == INIT_OK && strlen(pp->wwid)) {
+ strlcpy(mpp->wwid, pp->wwid, sizeof(mpp->wwid));
+ condlog(2, "%s: guessed WWID %s from path %s",
+ mpp->alias, mpp->wwid, pp->dev);
+ return true;
+ }
+ }
+ }
+ condlog(1, "%s: unable to guess WWID", mpp->alias);
+ return false;
+}
+
+/*
+ * update_pathvec_from_dm() - update pathvec after disassemble_map()
+ *
+ * disassemble_map() may return block devices that are members in
+ * multipath maps but haven't been discovered. Check whether they
+ * need to be added to pathvec or discarded.
+ *
+ * Returns: true if immediate map reload is desirable
+ *
+ * Side effects:
+ * - may delete non-existing paths and empty pathgroups from mpp
+ * - may set pp->wwid and / or mpp->wwid
+ * - calls pathinfo() on existing paths is pathinfo_flags is not 0
+ */
+bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
+ int pathinfo_flags)
+{
+ int i, j;
+ struct pathgroup *pgp;
+ struct path *pp;
+ struct config *conf;
+ bool mpp_has_wwid;
+ bool must_reload = false;
+
+ if (!mpp->pg)
+ return false;
+
+ /*
+ * This will initialize mpp->wwid with an educated guess,
+ * either from the dm uuid or from a member path with properly
+ * determined WWID.
+ */
+ mpp_has_wwid = guess_mpp_wwid(mpp);
+
+ vector_foreach_slot(mpp->pg, pgp, i) {
+ if (!pgp->paths)
+ goto delete_pg;
+
+ vector_foreach_slot(pgp->paths, pp, j) {
+
+ if (pp->mpp && pp->mpp != mpp) {
+ condlog(0, "BUG: %s: found path %s which is already in %s",
+ mpp->alias, pp->dev, pp->mpp->alias);
+
+ /*
+ * Either we added this path to the other mpp
+ * explicitly, or we came by here earlier and
+ * decided it belonged there. In both cases,
+ * the path should remain in the other map,
+ * and be deleted here.
+ */
+ must_reload = true;
+ dm_fail_path(mpp->alias, pp->dev_t);
+ vector_del_slot(pgp->paths, j--);
+ continue;
+ }
+ pp->mpp = mpp;
+
+ /*
+ * The way disassemble_map() works: If it encounters a
+ * path device which isn't found in pathvec, it adds an
+ * uninitialized struct path to pgp->paths, with only
+ * pp->dev_t filled in. Thus if pp->udev is set here,
+ * we know that the path is in pathvec already.
+ * However, it's possible that the path in pathvec is
+ * different from the one the kernel still had in its
+ * map.
+ */
+ if (pp->udev) {
+ if (pathinfo_flags & ~DI_NOIO) {
+ conf = get_multipath_config();
+ pthread_cleanup_push(put_multipath_config,
+ conf);
+ pathinfo(pp, conf, pathinfo_flags|DI_WWID);
+ pthread_cleanup_pop(1);
+ }
+ } else {
+ /* If this fails, the device is not in sysfs */
+ pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
+
+ if (!pp->udev) {
+ condlog(2, "%s: discarding non-existing path %s",
+ mpp->alias, pp->dev_t);
+ vector_del_slot(pgp->paths, j--);
+ free_path(pp);
+ must_reload = true;
+ continue;
+ } else {
+ int rc;
+
+ devt2devname(pp->dev, sizeof(pp->dev),
+ pp->dev_t);
+ conf = get_multipath_config();
+ pthread_cleanup_push(put_multipath_config,
+ conf);
+ pp->checkint = conf->checkint;
+ rc = pathinfo(pp, conf,
+ DI_SYSFS|DI_WWID|DI_BLACKLIST|
+ pathinfo_flags);
+ pthread_cleanup_pop(1);
+ if (rc != PATHINFO_OK) {
+ condlog(1, "%s: error %d in pathinfo, discarding path",
+ pp->dev, rc);
+ vector_del_slot(pgp->paths, j--);
+ free_path(pp);
+ must_reload = true;
+ continue;
+ }
+ condlog(2, "%s: adding new path %s",
+ mpp->alias, pp->dev);
+ store_path(pathvec, pp);
+ pp->tick = 1;
+ }
+ }
+
+ /* We don't set the map WWID from paths here */
+ if (!mpp_has_wwid)
+ continue;
+
+ /*
+ * At this point, pp->udev is valid and and pp->wwid
+ * is the best we could get
+ */
+ if (*pp->wwid && strcmp(mpp->wwid, pp->wwid)) {
+ condlog(0, "%s: path %s WWID %s doesn't match, removing from map",
+ mpp->wwid, pp->dev_t, pp->wwid);
+ /*
+ * This path exists, but in the wrong map.
+ * We can't reload the map from here.
+ * Make sure it isn't used in this map
+ * any more, and let the checker re-add
+ * it as it sees fit.
+ */
+ dm_fail_path(mpp->alias, pp->dev_t);
+ vector_del_slot(pgp->paths, j--);
+ orphan_path(pp, "WWID mismatch");
+ pp->tick = 1;
+ must_reload = true;
+ } else if (!*pp->wwid) {
+ condlog(3, "%s: setting wwid from map: %s",
+ pp->dev, mpp->wwid);
+ strlcpy(pp->wwid, mpp->wwid,
+ sizeof(pp->wwid));
+ }
+ }
+ if (VECTOR_SIZE(pgp->paths) != 0)
+ continue;
+ delete_pg:
+ condlog(2, "%s: removing empty pathgroup %d", mpp->alias, i);
+ vector_del_slot(mpp->pg, i--);
+ free_pathgroup(pgp, KEEP_PATHS);
+ must_reload = true;
+ }
+ return must_reload;
}
int adopt_paths(vector pathvec, struct multipath *mpp)
pp->dev, mpp->alias);
continue;
}
- condlog(3, "%s: ownership set to %s",
- pp->dev, mpp->alias);
- pp->mpp = mpp;
-
+ if (pp->initialized == INIT_REMOVED)
+ continue;
if (!mpp->paths && !(mpp->paths = vector_alloc()))
- return 1;
+ goto err;
- if (!find_path_by_dev(mpp->paths, pp->dev) &&
- store_path(mpp->paths, pp))
- return 1;
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
ret = pathinfo(pp, conf,
DI_PRIO | DI_CHECKER);
pthread_cleanup_pop(1);
- if (ret)
- return 1;
+ if (ret) {
+ condlog(3, "%s: pathinfo failed for %s",
+ __func__, pp->dev);
+ continue;
+ }
+
+ if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
+ store_path(mpp->paths, pp))
+ goto err;
+
+ pp->mpp = mpp;
+ condlog(3, "%s: ownership set to %s",
+ pp->dev, mpp->alias);
}
}
return 0;
+err:
+ condlog(1, "error setting ownership of %s to %s", pp->dev, mpp->alias);
+ return 1;
}
void orphan_path(struct path *pp, const char *reason)
{
condlog(3, "%s: orphan path, %s", pp->dev, reason);
+ if (pp->mpp && pp->hwe && pp->mpp->hwe == pp->hwe) {
+ condlog(0, "BUG: orphaning path %s that holds hwe of %s",
+ pp->dev, pp->mpp->alias);
+ pp->mpp->hwe = NULL;
+ }
pp->mpp = NULL;
- pp->dmstate = PSTATE_UNDEF;
- pp->uid_attribute = NULL;
- pp->getuid = NULL;
- prio_put(&pp->prio);
- checker_put(&pp->checker);
- if (pp->fd >= 0)
- close(pp->fd);
- pp->fd = -1;
+ uninitialize_path(pp);
}
void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
int i;
struct path * pp;
+ /* Avoid BUG message from orphan_path() */
+ mpp->hwe = NULL;
vector_foreach_slot (pathvec, pp, i) {
if (pp->mpp == mpp) {
- orphan_path(pp, reason);
+ if (pp->initialized == INIT_REMOVED) {
+ condlog(3, "%s: freeing path in removed state",
+ pp->dev);
+ vector_del_slot(pathvec, i--);
+ free_path(pp);
+ } else
+ orphan_path(pp, reason);
}
}
}
+void set_path_removed(struct path *pp)
+{
+ struct multipath *mpp = pp->mpp;
+
+ orphan_path(pp, "removed");
+ /*
+ * Keep link to mpp. It will be removed when the path
+ * is successfully removed from the map.
+ */
+ if (!mpp) {
+ condlog(0, "%s: internal error: mpp == NULL", pp->dev);
+ return;
+ }
+ pp->mpp = mpp;
+ pp->initialized = INIT_REMOVED;
+}
+
void
-remove_map(struct multipath * mpp, struct vectors * vecs, int purge_vec)
+remove_map(struct multipath *mpp, vector pathvec, vector mpvec, int purge_vec)
{
int i;
/*
* clear references to this map
*/
- orphan_paths(vecs->pathvec, mpp, "map removed internally");
+ orphan_paths(pathvec, mpp, "map removed internally");
if (purge_vec &&
- (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
- vector_del_slot(vecs->mpvec, i);
+ (i = find_slot(mpvec, (void *)mpp)) != -1)
+ vector_del_slot(mpvec, i);
/*
* final free
struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias);
if (mpp) {
condlog(2, "%s: removing map by alias", alias);
- remove_map(mpp, vecs, purge_vec);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, purge_vec);
}
}
return;
vector_foreach_slot (vecs->mpvec, mpp, i) {
- remove_map(mpp, vecs, 1);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
i--;
}
}
int
-update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon)
+update_multipath_table (struct multipath *mpp, vector pathvec, int flags)
{
+ int r = DMP_ERR;
char params[PARAMS_SIZE] = {0};
if (!mpp)
- return 1;
+ return r;
- if (dm_get_map(mpp->alias, &mpp->size, params)) {
- condlog(3, "%s: cannot get map", mpp->alias);
- return 1;
+ r = dm_get_map(mpp->alias, &mpp->size, params);
+ if (r != DMP_OK) {
+ condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present");
+ return r;
}
- if (disassemble_map(pathvec, params, mpp, is_daemon)) {
+ if (disassemble_map(pathvec, params, mpp)) {
condlog(3, "%s: cannot disassemble map", mpp->alias);
- return 1;
+ return DMP_ERR;
}
- return 0;
+ /* FIXME: we should deal with the return value here */
+ update_pathvec_from_dm(pathvec, mpp, flags);
+
+ return DMP_OK;
}
int
update_multipath_status (struct multipath *mpp)
{
+ int r = DMP_ERR;
char status[PARAMS_SIZE] = {0};
if (!mpp)
- return 1;
+ return r;
- if (dm_get_status(mpp->alias, status)) {
- condlog(3, "%s: cannot get status", mpp->alias);
- return 1;
+ r = dm_get_status(mpp->alias, status);
+ if (r != DMP_OK) {
+ condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present");
+ return r;
}
if (disassemble_status(status, mpp)) {
condlog(3, "%s: cannot disassemble status", mpp->alias);
- return 1;
+ return DMP_ERR;
}
- return 0;
+ return DMP_OK;
+}
+
+static struct path *find_devt_in_pathgroups(const struct multipath *mpp,
+ const char *dev_t)
+{
+ struct pathgroup *pgp;
+ struct path *pp;
+ int j;
+
+ vector_foreach_slot(mpp->pg, pgp, j) {
+ pp = find_path_by_devt(pgp->paths, dev_t);
+ if (pp)
+ return pp;
+ }
+ return NULL;
+}
+
+static void check_removed_paths(const struct multipath *mpp, vector pathvec)
+{
+ struct path *pp;
+ int i;
+
+ vector_foreach_slot(pathvec, pp, i) {
+ if (pp->initialized != INIT_REMOVED || pp->mpp != mpp)
+ continue;
+ if (!find_devt_in_pathgroups(mpp, pp->dev_t)) {
+ condlog(2, "%s: %s: freeing path in removed state",
+ __func__, pp->dev);
+ vector_del_slot(pathvec, i--);
+ free_path(pp);
+ }
+ }
}
void sync_paths(struct multipath *mpp, vector pathvec)
}
if (!found) {
condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
+ if (mpp->hwe == pp->hwe)
+ mpp->hwe = NULL;
vector_del_slot(mpp->paths, i--);
orphan_path(pp, "path removed externally");
}
}
+ check_removed_paths(mpp, pathvec);
update_mpp_paths(mpp, pathvec);
vector_foreach_slot (mpp->paths, pp, i)
pp->mpp = mpp;
+ if (mpp->hwe == NULL)
+ extract_hwe_from_path(mpp);
}
int
-update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
+update_multipath_strings(struct multipath *mpp, vector pathvec)
{
struct pathgroup *pgp;
- int i;
+ int i, r = DMP_ERR;
if (!mpp)
- return 1;
+ return r;
update_mpp_paths(mpp, pathvec);
condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
free_pgvec(mpp->pg, KEEP_PATHS);
mpp->pg = NULL;
- if (update_multipath_table(mpp, pathvec, is_daemon))
- return 1;
+ r = update_multipath_table(mpp, pathvec, 0);
+ if (r != DMP_OK)
+ return r;
sync_paths(mpp, pathvec);
- if (update_multipath_status(mpp))
- return 1;
+ r = update_multipath_status(mpp);
+ if (r != DMP_OK)
+ return r;
vector_foreach_slot(mpp->pg, pgp, i)
if (pgp->paths)
path_group_prio_update(pgp);
- return 0;
+ return DMP_OK;
}
static void enter_recovery_mode(struct multipath *mpp)
void __set_no_path_retry(struct multipath *mpp, bool check_features)
{
- bool is_queueing;
+ bool is_queueing = false; /* assign a value to make gcc happy */
check_features = check_features && mpp->features != NULL;
if (check_features)
goto out;
mpp->size = pp->size;
- if (adopt_paths(vecs->pathvec, mpp))
+ if (adopt_paths(vecs->pathvec, mpp) ||
+ find_slot(vecs->pathvec, pp) == -1)
goto out;
if (add_vec) {
return mpp;
out:
- remove_map(mpp, vecs, PURGE_VEC);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
return NULL;
}
-int verify_paths(struct multipath *mpp, struct vectors *vecs)
+int verify_paths(struct multipath *mpp)
{
struct path * pp;
int count = 0;
- int i, j;
+ int i;
if (!mpp)
return 0;
/*
* see if path is in sysfs
*/
- if (sysfs_attr_get_value(pp->udev, "dev",
+ if (!pp->udev || 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);
} else {
- condlog(3, "%s: failed to access path %s",
+ condlog(2, "%s: failed to access path %s",
mpp->alias, pp->dev);
}
count++;
*/
if (mpp->hwe == pp->hwe)
mpp->hwe = NULL;
- if ((j = find_slot(vecs->pathvec,
- (void *)pp)) != -1)
- vector_del_slot(vecs->pathvec, j);
- free_path(pp);
+ /*
+ * Don't delete path from pathvec yet. We'll do this
+ * after the path has been removed from the map, in
+ * sync_paths().
+ */
+ set_path_removed(pp);
} else {
condlog(4, "%s: verified path %s dev_t %s",
mpp->alias, pp->dev, pp->dev_t);
void orphan_paths(vector pathvec, struct multipath *mpp,
const char *reason);
void orphan_path (struct path * pp, const char *reason);
+void set_path_removed(struct path *pp);
-int verify_paths(struct multipath * mpp, struct vectors * vecs);
+int verify_paths(struct multipath *mpp);
+bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
+ int pathinfo_flags);
int update_mpp_paths(struct multipath * mpp, vector pathvec);
-int update_multipath_strings (struct multipath *mpp, vector pathvec,
- int is_daemon);
+int update_multipath_strings (struct multipath *mpp, vector pathvec);
void extract_hwe_from_path(struct multipath * mpp);
-#define PURGE_VEC 1
+enum {
+ KEEP_VEC,
+ PURGE_VEC,
+};
-void remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec);
+void remove_map (struct multipath *mpp, vector pathvec, vector mpvec,
+ int purge_vec);
void remove_map_by_alias(const char *alias, struct vectors * vecs,
int purge_vec);
void remove_maps (struct vectors * vecs);
struct path * pp, int add_vec);
void update_queue_mode_del_path(struct multipath *mpp);
void update_queue_mode_add_path(struct multipath *mpp);
-int update_multipath_table (struct multipath *mpp, vector pathvec,
- int is_daemon);
+int update_multipath_table (struct multipath *mpp, vector pathvec, int flags);
int update_multipath_status (struct multipath *mpp);
vector get_used_hwes(const struct _vector *pathvec);
continue;
}
table_name = dm_mapname(major, table_minor);
-
+ if (!table_name) {
+ condlog(2, "%s: mapname not found for %d:%d", check_dev,
+ major, table_minor);
+ continue;
+ }
condlog(0, "%s: reassign table %s old %s new %s", check_dev,
table_name, check_devt, new_devt);
return fnmatch("dm-*", di->d_name, FNM_FILE_NAME) == 0;
}
-bool sysfs_is_multipathed(const struct path *pp)
+bool sysfs_is_multipathed(struct path *pp, bool set_wwid)
{
char pathbuf[PATH_MAX];
struct scandir_result sr;
for (i = 0; i < r && !found; i++) {
long fd;
int nr;
- char uuid[6];
+ char uuid[WWID_SIZE + UUID_PREFIX_LEN];
if (safe_snprintf(pathbuf + n, sizeof(pathbuf) - n,
"/%s/dm/uuid", di[i]->d_name))
pthread_cleanup_push(close_fd, (void *)fd);
nr = read(fd, uuid, sizeof(uuid));
- if (nr == sizeof(uuid) && !memcmp(uuid, "mpath-", sizeof(uuid)))
+ if (nr > (int)UUID_PREFIX_LEN &&
+ !memcmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN))
found = true;
else if (nr < 0) {
- condlog(1, "%s: error reading from %s: %s",
- __func__, pathbuf, strerror(errno));
+ condlog(1, "%s: error reading from %s: %m",
+ __func__, pathbuf);
}
+ if (found && set_wwid) {
+ nr -= UUID_PREFIX_LEN;
+ memcpy(pp->wwid, uuid + UUID_PREFIX_LEN, nr);
+ if (nr == WWID_SIZE) {
+ condlog(4, "%s: overflow while reading from %s",
+ __func__, pathbuf);
+ pp->wwid[0] = '\0';
+ } else {
+ pp->wwid[nr] = '\0';
+ strchop(pp->wwid);
+ }
+ }
+
pthread_cleanup_pop(1);
}
pthread_cleanup_pop(1);
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);
-bool sysfs_is_multipathed(const struct path *pp);
+bool sysfs_is_multipathed(struct path *pp, bool set_wwid);
#endif
typedef int (uev_trigger)(struct uevent *, void * trigger_data);
-LIST_HEAD(uevq);
-pthread_mutex_t uevq_lock = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t *uevq_lockp = &uevq_lock;
-pthread_cond_t uev_cond = PTHREAD_COND_INITIALIZER;
-pthread_cond_t *uev_condp = &uev_cond;
-uev_trigger *my_uev_trigger;
-void * my_trigger_data;
-int servicing_uev;
+static LIST_HEAD(uevq);
+static pthread_mutex_t uevq_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t *uevq_lockp = &uevq_lock;
+static pthread_cond_t uev_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t *uev_condp = &uev_cond;
+static uev_trigger *my_uev_trigger;
+static void *my_trigger_data;
+static int servicing_uev;
int is_uevent_busy(void)
{
return uev;
}
-void
-uevq_cleanup(struct list_head *tmpq)
+static void uevq_cleanup(struct list_head *tmpq)
{
struct uevent *uev, *tmp;
}
}
- condlog(4, "%s: %s -> '%s'", __func__, attr, p);
+ condlog(4, "%s: %s -> '%s'", __func__, attr, p ?: "(null)");
return p;
invalid:
return NULL;
}
-static int uevent_get_env_positive_int(const struct uevent *uev,
+int uevent_get_env_positive_int(const struct uevent *uev,
const char *attr)
{
const char *p = uevent_get_env_var(uev, attr);
uev->wwid = val;
}
-bool
-uevent_need_merge(void)
+static bool uevent_need_merge(void)
{
struct config * conf;
bool need_merge = false;
return need_merge;
}
-bool
-uevent_can_discard(struct uevent *uev)
+static bool uevent_can_discard(struct uevent *uev)
{
int invalid = 0;
struct config * conf;
return false;
}
-bool
+static bool
uevent_can_filter(struct uevent *earlier, struct uevent *later)
{
return false;
}
-bool
+static bool
merge_need_stop(struct uevent *earlier, struct uevent *later)
{
/*
return false;
}
-bool
+static bool
uevent_can_merge(struct uevent *earlier, struct uevent *later)
{
/* merge paths uevents
return false;
}
-void
+static void
uevent_prepare(struct list_head *tmpq)
{
struct uevent *uev, *tmp;
}
}
-void
+static void
uevent_filter(struct uevent *later, struct list_head *tmpq)
{
struct uevent *earlier, *tmp;
}
}
-void
+static void
uevent_merge(struct uevent *later, struct list_head *tmpq)
{
struct uevent *earlier, *tmp;
}
}
-void
+static void
merge_uevq(struct list_head *tmpq)
{
struct uevent *later;
}
}
-void
+static void
service_uevq(struct list_head *tmpq)
{
struct uevent *uev, *tmp;
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;
- struct sockaddr_nl snl;
- struct sockaddr_un sun;
- socklen_t addrlen;
- int retval;
- int rcvbufsz = 128*1024;
- int rcvsz = 0;
- int rcvszsz = sizeof(rcvsz);
- unsigned int *prcvszsz = (unsigned int *)&rcvszsz;
- const int feature_on = 1;
- /*
- * First check whether we have a udev socket
- */
- memset(&sun, 0x00, sizeof(struct sockaddr_un));
- sun.sun_family = AF_LOCAL;
- strcpy(&sun.sun_path[1], "/org/kernel/dm/multipath_event");
- addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun.sun_path+1) + 1;
-
- sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
- if (sock >= 0) {
-
- condlog(3, "reading events from udev socket.");
-
- /* the bind takes care of ensuring only one copy running */
- retval = bind(sock, (struct sockaddr *) &sun, addrlen);
- if (retval < 0) {
- condlog(0, "bind failed, exit");
- goto exit;
- }
-
- /* enable receiving of the sender credentials */
- retval = setsockopt(sock, SOL_SOCKET, SO_PASSCRED,
- &feature_on, sizeof(feature_on));
- if (retval < 0) {
- condlog(0, "failed to enable credential passing, exit");
- goto exit;
- }
-
- } else {
- /* Fallback to read kernel netlink events */
- memset(&snl, 0x00, sizeof(struct sockaddr_nl));
- snl.nl_family = AF_NETLINK;
- snl.nl_pid = getpid();
- snl.nl_groups = 0x01;
-
- sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
- if (sock == -1) {
- condlog(0, "error getting socket, exit");
- return 1;
- }
-
- condlog(3, "reading events from kernel.");
-
- /*
- * try to avoid dropping uevents, even so, this is not a guarantee,
- * but it does help to change the netlink uevent socket's
- * receive buffer threshold from the default value of 106,496 to
- * the maximum value of 262,142.
- */
- retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz,
- sizeof(rcvbufsz));
-
- if (retval < 0) {
- condlog(0, "error setting receive buffer size for socket, exit");
- exit(1);
- }
- retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz);
- if (retval < 0) {
- condlog(0, "error setting receive buffer size for socket, exit");
- exit(1);
- }
- condlog(3, "receive buffer size for socket is %u.", rcvsz);
-
- /* enable receiving of the sender credentials */
- if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED,
- &feature_on, sizeof(feature_on)) < 0) {
- condlog(0, "error on enabling credential passing for socket");
- exit(1);
- }
-
- retval = bind(sock, (struct sockaddr *) &snl,
- sizeof(struct sockaddr_nl));
- if (retval < 0) {
- condlog(0, "bind failed, exit");
- goto exit;
- }
- }
-
- while (1) {
- size_t bufpos;
- ssize_t buflen;
- struct uevent *uev;
- struct msghdr smsg;
- struct iovec iov;
- char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
- struct cmsghdr *cmsg;
- struct ucred *cred;
- static char buf[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE];
-
- memset(buf, 0x00, sizeof(buf));
- iov.iov_base = &buf;
- iov.iov_len = sizeof(buf);
- memset (&smsg, 0x00, sizeof(struct msghdr));
- smsg.msg_iov = &iov;
- smsg.msg_iovlen = 1;
- smsg.msg_control = cred_msg;
- smsg.msg_controllen = sizeof(cred_msg);
-
- buflen = recvmsg(sock, &smsg, 0);
- if (buflen < 0) {
- if (errno != EINTR)
- condlog(0, "error receiving message, errno %d", errno);
- continue;
- }
-
- cmsg = CMSG_FIRSTHDR(&smsg);
- if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
- condlog(3, "no sender credentials received, message ignored");
- continue;
- }
-
- cred = (struct ucred *)CMSG_DATA(cmsg);
- if (cred->uid != 0) {
- condlog(3, "sender uid=%d, message ignored", cred->uid);
- continue;
- }
-
- /* skip header */
- bufpos = strlen(buf) + 1;
- if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) {
- condlog(3, "invalid message length");
- continue;
- }
-
- /* check message header */
- if (strstr(buf, "@/") == NULL) {
- condlog(3, "unrecognized message header");
- continue;
- }
- if ((size_t)buflen > sizeof(buf)-1) {
- condlog(2, "buffer overflow for received uevent");
- buflen = sizeof(buf)-1;
- }
-
- uev = uevent_from_buffer(buf, buflen);
- if (!uev)
- continue;
- /*
- * 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);
- }
-
-exit:
- close(sock);
- return 1;
-}
-
-struct uevent *uevent_from_udev_device(struct udev_device *dev)
+static struct uevent *uevent_from_udev_device(struct udev_device *dev)
{
struct uevent *uev;
int i = 0;
return uev;
}
-bool uevent_burst(struct timeval *start_time, int events)
+static bool uevent_burst(struct timeval *start_time, int events)
{
struct timeval diff_time, end_time;
unsigned long speed;
struct udev_monitor *monitor = NULL;
int fd, socket_flags, events;
struct timeval start_time;
- int need_failback = 1;
int timeout = 30;
LIST_HEAD(uevlisten_tmp);
monitor = udev_monitor_new_from_netlink(udev, "udev");
if (!monitor) {
condlog(2, "failed to create udev monitor");
- goto failback;
+ goto out_udev;
}
pthread_cleanup_push(monitor_cleanup, monitor);
#ifdef LIBUDEV_API_RECVBUF
gettimeofday(&start_time, NULL);
timeout = 30;
}
- need_failback = 0;
out:
pthread_cleanup_pop(1);
-failback:
- if (need_failback)
- err = failback_listen();
+out_udev:
pthread_cleanup_pop(1);
return err;
}
-int uevent_get_major(const struct uevent *uev)
-{
- return uevent_get_env_positive_int(uev, "MAJOR");
-}
-
-int uevent_get_minor(const struct uevent *uev)
-{
- return uevent_get_env_positive_int(uev, "MINOR");
-}
-
-int uevent_get_disk_ro(const struct uevent *uev)
-{
- return uevent_get_env_positive_int(uev, "DISK_RO");
-}
-
-static char *uevent_get_dm_str(const struct uevent *uev, char *attr)
+char *uevent_get_dm_str(const struct uevent *uev, char *attr)
{
const char *tmp = uevent_get_env_var(uev, attr);
return strdup(tmp);
}
-char *uevent_get_dm_name(const struct uevent *uev)
-{
- return uevent_get_dm_str(uev, "DM_NAME");
-}
-
-char *uevent_get_dm_path(const struct uevent *uev)
-{
- return uevent_get_dm_str(uev, "DM_PATH");
-}
-
-char *uevent_get_dm_action(const struct uevent *uev)
-{
- return uevent_get_dm_str(uev, "DM_ACTION");
-}
-
bool uevent_is_mpath(const struct uevent *uev)
{
const char *uuid = uevent_get_env_var(uev, "DM_UUID");
#define HOTPLUG_NUM_ENVP 32
#define OBJECT_SIZE 512
-#ifndef NETLINK_KOBJECT_UEVENT
-#define NETLINK_KOBJECT_UEVENT 15
-#endif
-
struct udev;
struct uevent {
char *envp[HOTPLUG_NUM_ENVP];
};
+struct uevent *alloc_uevent(void);
int is_uevent_busy(void);
int uevent_listen(struct udev *udev);
int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data),
void * trigger_data);
-int uevent_get_major(const struct uevent *uev);
-int uevent_get_minor(const struct uevent *uev);
-int uevent_get_disk_ro(const struct uevent *uev);
-char *uevent_get_dm_name(const struct uevent *uev);
-char *uevent_get_dm_path(const struct uevent *uev);
-char *uevent_get_dm_action(const struct uevent *uev);
bool uevent_is_mpath(const struct uevent *uev);
+void uevent_get_wwid(struct uevent *uev);
+
+int uevent_get_env_positive_int(const struct uevent *uev,
+ const char *attr);
+
+static inline int uevent_get_major(const struct uevent *uev)
+{
+ return uevent_get_env_positive_int(uev, "MAJOR");
+}
+
+static inline int uevent_get_minor(const struct uevent *uev)
+{
+ return uevent_get_env_positive_int(uev, "MINOR");
+}
+
+static inline int uevent_get_disk_ro(const struct uevent *uev)
+{
+ return uevent_get_env_positive_int(uev, "DISK_RO");
+}
+
+char *uevent_get_dm_str(const struct uevent *uev, char *attr);
+
+static inline char *uevent_get_dm_name(const struct uevent *uev)
+{
+ return uevent_get_dm_str(uev, "DM_NAME");
+}
+
+static inline char *uevent_get_dm_path(const struct uevent *uev)
+{
+ return uevent_get_dm_str(uev, "DM_PATH");
+}
+
+static inline char *uevent_get_dm_action(const struct uevent *uev)
+{
+ return uevent_get_dm_str(uev, "DM_ACTION");
+}
#endif /* _UEVENT_H */
#include "checkers.h"
#include "vector.h"
#include "structs.h"
+#include "config.h"
#include "log.h"
size_t
strchop(char *str)
{
- int i;
+ size_t i;
- for (i=strlen(str)-1; i >=0 && isspace(str[i]); --i) ;
+ for (i = strlen(str) - 1; i != (size_t) -1 && isspace(str[i]); i--) ;
str[++i] = '\0';
- return strlen(str);
+ return i;
+}
+
+#ifndef __GLIBC__
+/*
+ * glibc's non-destructive version of basename()
+ * License: LGPL-2.1-or-later
+ */
+static const char *__basename(const char *filename)
+{
+ char *p = strrchr(filename, '/');
+ return p ? p + 1 : filename;
}
+#define basename(x) __basename(x)
+#endif
int
basenamecpy (const char *src, char *dst, size_t size)
}
int
-filepresent (char * run) {
+filepresent (const char *run) {
struct stat buf;
if(!stat(run, &buf))
return 0;
}
-char *get_next_string(char **temp, char *split_char)
+char *get_next_string(char **temp, const char *split_char)
{
char *token = NULL;
token = strsep(temp, split_char);
}
int
-get_word (char * sentence, char ** word)
+get_word (const char *sentence, char **word)
{
- char * p;
+ const char *p;
int len;
int skip = 0;
return skip + len;
}
-size_t strlcpy(char *dst, const char *src, size_t size)
+size_t strlcpy(char * restrict dst, const char * restrict src, size_t size)
{
size_t bytes = 0;
- char *q = dst;
- const char *p = src;
char ch;
- while ((ch = *p++)) {
- if (bytes+1 < size)
- *q++ = ch;
+ while ((ch = *src++)) {
+ if (bytes + 1 < size)
+ *dst++ = ch;
bytes++;
}
/* If size == 0 there is no space for a final null... */
if (size)
- *q = '\0';
+ *dst = '\0';
return bytes;
}
-size_t strlcat(char *dst, const char *src, size_t size)
+size_t strlcat(char * restrict dst, const char * restrict src, size_t size)
{
size_t bytes = 0;
- char *q = dst;
- const char *p = src;
char ch;
- while (bytes < size && *q) {
- q++;
+ while (bytes < size && *dst) {
+ dst++;
bytes++;
}
if (bytes == size)
return (bytes + strlen(src));
- while ((ch = *p++)) {
- if (bytes+1 < size)
- *q++ = ch;
+ while ((ch = *src++)) {
+ if (bytes + 1 < size)
+ *dst++ = ch;
bytes++;
}
- *q = '\0';
+ *dst = '\0';
return bytes;
}
-int devt2devname(char *devname, int devname_len, char *devt)
+int devt2devname(char *devname, int devname_len, const char *devt)
{
- FILE *fd;
- unsigned int tmpmaj, tmpmin, major, minor;
- char dev[FILE_NAME_SIZE];
- char block_path[PATH_SIZE];
- struct stat statbuf;
-
- memset(block_path, 0, sizeof(block_path));
- memset(dev, 0, sizeof(dev));
- if (sscanf(devt, "%u:%u", &major, &minor) != 2) {
- condlog(0, "Invalid device number %s", devt);
- return 1;
- }
-
- if (devname_len > FILE_NAME_SIZE)
- devname_len = FILE_NAME_SIZE;
-
- if (stat("/sys/dev/block", &statbuf) == 0) {
- /* Newer kernels have /sys/dev/block */
- sprintf(block_path,"/sys/dev/block/%u:%u", major, minor);
- dev[FILE_NAME_SIZE - 1] = '\0';
- if (lstat(block_path, &statbuf) == 0) {
- if (S_ISLNK(statbuf.st_mode) &&
- readlink(block_path, dev, FILE_NAME_SIZE-1) > 0) {
- char *p = strrchr(dev, '/');
-
- if (!p) {
- condlog(0, "No sysfs entry for %s",
- block_path);
- return 1;
- }
- p++;
- strlcpy(devname, p, devname_len);
- return 0;
- }
- }
- condlog(4, "%s is invalid", block_path);
- return 1;
- }
- memset(block_path, 0, sizeof(block_path));
-
- if (!(fd = fopen("/proc/partitions", "r"))) {
- condlog(0, "Cannot open /proc/partitions");
- return 1;
- }
-
- while (!feof(fd)) {
- int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev);
- if (!r) {
- r = fscanf(fd,"%*s\n");
- continue;
- }
- if (r != 3)
- continue;
-
- if ((major == tmpmaj) && (minor == tmpmin)) {
- if (safe_sprintf(block_path, "/sys/block/%s", dev)) {
- condlog(0, "device name %s is too long", dev);
- fclose(fd);
- return 1;
- }
- break;
- }
- }
- fclose(fd);
+ struct udev_device *u_dev;
+ int r;
- if (strncmp(block_path,"/sys/block", 10)) {
- condlog(3, "No device found for %u:%u", major, minor);
+ if (!devname || !devname_len || !devt)
return 1;
- }
- if (stat(block_path, &statbuf) < 0) {
- condlog(0, "No sysfs entry for %s", block_path);
+ u_dev = udev_device_new_from_devnum(udev, 'b', parse_devt(devt));
+ if (!u_dev) {
+ condlog(0, "\"%s\": invalid major/minor numbers, not found in sysfs", devt);
return 1;
}
+ r = strlcpy(devname, udev_device_get_sysname(u_dev), devname_len);
+ udev_device_unref(u_dev);
- if (S_ISDIR(statbuf.st_mode) == 0) {
- condlog(0, "sysfs entry %s is not a directory", block_path);
- return 1;
- }
- basenamecpy((const char *)block_path, devname, devname_len);
- return 0;
+ return !(r < devname_len);
}
/* This function returns a pointer inside of the supplied pathname string.
p = d->d_name + strlen(d->d_name) - 6;
if (strcmp(p, ".wants"))
continue;
- snprintf(file, sizeof(file), "%s/%s/%s",
- path, d->d_name, service);
- if (stat(file, &stbuf) == 0) {
+ if (!safe_sprintf(file, "%s/%s/%s",
+ path, d->d_name, service)
+ && stat(file, &stbuf) == 0) {
condlog(3, "%s: found %s", dev, file);
found++;
break;
return _linux_version_code;
}
-int parse_prkey(char *ptr, uint64_t *prkey)
+int parse_prkey(const char *ptr, uint64_t *prkey)
{
if (!ptr)
return 1;
return 0;
}
-int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags)
+int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags)
{
char *flagstr;
if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
condlog(0, "can't set open fds limit to "
"%lu/%lu : %s",
- fd_limit.rlim_cur, fd_limit.rlim_max,
+ (unsigned long)fd_limit.rlim_cur,
+ (unsigned long)fd_limit.rlim_max,
strerror(errno));
} else {
condlog(3, "set open fds limit to %lu/%lu",
- fd_limit.rlim_cur, fd_limit.rlim_max);
+ (unsigned long)fd_limit.rlim_cur,
+ (unsigned long)fd_limit.rlim_max);
}
}
}
{
close((long)arg);
}
+
+void cleanup_free_ptr(void *arg)
+{
+ void **p = arg;
+
+ if (p && *p)
+ free(*p);
+}
+
+struct bitfield *alloc_bitfield(unsigned int maxbit)
+{
+ unsigned int n;
+ struct bitfield *bf;
+
+ if (maxbit == 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ n = (maxbit - 1) / bits_per_slot + 1;
+ bf = calloc(1, sizeof(struct bitfield) + n * sizeof(bitfield_t));
+ if (bf)
+ bf->len = maxbit;
+ return bf;
+}
+
+void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len)
+{
+ condlog(0, "%s: bitfield overflow: %u >= %u", f, bit, len);
+}
#ifndef _UTIL_H
#define _UTIL_H
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
#include <sys/types.h>
/* for rlim_t */
#include <sys/resource.h>
size_t strchop(char *);
int basenamecpy (const char *src, char *dst, size_t size);
-int filepresent (char * run);
-char *get_next_string(char **temp, char *split_char);
-int get_word (char * sentence, char ** word);
-size_t strlcpy(char *dst, const char *src, size_t size);
-size_t strlcat(char *dst, const char *src, size_t size);
-int devt2devname (char *, int, char *);
+int filepresent (const char *run);
+char *get_next_string(char **temp, const char *split_char);
+int get_word (const char * sentence, char ** word);
+size_t strlcpy(char * restrict dst, const char * restrict src, size_t size);
+size_t strlcat(char * restrict dst, const char * restrict src, size_t size);
+int devt2devname (char *, int, const char *);
dev_t parse_devt(const char *dev_t);
char *convert_dev(char *dev, int is_path_device);
void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
int systemd_service_enabled(const char *dev);
int get_linux_version_code(void);
-int parse_prkey(char *ptr, uint64_t *prkey);
-int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags);
+int parse_prkey(const char *ptr, uint64_t *prkey);
+int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags);
int safe_write(int fd, const void *buf, size_t count);
void set_max_fds(rlim_t max_fds);
pthread_cleanup_push(((void (*)(void *))&f), (arg))
void close_fd(void *arg);
+void cleanup_free_ptr(void *arg);
struct scandir_result {
struct dirent **di;
};
void free_scandir_result(struct scandir_result *);
-static inline bool is_bit_set_in_array(unsigned int bit, const uint64_t *arr)
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(x, y) 0
+#endif
+/*
+ * ffsll() is also available on glibc < 2.27 if _GNU_SOURCE is defined.
+ * But relying on that would require that every program using this header file
+ * set _GNU_SOURCE during compilation, because otherwise the library and the
+ * program would use different types for bitfield_t, causing errors.
+ * That's too error prone, so if in doubt, use ffs().
+ */
+#if __GLIBC_PREREQ(2, 27)
+typedef unsigned long long int bitfield_t;
+#define _ffs(x) ffsll(x)
+#else
+typedef unsigned int bitfield_t;
+#define _ffs(x) ffs(x)
+#endif
+#define bits_per_slot (sizeof(bitfield_t) * CHAR_BIT)
+
+struct bitfield {
+ unsigned int len;
+ bitfield_t bits[];
+};
+
+struct bitfield *alloc_bitfield(unsigned int maxbit);
+
+void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len);
+#define log_bitfield_overflow(bit, len) \
+ _log_bitfield_overflow(__func__, bit, len)
+
+static inline bool is_bit_set_in_bitfield(unsigned int bit,
+ const struct bitfield *bf)
{
- return arr[bit / 64] & (1ULL << (bit % 64)) ? 1 : 0;
+ if (bit >= bf->len) {
+ log_bitfield_overflow(bit, bf->len);
+ return false;
+ }
+ return !!(bf->bits[bit / bits_per_slot] &
+ (1ULL << (bit % bits_per_slot)));
}
-static inline void set_bit_in_array(unsigned int bit, uint64_t *arr)
+static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
{
- arr[bit / 64] |= (1ULL << (bit % 64));
+ if (bit >= bf->len) {
+ log_bitfield_overflow(bit, bf->len);
+ return;
+ }
+ bf->bits[bit / bits_per_slot] |= (1ULL << (bit % bits_per_slot));
}
-static inline void clear_bit_in_array(unsigned int bit, uint64_t *arr)
+static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
{
- arr[bit / 64] &= ~(1ULL << (bit % 64));
+ if (bit >= bf->len) {
+ log_bitfield_overflow(bit, bf->len);
+ return;
+ }
+ bf->bits[bit / bits_per_slot] &= ~(1ULL << (bit % bits_per_slot));
}
+#define steal_ptr(x) \
+ ({ \
+ void *___p = x; \
+ x = NULL; \
+ ___p; \
+ })
+
#endif /* _UTIL_H */
--- /dev/null
+/*
+ Copyright (c) 2020 Benjamin Marzinski, IBM
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <stddef.h>
+#include <errno.h>
+#include <libudev.h>
+
+#include "vector.h"
+#include "config.h"
+#include "debug.h"
+#include "util.h"
+#include "devmapper.h"
+#include "discovery.h"
+#include "wwids.h"
+#include "sysfs.h"
+#include "blacklist.h"
+#include "mpath_cmd.h"
+#include "valid.h"
+
+int
+is_path_valid(const char *name, struct config *conf, struct path *pp,
+ bool check_multipathd)
+{
+ int r;
+ int fd;
+
+ if (!pp || !name || !conf)
+ return PATH_IS_ERROR;
+
+ if (conf->find_multipaths <= FIND_MULTIPATHS_UNDEF ||
+ conf->find_multipaths >= __FIND_MULTIPATHS_LAST)
+ return PATH_IS_ERROR;
+
+ if (safe_sprintf(pp->dev, "%s", name))
+ return PATH_IS_ERROR;
+
+ if (sysfs_is_multipathed(pp, true)) {
+ if (pp->wwid[0] == '\0')
+ return PATH_IS_ERROR;
+ return PATH_IS_VALID_NO_CHECK;
+ }
+
+ /*
+ * "multipath -u" may be run before the daemon is started. In this
+ * case, systemd might own the socket but might delay multipathd
+ * startup until some other unit (udev settle!) has finished
+ * starting. With many LUNs, the listen backlog may be exceeded, which
+ * would cause connect() to block. This causes udev workers calling
+ * "multipath -u" to hang, and thus creates a deadlock, until "udev
+ * settle" times out. To avoid this, call connect() in non-blocking
+ * mode here, and take EAGAIN as indication for a filled-up systemd
+ * backlog.
+ */
+
+ if (check_multipathd) {
+ fd = __mpath_connect(1);
+ if (fd < 0) {
+ if (errno != EAGAIN && !systemd_service_enabled(name)) {
+ condlog(3, "multipathd not running or enabled");
+ return PATH_IS_NOT_VALID;
+ }
+ } else
+ mpath_disconnect(fd);
+ }
+
+ pp->udev = udev_device_new_from_subsystem_sysname(udev, "block", name);
+ if (!pp->udev)
+ return PATH_IS_ERROR;
+
+ r = pathinfo(pp, conf, DI_SYSFS | DI_WWID | DI_BLACKLIST);
+ if (r == PATHINFO_SKIPPED)
+ return PATH_IS_NOT_VALID;
+ else if (r)
+ return PATH_IS_ERROR;
+
+ if (pp->wwid[0] == '\0')
+ return PATH_IS_NOT_VALID;
+
+ if (pp->udev && pp->uid_attribute &&
+ filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
+ return PATH_IS_NOT_VALID;
+
+ r = is_failed_wwid(pp->wwid);
+ if (r != WWID_IS_NOT_FAILED) {
+ if (r == WWID_IS_FAILED)
+ return PATH_IS_NOT_VALID;
+ return PATH_IS_ERROR;
+ }
+
+ if (conf->find_multipaths == FIND_MULTIPATHS_GREEDY)
+ return PATH_IS_VALID;
+
+ if (check_wwids_file(pp->wwid, 0) == 0)
+ return PATH_IS_VALID_NO_CHECK;
+
+ if (dm_map_present_by_uuid(pp->wwid) == 1)
+ return PATH_IS_VALID;
+
+ /* all these act like FIND_MULTIPATHS_STRICT for finding if a
+ * path is valid */
+ if (conf->find_multipaths != FIND_MULTIPATHS_SMART)
+ return PATH_IS_NOT_VALID;
+
+ return PATH_IS_MAYBE_VALID;
+}
--- /dev/null
+/*
+ Copyright (c) 2020 Benjamin Marzinski, IBM
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef _VALID_H
+#define _VALID_H
+
+/*
+ * PATH_IS_VALID_NO_CHECK is returned when multipath should claim
+ * the path, regardless of whether is has been released to systemd
+ * already.
+ * PATH_IS_VALID is returned by is_path_valid, when the path is
+ * valid only if it hasn't been released to systemd already.
+ * PATH_IS_MAYBE_VALID is returned when the the path would be valid
+ * if other paths with the same wwid existed. It is up to the caller
+ * to check for these other paths.
+ */
+enum is_path_valid_result {
+ PATH_IS_ERROR = -1,
+ PATH_IS_NOT_VALID,
+ PATH_IS_VALID,
+ PATH_IS_VALID_NO_CHECK,
+ PATH_IS_MAYBE_VALID,
+ PATH_MAX_VALID_RESULT, /* only for bounds checking */
+};
+
+int is_path_valid(const char *name, struct config *conf, struct path *pp,
+ bool check_multipathd);
+
+#endif /* _VALID_D */
}
/* allocated one slot */
-void *
+bool
vector_alloc_slot(vector v)
{
void *new_slot = NULL;
+ int new_allocated;
+ int i;
if (!v)
- return NULL;
-
- v->allocated += VECTOR_DEFAULT_SIZE;
- if (v->slot)
- new_slot = REALLOC(v->slot, sizeof (void *) * v->allocated);
- else
- new_slot = (void *) MALLOC(sizeof (void *) * v->allocated);
+ return false;
+ new_allocated = v->allocated + VECTOR_DEFAULT_SIZE;
+ new_slot = REALLOC(v->slot, sizeof (void *) * new_allocated);
if (!new_slot)
- v->allocated -= VECTOR_DEFAULT_SIZE;
- else
- v->slot = new_slot;
+ return false;
- return v->slot;
+ v->slot = new_slot;
+ for (i = v->allocated; i < new_allocated; i++)
+ v->slot[i] = NULL;
+
+ v->allocated = new_allocated;
+ return true;
}
int
{
int i;
- if (!v || !v->allocated || slot < 0 || slot > VECTOR_SIZE(v))
+ if (!v || !v->allocated || slot < 0 || slot >= VECTOR_SIZE(v))
return;
for (i = slot + 1; i < VECTOR_SIZE(v); i++)
if (n >= 0)
return n;
- if (vector_alloc_slot(v) == NULL)
+ if (!vector_alloc_slot(v))
return -1;
vector_set_slot(v, value);
return VECTOR_SIZE(v) - 1;
#ifndef _VECTOR_H
#define _VECTOR_H
+#include <stdbool.h>
+
/* vector definition */
struct _vector {
int allocated;
#define VECTOR_DEFAULT_SIZE 1
#define VECTOR_SIZE(V) ((V) ? ((V)->allocated) / VECTOR_DEFAULT_SIZE : 0)
-#define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V)) ? (V)->slot[(E)] : NULL)
+#define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V) && (E) >= 0) ? (V)->slot[(E)] : NULL)
#define VECTOR_LAST_SLOT(V) (((V) && VECTOR_SIZE(V) > 0) ? (V)->slot[(VECTOR_SIZE(V) - 1)] : NULL)
#define vector_foreach_slot(v,p,i) \
__t = vector_alloc(); \
if (__t != NULL) { \
vector_foreach_slot(__v, __j, __i) { \
- if (vector_alloc_slot(__t) == NULL) { \
+ if (!vector_alloc_slot(__t)) { \
vector_free(__t); \
__t = NULL; \
break; \
/* Prototypes */
extern vector vector_alloc(void);
-extern void *vector_alloc_slot(vector v);
+extern bool vector_alloc_slot(vector v);
vector vector_reset(vector v);
extern void vector_free(vector v);
#define vector_free_const(x) vector_free((vector)(long)(x))
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000804
-#define DATE_CODE 0x050414
+#define VERSION_CODE 0x000805
+#define DATE_CODE 0x0b0914
#define PROG "multipath-tools"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include "util.h"
#include "checkers.h"
int
should_multipath(struct path *pp1, vector pathvec, vector mpvec)
{
- int i, ignore_new_devs, find_multipaths;
+ int i, find_multipaths;
struct path *pp2;
struct config *conf;
conf = get_multipath_config();
- ignore_new_devs = ignore_new_devs_on(conf);
- find_multipaths = find_multipaths_on(conf);
+ find_multipaths = conf->find_multipaths;
put_multipath_config(conf);
- if (!find_multipaths && !ignore_new_devs)
+ if (find_multipaths == FIND_MULTIPATHS_OFF ||
+ find_multipaths == FIND_MULTIPATHS_GREEDY)
return 1;
condlog(4, "checking if %s should be multipathed", pp1->dev);
- if (!ignore_new_devs) {
+ if (find_multipaths != FIND_MULTIPATHS_STRICT) {
char tmp_wwid[WWID_SIZE];
struct multipath *mp = find_mp_by_wwid(mpvec, pp1->wwid);
}
static const char shm_dir[] = MULTIPATH_SHM_BASE "failed_wwids";
-static const char shm_lock[] = ".lock";
-static const char shm_header[] = "multipath shm lock file, don't edit";
-static char _shm_lock_path[sizeof(shm_dir)+sizeof(shm_lock)];
-static const char *shm_lock_path = &_shm_lock_path[0];
-static void init_shm_paths(void)
+static void print_failed_wwid_result(const char * msg, const char *wwid, int r)
{
- snprintf(_shm_lock_path, sizeof(_shm_lock_path),
- "%s/%s", shm_dir, shm_lock);
+ switch(r) {
+ case WWID_FAILED_ERROR:
+ condlog(1, "%s: %s: %m", msg, wwid);
+ return;
+ case WWID_IS_FAILED:
+ case WWID_IS_NOT_FAILED:
+ condlog(4, "%s: %s is %s", msg, wwid,
+ r == WWID_IS_FAILED ? "failed" : "good");
+ return;
+ case WWID_FAILED_CHANGED:
+ condlog(3, "%s: %s", msg, wwid);
+ }
}
-static pthread_once_t shm_path_once = PTHREAD_ONCE_INIT;
-
-static int multipath_shm_open(bool rw)
+int is_failed_wwid(const char *wwid)
{
- int fd;
- int can_write;
-
- pthread_once(&shm_path_once, init_shm_paths);
- fd = open_file(shm_lock_path, &can_write, shm_header);
+ struct stat st;
+ char path[PATH_MAX];
+ int r;
- if (fd >= 0 && rw && !can_write) {
- close(fd);
- condlog(1, "failed to open %s for writing", shm_dir);
- return -1;
+ if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
+ condlog(1, "%s: path name overflow", __func__);
+ return WWID_FAILED_ERROR;
}
- return fd;
-}
-
-static void multipath_shm_close(void *arg)
-{
- long fd = (long)arg;
+ if (lstat(path, &st) == 0)
+ r = WWID_IS_FAILED;
+ else if (errno == ENOENT)
+ r = WWID_IS_NOT_FAILED;
+ else
+ r = WWID_FAILED_ERROR;
- close(fd);
- unlink(shm_lock_path);
+ print_failed_wwid_result("is_failed", wwid, r);
+ return r;
}
-static int _failed_wwid_op(const char *wwid, bool rw,
- int (*func)(const char *), const char *msg)
+int mark_failed_wwid(const char *wwid)
{
- char path[PATH_MAX];
- long lockfd;
- int r = -1;
+ char tmpfile[WWID_SIZE + 2 * sizeof(long) + 1];
+ int r = WWID_FAILED_ERROR, fd, dfd;
- if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
- condlog(1, "%s: path name overflow", __func__);
- return -1;
+ dfd = open(shm_dir, O_RDONLY|O_DIRECTORY);
+ if (dfd == -1 && errno == ENOENT) {
+ char path[sizeof(shm_dir) + 2];
+
+ /* arg for ensure_directories_exist() must not end with "/" */
+ safe_sprintf(path, "%s/_", shm_dir);
+ ensure_directories_exist(path, 0700);
+ dfd = open(shm_dir, O_RDONLY|O_DIRECTORY);
+ }
+ if (dfd == -1) {
+ condlog(1, "%s: can't setup %s: %m", __func__, shm_dir);
+ return WWID_FAILED_ERROR;
}
- lockfd = multipath_shm_open(rw);
- if (lockfd == -1)
- return -1;
+ safe_sprintf(tmpfile, "%s.%lx", wwid, (long)getpid());
+ fd = openat(dfd, tmpfile, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR);
+ if (fd >= 0)
+ close(fd);
+ else
+ goto out_closedir;
- pthread_cleanup_push(multipath_shm_close, (void *)lockfd);
- r = func(path);
- pthread_cleanup_pop(1);
+ if (linkat(dfd, tmpfile, dfd, wwid, 0) == 0)
+ r = WWID_FAILED_CHANGED;
+ else if (errno == EEXIST)
+ r = WWID_FAILED_UNCHANGED;
+ else
+ r = WWID_FAILED_ERROR;
- if (r == WWID_FAILED_ERROR)
- condlog(1, "%s: %s: %s", msg, wwid, strerror(errno));
- else if (r == WWID_FAILED_CHANGED)
- condlog(3, "%s: %s", msg, wwid);
- else if (!rw)
- condlog(4, "%s: %s is %s", msg, wwid,
- r == WWID_IS_FAILED ? "failed" : "good");
+ if (unlinkat(dfd, tmpfile, 0) == -1)
+ condlog(2, "%s: failed to unlink %s/%s: %m",
+ __func__, shm_dir, tmpfile);
+out_closedir:
+ close(dfd);
+ print_failed_wwid_result("mark_failed", wwid, r);
return r;
}
-static int _is_failed(const char *path)
+int unmark_failed_wwid(const char *wwid)
{
- struct stat st;
+ char path[PATH_MAX];
+ int r;
- if (lstat(path, &st) == 0)
- return WWID_IS_FAILED;
- else if (errno == ENOENT)
- return WWID_IS_NOT_FAILED;
- else
+ if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
+ condlog(1, "%s: path name overflow", __func__);
return WWID_FAILED_ERROR;
-}
-
-static int _mark_failed(const char *path)
-{
- /* Called from _failed_wwid_op: we know that shm_lock_path exists */
- if (_is_failed(path) == WWID_IS_FAILED)
- return WWID_FAILED_UNCHANGED;
- return (link(shm_lock_path, path) == 0 ? WWID_FAILED_CHANGED :
- WWID_FAILED_ERROR);
-}
+ }
-static int _unmark_failed(const char *path)
-{
- if (_is_failed(path) == WWID_IS_NOT_FAILED)
- return WWID_FAILED_UNCHANGED;
- return (unlink(path) == 0 ? WWID_FAILED_CHANGED : WWID_FAILED_ERROR);
-}
+ if (unlink(path) == 0)
+ r = WWID_FAILED_CHANGED;
+ else if (errno == ENOENT)
+ r = WWID_FAILED_UNCHANGED;
+ else
+ r = WWID_FAILED_ERROR;
-#define declare_failed_wwid_op(op, rw) \
-int op ## _wwid(const char *wwid) \
-{ \
- return _failed_wwid_op(wwid, (rw), _ ## op, #op); \
+ print_failed_wwid_result("unmark_failed", wwid, r);
+ return r;
}
-
-declare_failed_wwid_op(is_failed, false)
-declare_failed_wwid_op(mark_failed, true)
-declare_failed_wwid_op(unmark_failed, true)
return ret;
}
+static struct prout_param_descriptor *
+alloc_prout_param_descriptor(int num_transportid)
+{
+ struct prout_param_descriptor *paramp;
+
+ if (num_transportid < 0 || num_transportid > MPATH_MX_TIDS)
+ return NULL;
+
+ paramp= malloc(sizeof(struct prout_param_descriptor) +
+ (sizeof(struct transportid *) * num_transportid));
+
+ if (!paramp)
+ return NULL;
+
+ memset(paramp, 0, sizeof(struct prout_param_descriptor) +
+ (sizeof(struct transportid *) * num_transportid));
+ return paramp;
+}
+
+static void free_prout_param_descriptor(struct prout_param_descriptor *paramp)
+{
+ unsigned int i;
+ if (!paramp)
+ return;
+
+ for (i = 0; i < paramp->num_transportid; i++)
+ free(paramp->trnptid_list[i]);
+
+ free(paramp);
+}
+
static int handle_args(int argc, char * argv[], int nline)
{
int c;
int prin = 1;
int prin_sa = -1;
int prout_sa = -1;
- int num_transport =0;
char *batch_fn = NULL;
void *resp = NULL;
- struct transportid * tmp;
memset(transportids, 0, MPATH_MX_TIDS * sizeof(struct transportid));
break;
case 'X':
- if (0 != construct_transportid(optarg, transportids, num_transport)) {
+ if (0 != construct_transportid(optarg, transportids, num_transportids)) {
fprintf(stderr, "bad argument to '--transport-id'\n");
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
- ++num_transport;
+ ++num_transportids;
break;
case 'l':
int j;
struct prout_param_descriptor *paramp;
- paramp= malloc(sizeof(struct prout_param_descriptor) + (sizeof(struct transportid *)*(MPATH_MX_TIDS )));
-
- memset(paramp, 0, sizeof(struct prout_param_descriptor) + (sizeof(struct transportid *)*(MPATH_MX_TIDS)));
+ paramp = alloc_prout_param_descriptor(num_transportids);
+ if (!paramp) {
+ fprintf(stderr, "malloc paramp failed\n");
+ ret = MPATH_PR_OTHER;
+ goto out_fd;
+ }
for (j = 7; j >= 0; --j) {
paramp->key[j] = (param_rk & 0xff);
if (param_aptpl)
paramp->sa_flags |= MPATH_F_APTPL_MASK;
- if (num_transport)
+ if (num_transportids)
{
paramp->sa_flags |= MPATH_F_SPEC_I_PT_MASK;
- paramp->num_transportid = num_transport;
- for (j = 0 ; j < num_transport; j++)
+ paramp->num_transportid = num_transportids;
+ for (j = 0 ; j < num_transportids; j++)
{
paramp->trnptid_list[j] = (struct transportid *)malloc(sizeof(struct transportid));
+ if (!paramp->trnptid_list[j]) {
+ fprintf(stderr, "malloc paramp->trnptid_list[%d] failed.\n", j);
+ ret = MPATH_PR_OTHER;
+ free_prout_param_descriptor(paramp);
+ goto out_fd;
+ }
memcpy(paramp->trnptid_list[j], &transportids[j],sizeof(struct transportid));
}
}
/* PROUT commands other than 'register and move' */
ret = __mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type,
paramp, noisy);
- for (j = 0 ; j < num_transport; j++)
- {
- tmp = paramp->trnptid_list[j];
- free(tmp);
- }
- free(paramp);
+ free_prout_param_descriptor(paramp);
}
if (ret != MPATH_PR_SUCCESS)
ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0",\
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"
+ ENV{DM_ACTIVATION}="1", ENV{MPATH_UNCHANGED}="0"
# The code to check multipath state ends here. We need to set
# properties and symlinks regardless whether the map is usable or
#include "propsel.h"
#include "time-util.h"
#include "file.h"
+#include "valid.h"
+#include "alias.h"
int logsink;
struct udev *udev;
struct config *multipath_conf;
/*
- * Return values of configure(), print_cmd_valid(), and main().
- * RTVL_{YES,NO} are synonyms for RTVL_{OK,FAIL} for the CMD_VALID_PATH case.
+ * Return values of configure(), check_path_valid(), and main().
*/
enum {
RTVL_OK = 0,
- RTVL_YES = RTVL_OK,
RTVL_FAIL = 1,
- RTVL_NO = RTVL_FAIL,
- RTVL_MAYBE, /* only used internally, never returned */
RTVL_RETRY, /* returned by configure(), not by main() */
};
" -h print this usage text\n"
" -l show multipath topology (sysfs and DM info)\n"
" -ll show multipath topology (maximum info)\n"
+ " -e enable foreign libraries with -l/-ll\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"
}
static int
-update_paths (struct multipath * mpp, int quick)
-{
- int i, j;
- struct pathgroup * pgp;
- struct path * pp;
- struct config *conf;
-
- if (!mpp->pg)
- return 0;
-
- vector_foreach_slot (mpp->pg, pgp, i) {
- if (!pgp->paths)
- continue;
-
- vector_foreach_slot (pgp->paths, pp, j) {
- if (!strlen(pp->dev)) {
- if (devt2devname(pp->dev, FILE_NAME_SIZE,
- pp->dev_t)) {
- /*
- * path is not in sysfs anymore
- */
- pp->chkrstate = pp->state = PATH_DOWN;
- pp->offline = 1;
- continue;
- }
- pp->mpp = mpp;
- if (quick)
- continue;
- conf = get_multipath_config();
- if (pathinfo(pp, conf, DI_ALL))
- pp->state = PATH_UNCHECKED;
- put_multipath_config(conf);
- continue;
- }
- pp->mpp = mpp;
- if (quick)
- continue;
- if (pp->state == PATH_UNCHECKED ||
- pp->state == PATH_WILD) {
- conf = get_multipath_config();
- if (pathinfo(pp, conf, DI_CHECKER))
- pp->state = PATH_UNCHECKED;
- put_multipath_config(conf);
- }
-
- if (pp->priority == PRIO_UNDEF) {
- conf = get_multipath_config();
- if (pathinfo(pp, conf, DI_PRIO))
- pp->priority = PRIO_UNDEF;
- put_multipath_config(conf);
- }
- }
- }
- return 0;
-}
-
-static int
get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
{
int i;
struct multipath * mpp;
- char params[PARAMS_SIZE], status[PARAMS_SIZE];
+ int flags = (cmd == CMD_LIST_SHORT ? DI_NOIO : DI_ALL);
if (dm_get_maps(curmp))
return 1;
if (refwwid && strlen(refwwid) &&
strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
condlog(3, "skip map %s: out of scope", mpp->alias);
- free_multipath(mpp, KEEP_PATHS);
- vector_del_slot(curmp, i);
+ remove_map(mpp, pathvec, curmp, PURGE_VEC);
i--;
continue;
}
- if (cmd == CMD_VALID_PATH)
+ if (update_multipath_table(mpp, pathvec, flags) != DMP_OK ||
+ update_multipath_status(mpp) != DMP_OK) {
+ condlog(1, "error parsing map %s", mpp->wwid);
+ remove_map(mpp, pathvec, curmp, PURGE_VEC);
+ i--;
continue;
-
- dm_get_map(mpp->alias, &mpp->size, params);
- condlog(3, "params = %s", params);
- dm_get_status(mpp->alias, status);
- condlog(3, "status = %s", status);
-
- disassemble_map(pathvec, params, mpp, 0);
-
- /*
- * disassemble_map() can add new paths to pathvec.
- * If not in "fast list mode", we need to fetch information
- * about them
- */
- update_paths(mpp, (cmd == CMD_LIST_SHORT));
+ }
if (cmd == CMD_LIST_LONG)
mpp->bestpg = select_path_group(mpp);
- disassemble_status(status, mpp);
-
if (cmd == CMD_LIST_SHORT ||
cmd == CMD_LIST_LONG) {
struct config *conf = get_multipath_config();
struct path *pp;
char *mapname;
vector pathvec = NULL;
- char params[PARAMS_SIZE], status[PARAMS_SIZE];
dev_t devt;
int r = 1, i, j;
if (mpp == NULL)
goto free;
- dm_get_map(mpp->alias, &mpp->size, params);
- dm_get_status(mpp->alias, status);
- disassemble_map(pathvec, params, mpp, 0);
- disassemble_status(status, mpp);
+ if (update_multipath_table(mpp, pathvec, 0) != DMP_OK ||
+ update_multipath_status(mpp) != DMP_OK)
+ goto free;
vector_foreach_slot (mpp->pg, pg, i) {
vector_foreach_slot (pg->paths, pp, j) {
struct timespec until;
struct path *pp;
- if (k != RTVL_YES && k != RTVL_NO && k != RTVL_MAYBE)
- return RTVL_NO;
+ if (k != PATH_IS_VALID && k != PATH_IS_NOT_VALID &&
+ k != PATH_IS_MAYBE_VALID)
+ return PATH_IS_NOT_VALID;
- if (k == RTVL_MAYBE) {
+ if (k == PATH_IS_MAYBE_VALID) {
/*
* Caller ensures that pathvec[0] is the path to
* examine.
wait = find_multipaths_check_timeout(
pp, pp->find_multipaths_timeout, &until);
if (wait != FIND_MULTIPATHS_WAITING)
- k = RTVL_NO;
+ k = PATH_IS_NOT_VALID;
} else if (pathvec != NULL && (pp = VECTOR_SLOT(pathvec, 0)))
wait = find_multipaths_check_timeout(pp, 0, &until);
if (wait == FIND_MULTIPATHS_WAITING)
else if (wait == FIND_MULTIPATHS_WAIT_DONE)
printf("FIND_MULTIPATHS_WAIT_UNTIL=\"0\"\n");
printf("DM_MULTIPATH_DEVICE_PATH=\"%d\"\n",
- k == RTVL_MAYBE ? 2 : k == RTVL_YES ? 1 : 0);
+ k == PATH_IS_MAYBE_VALID ? 2 : k == PATH_IS_VALID ? 1 : 0);
/* Never return RTVL_MAYBE */
- return k == RTVL_NO ? RTVL_NO : RTVL_YES;
+ return k == PATH_IS_NOT_VALID ? PATH_IS_NOT_VALID : PATH_IS_VALID;
}
/*
int di_flag = 0;
char * refwwid = NULL;
char * dev = NULL;
- bool released = released_to_systemd();
/*
* allocate core vectors to store paths and multipaths
cmd != CMD_REMOVE_WWID &&
(filter_devnode(conf->blist_devnode,
conf->elist_devnode, dev) > 0)) {
- goto print_valid;
+ goto out;
}
/*
* failing the translation is fatal (by policy)
*/
if (devpath) {
- int failed = get_refwwid(cmd, devpath, dev_type,
- pathvec, &refwwid);
+ get_refwwid(cmd, devpath, dev_type, pathvec, &refwwid);
if (!refwwid) {
condlog(4, "%s: failed to get wwid", devpath);
- if (failed == 2 && cmd == CMD_VALID_PATH)
- goto print_valid;
- else
- condlog(3, "scope is null");
+ condlog(3, "scope is null");
goto out;
}
if (cmd == CMD_REMOVE_WWID) {
goto out;
}
condlog(3, "scope limited to %s", refwwid);
- /* 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.
- * Paths listed in the wwids file are always considered valid.
- */
- if (cmd == CMD_VALID_PATH) {
- if (is_failed_wwid(refwwid) == WWID_IS_FAILED) {
- r = RTVL_NO;
- goto print_valid;
- }
- if ((!find_multipaths_on(conf) &&
- ignore_wwids_on(conf)) ||
- check_wwids_file(refwwid, 0) == 0)
- r = RTVL_YES;
- if (!ignore_wwids_on(conf))
- goto print_valid;
- /* At this point, either r==0 or find_multipaths_on. */
-
- /*
- * Shortcut for find_multipaths smart:
- * Quick check if path is already multipathed.
- */
- if (sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0))) {
- r = RTVL_YES;
- goto print_valid;
- }
-
- /*
- * DM_MULTIPATH_DEVICE_PATH=="0" means that we have
- * been called for this device already, and have
- * released it to systemd. Unless the device is now
- * already multipathed (see above), we can't try to
- * grab it, because setting SYSTEMD_READY=0 would
- * cause file systems to be unmounted.
- * Leave DM_MULTIPATH_DEVICE_PATH="0".
- */
- if (released) {
- r = RTVL_NO;
- goto print_valid;
- }
- if (r == RTVL_YES)
- goto print_valid;
- /* find_multipaths_on: Fall through to path detection */
- }
}
/*
goto out;
}
- if (cmd == CMD_VALID_PATH) {
- struct path *pp;
- int fd;
-
- /* This only happens if find_multipaths and
- * ignore_wwids is set, and the path is not in WWIDs
- * file, not currently multipathed, and has
- * never been released to systemd.
- * 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) {
- r = RTVL_YES;
- goto print_valid;
- } else if (VECTOR_SIZE(pathvec) > 1)
- r = RTVL_YES;
- else
- r = RTVL_MAYBE;
-
- /*
- * If opening the path with O_EXCL fails, the path
- * is in use (e.g. mounted during initramfs processing).
- * We know that it's not used by dm-multipath.
- * We may not set SYSTEMD_READY=0 on such devices, it
- * might cause systemd to umount the device.
- * Use O_RDONLY, because udevd would trigger another
- * uevent for close-after-write.
- *
- * The O_EXCL check is potentially dangerous, because it may
- * race with other tasks trying to access the device. Therefore
- * this code is only executed if the path hasn't been released
- * to systemd earlier (see above).
- *
- * get_refwwid() above stores the path we examine in slot 0.
- */
- pp = VECTOR_SLOT(pathvec, 0);
- fd = open(udev_device_get_devnode(pp->udev),
- O_RDONLY|O_EXCL);
- if (fd >= 0)
- close(fd);
- else {
- condlog(3, "%s: path %s is in use: %s",
- __func__, pp->dev,
- strerror(errno));
- /*
- * Check if we raced with multipathd
- */
- r = sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0)) ?
- RTVL_YES : RTVL_NO;
- }
- goto print_valid;
- }
-
if (cmd != CMD_CREATE && cmd != CMD_DRY_RUN) {
r = RTVL_OK;
goto out;
conf->force_reload, cmd);
r = rc == CP_RETRY ? RTVL_RETRY : rc == CP_OK ? RTVL_OK : RTVL_FAIL;
-print_valid:
- if (cmd == CMD_VALID_PATH)
- r = print_cmd_valid(r, pathvec, conf);
-
out:
if (refwwid)
FREE(refwwid);
}
static int
+check_path_valid(const char *name, struct config *conf, bool is_uevent)
+{
+ int fd, r = PATH_IS_ERROR;
+ struct path *pp = NULL;
+ vector pathvec = NULL;
+
+ pp = alloc_path();
+ if (!pp)
+ return RTVL_FAIL;
+
+ r = is_path_valid(name, conf, pp, is_uevent);
+ if (r <= PATH_IS_ERROR || r >= PATH_MAX_VALID_RESULT)
+ goto fail;
+
+ /* set path values if is_path_valid() didn't */
+ if (!pp->udev)
+ pp->udev = udev_device_new_from_subsystem_sysname(udev, "block",
+ name);
+ if (!pp->udev)
+ goto fail;
+
+ if (!strlen(pp->dev_t)) {
+ dev_t devt = udev_device_get_devnum(pp->udev);
+ if (major(devt) == 0 && minor(devt) == 0)
+ goto fail;
+ snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt),
+ minor(devt));
+ }
+
+ if ((r == PATH_IS_VALID || r == PATH_IS_MAYBE_VALID) &&
+ released_to_systemd())
+ r = PATH_IS_NOT_VALID;
+
+ /* This state is only used to skip the released_to_systemd() check */
+ if (r == PATH_IS_VALID_NO_CHECK)
+ r = PATH_IS_VALID;
+
+ if (r != PATH_IS_MAYBE_VALID)
+ goto out;
+
+ /*
+ * If opening the path with O_EXCL fails, the path
+ * is in use (e.g. mounted during initramfs processing).
+ * We know that it's not used by dm-multipath.
+ * We may not set SYSTEMD_READY=0 on such devices, it
+ * might cause systemd to umount the device.
+ * Use O_RDONLY, because udevd would trigger another
+ * uevent for close-after-write.
+ *
+ * The O_EXCL check is potentially dangerous, because it may
+ * race with other tasks trying to access the device. Therefore
+ * this code is only executed if the path hasn't been released
+ * to systemd earlier (see above).
+ */
+ fd = open(udev_device_get_devnode(pp->udev), O_RDONLY|O_EXCL);
+ if (fd >= 0)
+ close(fd);
+ else {
+ condlog(3, "%s: path %s is in use: %m", __func__, pp->dev);
+ /* Check if we raced with multipathd */
+ if (sysfs_is_multipathed(pp, false))
+ r = PATH_IS_VALID;
+ else
+ r = PATH_IS_NOT_VALID;
+ goto out;
+ }
+
+ pathvec = vector_alloc();
+ if (!pathvec)
+ goto fail;
+
+ if (store_path(pathvec, pp) != 0) {
+ free_path(pp);
+ goto fail;
+ }
+
+ /* For find_multipaths = SMART, if there is more than one path
+ * matching the refwwid, then the path is valid */
+ if (path_discovery(pathvec, DI_SYSFS | DI_WWID) < 0)
+ goto fail;
+ filter_pathvec(pathvec, pp->wwid);
+ if (VECTOR_SIZE(pathvec) > 1)
+ r = PATH_IS_VALID;
+ else
+ r = PATH_IS_MAYBE_VALID;
+
+out:
+ r = print_cmd_valid(r, pathvec, conf);
+ free_pathvec(pathvec, FREE_PATHS);
+ /*
+ * multipath -u must exit with status 0, otherwise udev won't
+ * import its output.
+ */
+ if (!is_uevent && r == PATH_IS_NOT_VALID)
+ return RTVL_FAIL;
+ return RTVL_OK;
+
+fail:
+ if (pathvec)
+ free_pathvec(pathvec, FREE_PATHS);
+ else
+ free_path(pp);
+ return RTVL_FAIL;
+}
+
+static int
get_dev_type(char *dev) {
struct stat buf;
int i;
*p = '\0';
n = sizeof(command);
+ if (conf->skip_delegate)
+ return NOT_DELEGATED;
+
if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) {
p += snprintf(p, n, "reconfigure");
}
+ else if (cmd == CMD_FLUSH_ONE && dev && dev_type == DEV_DEVMAP) {
+ p += snprintf(p, n, "del map %s", dev);
+ /* multipathd doesn't try as hard, to avoid potentially
+ * hanging. If it fails, retry with the regular multipath
+ * command */
+ r = NOT_DELEGATED;
+ }
+ else if (cmd == CMD_FLUSH_ALL) {
+ p += snprintf(p, n, "del maps");
+ /* multipathd doesn't try as hard, to avoid potentially
+ * hanging. If it fails, retry with the regular multipath
+ * command */
+ r = NOT_DELEGATED;
+ }
/* Add other translations here */
if (strlen(command) == 0)
goto out;
}
- if (reply != NULL && *reply != '\0' && strcmp(reply, "ok\n"))
- printf("%s", reply);
- r = DELEGATE_OK;
+ if (reply != NULL && *reply != '\0') {
+ if (strcmp(reply, "fail\n"))
+ r = DELEGATE_OK;
+ if (r != NOT_DELEGATED && strcmp(reply, "ok\n"))
+ printf("%s", reply);
+ }
out:
FREE(reply);
return r;
}
-static int test_multipathd_socket(void)
-{
- int fd;
- /*
- * "multipath -u" may be run before the daemon is started. In this
- * case, systemd might own the socket but might delay multipathd
- * startup until some other unit (udev settle!) has finished
- * starting. With many LUNs, the listen backlog may be exceeded, which
- * would cause connect() to block. This causes udev workers calling
- * "multipath -u" to hang, and thus creates a deadlock, until "udev
- * settle" times out. To avoid this, call connect() in non-blocking
- * mode here, and take EAGAIN as indication for a filled-up systemd
- * backlog.
- */
-
- fd = __mpath_connect(1);
- if (fd == -1) {
- if (errno == EAGAIN)
- condlog(3, "daemon backlog exceeded");
- else
- return 0;
- } else
- close(fd);
- return 1;
-}
-
int
main (int argc, char *argv[])
{
char *dev = NULL;
struct config *conf;
int retries = -1;
+ bool enable_foreign = false;
udev = udev_new();
logsink = 0;
multipath_conf = conf;
conf->retrigger_tries = 0;
conf->force_sync = 1;
- while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) {
+ while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
switch(arg) {
case 1: printf("optarg : %s\n",optarg);
break;
if (cmd == CMD_CREATE)
cmd = CMD_DRY_RUN;
break;
+ case 'D':
+ conf->skip_delegate = 1;
+ break;
case 'f':
- conf->remove = FLUSH_ONE;
+ cmd = CMD_FLUSH_ONE;
break;
case 'F':
- conf->remove = FLUSH_ALL;
+ cmd = CMD_FLUSH_ALL;
break;
case 'l':
if (optarg && !strncmp(optarg, "l", 1))
conf->force_reload = FORCE_RELOAD_YES;
break;
case 'i':
- conf->find_multipaths |= _FIND_MULTIPATHS_I;
+ if (conf->find_multipaths == FIND_MULTIPATHS_ON ||
+ conf->find_multipaths == FIND_MULTIPATHS_STRICT)
+ conf->find_multipaths = FIND_MULTIPATHS_SMART;
+ else if (conf->find_multipaths == FIND_MULTIPATHS_OFF)
+ conf->find_multipaths = FIND_MULTIPATHS_GREEDY;
break;
case 't':
r = dump_config(conf, NULL, NULL) ? RTVL_FAIL : RTVL_OK;
case 'R':
retries = atoi(optarg);
break;
+ case 'e':
+ enable_foreign = true;
+ break;
case ':':
fprintf(stderr, "Missing option argument\n");
usage(argv[0]);
exit(RTVL_FAIL);
}
+ check_alias_settings(conf);
+
if (optind < argc) {
dev = MALLOC(FILE_NAME_SIZE);
condlog(0, "'%s' is not a valid argument\n", dev);
goto out;
}
+ if (dev_type == DEV_DEVNODE || dev_type == DEV_DEVT)
+ strchop(dev);
}
if (dev_type == DEV_UEVENT) {
openlog("multipath", 0, LOG_DAEMON);
condlog(0, "failed to initialize prioritizers");
goto out;
}
+
+ if ((cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG) && enable_foreign)
+ conf->enable_foreign = strdup("");
+
/* Failing here is non-fatal */
init_foreign(conf->multipath_dir, conf->enable_foreign);
if (cmd == CMD_USABLE_PATHS) {
condlog(0, "the -c option requires a path to check");
goto out;
}
- if (cmd == CMD_VALID_PATH &&
- dev_type == DEV_UEVENT) {
- if (!test_multipathd_socket()) {
- condlog(3, "%s: daemon is not running", dev);
- if (!systemd_service_enabled(dev)) {
- r = print_cmd_valid(RTVL_NO, NULL, conf);
- goto out;
- }
- }
+ if (cmd == CMD_VALID_PATH) {
+ char * name = convert_dev(dev, (dev_type == DEV_DEVNODE));
+ r = check_path_valid(name, conf, dev_type == DEV_UEVENT);
+ goto out;
}
if (cmd == CMD_REMOVE_WWID && !dev) {
condlog(0, "the -w option requires a device");
goto out;
}
+ if (cmd == CMD_FLUSH_ONE && dev_type != DEV_DEVMAP) {
+ condlog(0, "the -f option requires a map name to remove");
+ goto out;
+ }
switch(delegate_to_multipathd(cmd, dev, dev_type, conf)) {
case DELEGATE_OK:
}
if (retries < 0)
retries = conf->remove_retries;
- if (conf->remove == FLUSH_ONE) {
- if (dev_type == DEV_DEVMAP) {
- r = dm_suspend_and_flush_map(dev, retries) ?
- RTVL_FAIL : RTVL_OK;
- } else
- condlog(0, "must provide a map name to remove");
-
+ if (cmd == CMD_FLUSH_ONE) {
+ r = dm_suspend_and_flush_map(dev, retries) ?
+ RTVL_FAIL : RTVL_OK;
goto out;
}
- else if (conf->remove == FLUSH_ALL) {
- r = dm_flush_maps(retries) ? RTVL_FAIL : RTVL_OK;
+ else if (cmd == CMD_FLUSH_ALL) {
+ r = dm_flush_maps(1, retries) ? RTVL_FAIL : RTVL_OK;
goto out;
}
while ((r = configure(conf, cmd, dev_type, dev)) == RTVL_RETRY)
cleanup_prio();
cleanup_checkers();
- /*
- * multipath -u must exit with status 0, otherwise udev won't
- * import its output.
- */
- if (cmd == CMD_VALID_PATH && dev_type == DEV_UEVENT && r == RTVL_NO)
- r = RTVL_OK;
-
if (dev_type == DEV_UEVENT)
closelog();
Other operation modes are chosen by using one of the following command line switches:
.TP
.B \-f
-Flush (remove) a multipath device map specified as parameter, if unused.
+Flush (remove) a multipath device map specified as parameter, if unused. This operation is delegated to the multipathd daemon if it's running.
.
.TP
.B \-F
-Flush (remove) all unused multipath device maps.
+Flush (remove) all unused multipath device maps. This operation is delegated to the multipathd daemon if it's running.
.
.TP
.B \-l
Dry run, do not create or update devmaps.
.
.TP
+.B \-e
+Enable all foreign libraries. This overrides the
+.I enable_foreign
+option from \fBmultipath.conf(5)\fR.
+.
+.TP
.B \-i
Ignore WWIDs file when processing devices. If
\fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in
(Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount
of outstanding I/O to the path and its relative throughput.
.TP
+.I "historical-service-time 0"
+(Since 5.8 kernel) Choose the path for the next bunch of IOs based on the
+estimation of future service time based on the history of previous I/O submitted
+to each path.
+.TP
The default is: \fBservice-time 0\fR
.RE
.
.
.
.TP
+.B allow_usb_devices
+If set to
+.I no
+, all USB devices will be skipped during path discovery. If you intend to use
+multipath on USB attached devices, set this to \fIyes\fR.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
.B flush_on_last_del
If set to
.I yes
.I FOREIGN MULTIPATH SUPPORT
below). The value is a regular expression; foreign libraries are loaded
if their name (e.g. \(dqnvme\(dq) matches the expression. By default,
-all foreign libraries are enabled.
+no foreign libraries are enabled. Set this to \(dqnvme\(dq to enable NVMe native
+multipath support, or \(dq.*\(dq to enable all foreign libraries.
.RS
.TP
-The default is: \fB\(dq\(dq\fR (the empty regular expression)
+The default is: \fB\(dqNONE\(dq\fR
.RE
.
.
.LP
.
.
+In the \fIblacklist\fR and \fIblacklist_exceptions\fR sections, starting a
+quoted value with an exclamation mark \fB"!"\fR will invert the matching
+of the rest of the regular expression. For instance, \fB"!^sd[a-z]"\fR will
+match all values that do not start with \fB"sd[a-z]"\fR. The exclamation mark
+can be escaped \fB"\\!"\fR to match a literal \fB!\fR at the start of a
+regular expression. \fBNote:\fR The exclamation mark must be inside quotes,
+otherwise it will be treated as starting a comment.
+.LP
+.
+.
The \fIblacklist_exceptions\fR section is used to revert the actions of the
\fIblacklist\fR section. This allows one to selectively include ("whitelist") devices which
would normally be excluded via the \fIblacklist\fR section. A common usage is
Regular expression matching the device nodes to be excluded/included.
.RS
.PP
-The default \fIblacklist\fR consists of the regular expressions
-"^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]" and
-"^(td|hd|vd)[a-z]". This causes virtual devices, non-disk devices, and some other
-device types to be excluded from multipath handling by default.
+The default \fIblacklist\fR consists of the regular expression
+\fB"!^(sd[a-z]|dasd[a-z]|nvme[0-9])"\fR. This causes all device types other
+than scsi, dasd, and nvme to be excluded from multipath handling by default.
.RE
.TP
.B wwid
include ../Makefile.inc
+ifneq ($(call check_func,dm_task_get_errno,/usr/include/libdevmapper.h),0)
+ CFLAGS += -DLIBDM_API_GET_ERRNO
+endif
+
#
# debugging stuff
#
add_handler(DEL+PATH, NULL);
add_handler(ADD+MAP, NULL);
add_handler(DEL+MAP, NULL);
+ add_handler(DEL+MAPS, NULL);
add_handler(SWITCH+MAP+GROUP, NULL);
add_handler(RECONFIGURE, NULL);
add_handler(SUSPEND+MAP, NULL);
c += snprint_foreign_paths(c, reply + maxlen - c,
style, pretty);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
c += snprint_path(c, reply + maxlen - c, style, pp, 0);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
c = reply;
c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
}
c += snprint_foreign_topology(c, reply + maxlen - c, 2);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
c = reply;
c += snprint_multipath_topology_json(c, maxlen, vecs);
- again = ((c - reply) == maxlen);
+ again = (c == reply + maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
c = reply;
c += snprint_multipath_map_json(c, maxlen, mpp);
- again = ((c - reply) == maxlen);
+ again = (c == reply + maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
c += snprint_multipath(c, reply + maxlen - c, style,
mpp, pretty);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
}
c += snprint_foreign_multipaths(c, reply + maxlen - c,
style, pretty);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
goto blacklisted;
pp = find_path_by_dev(vecs->pathvec, param);
- if (pp) {
+ if (pp && pp->initialized != INIT_REMOVED) {
condlog(2, "%s: path already in pathvec", param);
if (pp->mpp)
return 0;
- } else {
+ } else if (pp) {
+ /* Trying to add a path in INIT_REMOVED state */
+ struct multipath *prev_mpp;
+
+ prev_mpp = pp->mpp;
+ if (prev_mpp == NULL)
+ condlog(0, "Bug: %s was in INIT_REMOVED state without being a multipath member",
+ pp->dev);
+ pp->mpp = NULL;
+ pp->initialized = INIT_NEW;
+ pp->wwid[0] = '\0';
+ conf = get_multipath_config();
+ pthread_cleanup_push(put_multipath_config, conf);
+ r = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST);
+ pthread_cleanup_pop(1);
+
+ if (prev_mpp) {
+ /* Similar logic as in uev_add_path() */
+ pp->mpp = prev_mpp;
+ if (r == PATHINFO_OK &&
+ !strncmp(prev_mpp->wwid, pp->wwid, WWID_SIZE)) {
+ condlog(2, "%s: path re-added to %s", pp->dev,
+ pp->mpp->alias);
+ /* Have the checker reinstate this path asap */
+ pp->tick = 1;
+ return 0;
+ } else if (!ev_remove_path(pp, vecs, true))
+ /* Path removed in ev_remove_path() */
+ pp = NULL;
+ else {
+ /* Init state is now INIT_REMOVED again */
+ pp->dmstate = PSTATE_FAILED;
+ dm_fail_path(pp->mpp->alias, pp->dev_t);
+ condlog(1, "%s: failed to re-add path still mapped in %s",
+ pp->dev, pp->mpp->alias);
+ return 1;
+ }
+ } else {
+ switch (r) {
+ case PATHINFO_SKIPPED:
+ goto blacklisted;
+ case PATHINFO_OK:
+ break;
+ default:
+ condlog(0, "%s: failed to get pathinfo", param);
+ return 1;
+ }
+ }
+ }
+
+ if (!pp) {
struct udev_device *udevice;
udevice = udev_device_new_from_subsystem_sysname(udev,
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
- int major, minor;
+ int major = -1, minor = -1;
char dev_path[PATH_SIZE];
char *refwwid, *alias = NULL;
int rc, count = 0;
}
int
+cli_del_maps (void *v, char **reply, int *len, void *data)
+{
+ struct vectors * vecs = (struct vectors *)data;
+ struct multipath *mpp;
+ int i, ret = 0;
+
+ condlog(2, "remove maps (operator)");
+ vector_foreach_slot(vecs->mpvec, mpp, i) {
+ if (flush_map(mpp, vecs, 0))
+ ret++;
+ else
+ i--;
+ }
+ /* flush any multipath maps that aren't currently known by multipathd */
+ ret |= dm_flush_maps(0, 0);
+ return ret;
+}
+
+int
cli_reload(void *v, char **reply, int *len, void *data)
{
struct vectors * vecs = (struct vectors *)data;
return 1;
}
- return update_path_groups(mpp, vecs, 0);
+ return reload_and_sync_map(mpp, vecs, 0);
}
int resize_map(struct multipath *mpp, unsigned long long size,
c = reply;
c += snprint_blacklist_report(conf, c, maxlen);
- again = ((c - reply) == maxlen);
+ again = (c == reply + maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
pthread_cleanup_pop(1);
c = reply;
c += snprint_devices(conf, c, maxlen, vecs);
- again = ((c - reply) == maxlen);
+ again = (c == reply + maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
pthread_cleanup_pop(1);
return 1;
*reply = malloc(26);
+ if (!*reply)
+ return 1;
if (!get_be64(mpp->reservation_key)) {
sprintf(*reply, "none\n");
}
pp->marginal = 1;
- return update_path_groups(pp->mpp, vecs, 0);
+ return reload_and_sync_map(pp->mpp, vecs, 0);
}
int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
}
pp->marginal = 0;
- return update_path_groups(pp->mpp, vecs, 0);
+ return reload_and_sync_map(pp->mpp, vecs, 0);
}
int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
vector_foreach_slot (pgp->paths, pp, j)
pp->marginal = 0;
- return update_path_groups(mpp, vecs, 0);
+ return reload_and_sync_map(mpp, vecs, 0);
}
int cli_del_path (void * v, char ** reply, int * len, void * data);
int cli_add_map (void * v, char ** reply, int * len, void * data);
int cli_del_map (void * v, char ** reply, int * len, void * data);
+int cli_del_maps (void * v, char ** reply, int * len, void * data);
int cli_switch_group(void * v, char ** reply, int * len, void * data);
int cli_reconfigure(void * v, char ** reply, int * len, void * data);
int cli_resize(void * v, char ** reply, int * len, void * data);
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ if (!dm_task_run(dmt)) {
+ dm_log_error(3, DM_DEVICE_LIST, dmt);
goto fail;
+ }
if (!(names = dm_task_get_names(dmt)))
goto fail;
pthread_testcancel();
r = 0;
if (curr_dev.action == EVENT_REMOVE)
- remove_map_by_alias(curr_dev.name, waiter->vecs, 1);
+ remove_map_by_alias(curr_dev.name, waiter->vecs, PURGE_VEC);
else
r = update_multipath(waiter->vecs, curr_dev.name, 1);
pthread_cleanup_pop(1);
#include "uevent.h"
#include "log.h"
#include "uxsock.h"
+#include "alias.h"
#include "mpath_cmd.h"
#include "mpath_persist.h"
#define FILE_NAME_SIZE 256
#define CMDSIZE 160
+#define MSG_SIZE 32
#define LOG_MSG(lvl, verb, pp) \
do { \
static volatile sig_atomic_t reconfig_sig;
static volatile sig_atomic_t log_reset_sig;
+static const char *daemon_status_msg[DAEMON_STATUS_SIZE] = {
+ [DAEMON_INIT] = "init",
+ [DAEMON_START] = "startup",
+ [DAEMON_CONFIGURE] = "configure",
+ [DAEMON_IDLE] = "idle",
+ [DAEMON_RUNNING] = "running",
+ [DAEMON_SHUTDOWN] = "shutdown",
+};
+
const char *
daemon_status(void)
{
- switch (get_running_state()) {
- case DAEMON_INIT:
- return "init";
- case DAEMON_START:
- return "startup";
- case DAEMON_CONFIGURE:
- return "configure";
- case DAEMON_IDLE:
- return "idle";
- case DAEMON_RUNNING:
- return "running";
- case DAEMON_SHUTDOWN:
- return "shutdown";
- }
- return NULL;
+ int status = get_running_state();
+
+ if (status < DAEMON_INIT || status >= DAEMON_STATUS_SIZE)
+ return NULL;
+
+ return daemon_status_msg[status];
}
/*
* I love you too, systemd ...
*/
-static const char *
-sd_notify_status(enum daemon_status state)
-{
- switch (state) {
- case DAEMON_INIT:
- return "STATUS=init";
- case DAEMON_START:
- return "STATUS=startup";
- case DAEMON_CONFIGURE:
- return "STATUS=configure";
- case DAEMON_IDLE:
- case DAEMON_RUNNING:
- return "STATUS=up";
- case DAEMON_SHUTDOWN:
- return "STATUS=shutdown";
- }
- return NULL;
-}
-
#ifdef USE_SYSTEMD
static void do_sd_notify(enum daemon_status old_state,
enum daemon_status new_state)
{
+ char notify_msg[MSG_SIZE];
+ const char *msg;
/*
* Checkerloop switches back and forth between idle and running state.
* No need to tell systemd each time.
if ((new_state == DAEMON_IDLE || new_state == DAEMON_RUNNING) &&
(old_state == DAEMON_IDLE || old_state == DAEMON_RUNNING))
return;
- sd_notify(0, sd_notify_status(new_state));
+
+ if (new_state == DAEMON_IDLE || new_state == DAEMON_RUNNING)
+ msg = "up";
+ else
+ msg = daemon_status_msg[new_state];
+
+ if (msg && !safe_sprintf(notify_msg, "STATUS=%s", msg))
+ sd_notify(0, notify_msg);
}
#endif
static void __post_config_state(enum daemon_status state)
{
if (state != running_state && running_state != DAEMON_SHUTDOWN) {
+#ifdef USE_SYSTEMD
enum daemon_status old_state = running_state;
+#endif
running_state = state;
pthread_cond_broadcast(&config_cond);
pthread_cleanup_push(config_cleanup, NULL);
pthread_mutex_lock(&config_lock);
if (running_state != state) {
+#ifdef USE_SYSTEMD
enum daemon_status old_state = running_state;
+#endif
if (running_state == DAEMON_SHUTDOWN)
rc = EINVAL;
condlog(3, "%s: removing map from internal tables", mpp->alias);
if (!poll_dmevents)
stop_waiter_thread(mpp);
- remove_map(mpp, vecs, PURGE_VEC);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
}
static void
remove_maps(vecs);
}
-static void
-set_multipath_wwid (struct multipath * mpp)
-{
- if (strlen(mpp->wwid))
- return;
-
- dm_get_uuid(mpp->alias, mpp->wwid, WWID_SIZE);
-}
-
int __setup_multipath(struct vectors *vecs, struct multipath *mpp,
int reset)
{
if (dm_get_info(mpp->alias, &mpp->dmi)) {
/* Error accessing table */
- condlog(3, "%s: cannot access table", mpp->alias);
+ condlog(2, "%s: cannot access table", mpp->alias);
goto out;
}
- if (update_multipath_strings(mpp, vecs->pathvec, 1)) {
+ if (update_multipath_strings(mpp, vecs->pathvec) != DMP_OK) {
condlog(0, "%s: failed to setup multipath", mpp->alias);
goto out;
}
retries = -1;
goto fail;
}
- verify_paths(mpp, vecs);
+ verify_paths(mpp);
mpp->action = ACT_RELOAD;
if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
fail:
if (new_map && (retries < 0 || wait_for_events(mpp, vecs))) {
condlog(0, "%s: failed to create new map", mpp->alias);
- remove_map(mpp, vecs, 1);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
return 1;
}
condlog(3, "%s: cannot access table", mpp->alias);
goto out;
}
- set_multipath_wwid(mpp);
+ if (!strlen(mpp->wwid))
+ dm_get_uuid(mpp->alias, mpp->wwid, WWID_SIZE);
+ if (!strlen(mpp->wwid))
+ condlog(1, "%s: adding map with empty WWID", mpp->alias);
conf = get_multipath_config();
mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
put_multipath_config(conf);
- if (update_multipath_table(mpp, vecs->pathvec, 1))
+ if (update_multipath_table(mpp, vecs->pathvec, 0) != DMP_OK)
goto out;
- if (update_multipath_status(mpp))
+ if (update_multipath_status(mpp) != DMP_OK)
goto out;
if (!vector_alloc_slot(vecs->mpvec))
return mpp;
out:
- remove_map(mpp, vecs, PURGE_VEC);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
return NULL;
}
sync_map_state(mpp);
}
-static int
+int
flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths)
{
int r;
goto out;
}
+ dm_queue_if_no_path(alias, 0);
remove_map_and_stop_waiter(mpp, vecs);
out:
lock_cleanup_pop(vecs->lock);
pp = find_path_by_dev(vecs->pathvec, uev->kernel);
if (pp) {
int r;
+ struct multipath *prev_mpp = NULL;
+
+ if (pp->initialized == INIT_REMOVED) {
+ condlog(3, "%s: re-adding removed path", pp->dev);
+ pp->initialized = INIT_NEW;
+ prev_mpp = pp->mpp;
+ if (prev_mpp == NULL)
+ condlog(0, "Bug: %s was in INIT_REMOVED state without being a multipath member",
+ pp->dev);
+ pp->mpp = NULL;
+ /* make sure get_uid() is called */
+ pp->wwid[0] = '\0';
+ } else
+ condlog(3,
+ "%s: spurious uevent, path already in pathvec",
+ uev->kernel);
- condlog(3, "%s: spurious uevent, path already in pathvec",
- uev->kernel);
if (!pp->mpp && !strlen(pp->wwid)) {
condlog(3, "%s: reinitialize path", uev->kernel);
udev_device_unref(pp->udev);
r = pathinfo(pp, conf,
DI_ALL | DI_BLACKLIST);
pthread_cleanup_pop(1);
- if (r == PATHINFO_OK)
+ if (r == PATHINFO_OK && !prev_mpp)
ret = ev_add_path(pp, vecs, need_do_map);
- else if (r == PATHINFO_SKIPPED) {
+ else if (r == PATHINFO_OK &&
+ !strncmp(pp->wwid, prev_mpp->wwid, WWID_SIZE)) {
+ /*
+ * Path was unsuccessfully removed, but now
+ * re-added, and still belongs to the right map
+ * - all fine, reinstate asap
+ */
+ pp->mpp = prev_mpp;
+ pp->tick = 1;
+ ret = 0;
+ } else if (prev_mpp) {
+ /*
+ * Bad: re-added path still hangs in wrong map
+ * Make another attempt to remove the path
+ */
+ pp->mpp = prev_mpp;
+ ret = ev_remove_path(pp, vecs, true);
+ if (r == PATHINFO_OK && !ret)
+ /*
+ * Path successfully freed, move on to
+ * "new path" code path below
+ */
+ pp = NULL;
+ else {
+ /*
+ * Failure in ev_remove_path will keep
+ * path in pathvec in INIT_REMOVED state
+ * Fail the path to make sure it isn't
+ * used any more.
+ */
+ pp->dmstate = PSTATE_FAILED;
+ dm_fail_path(pp->mpp->alias, pp->dev_t);
+ condlog(1, "%s: failed to re-add path still mapped in %s",
+ pp->dev, pp->mpp->alias);
+ }
+ } else if (r == PATHINFO_SKIPPED) {
condlog(3, "%s: remove blacklisted path",
uev->kernel);
i = find_slot(vecs->pathvec, (void *)pp);
if (mpp) {
condlog(4,"%s: adopting all paths for path %s",
mpp->alias, pp->dev);
- if (adopt_paths(vecs->pathvec, mpp))
+ if (adopt_paths(vecs->pathvec, mpp) ||
+ find_slot(vecs->pathvec, pp) == -1)
goto fail; /* leave path added to pathvec */
- verify_paths(mpp, vecs);
+ verify_paths(mpp);
mpp->action = ACT_RELOAD;
} else {
if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) {
goto fail;
fail_map:
- remove_map(mpp, vecs, 1);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
fail:
orphan_path(pp, "failed to add path");
return 1;
}
/*
+ * Mark the path as removed. In case of success, we
+ * will delete it for good. Otherwise, it will be deleted
+ * later, unless all attempts to reload this map fail.
+ * Note: we have to explicitly remove pp from mpp->paths,
+ * update_mpp_paths() doesn't do that.
+ */
+ set_path_removed(pp);
+ i = find_slot(mpp->paths, pp);
+ if (i != -1)
+ vector_del_slot(mpp->paths, i);
+
+ /*
* Make sure mpp->hwe doesn't point to freed memory
* We call extract_hwe_from_path() below to restore mpp->hwe
*/
if (mpp->hwe == pp->hwe)
mpp->hwe = NULL;
- if ((i = find_slot(mpp->paths, (void *)pp)) != -1)
- vector_del_slot(mpp->paths, i);
-
/*
* remove the map IF removing the last path
*/
" removing all paths",
alias);
retval = 0;
+ /* flush_map() has freed the path */
goto out;
}
/*
/*
* update our state from kernel
*/
+ char devt[BLK_DEV_SIZE];
+
+ strlcpy(devt, pp->dev_t, sizeof(devt));
if (setup_multipath(vecs, mpp))
return 1;
+ /*
+ * Successful map reload without this path:
+ * sync_map_state() will free it.
+ */
sync_map_state(mpp);
- condlog(2, "%s [%s]: path removed from map %s",
- pp->dev, pp->dev_t, mpp->alias);
+ condlog(2, "%s: path removed from map %s",
+ devt, mpp->alias);
}
+ } else {
+ /* mpp == NULL */
+ if ((i = find_slot(vecs->pathvec, (void *)pp)) != -1)
+ vector_del_slot(vecs->pathvec, i);
+ free_path(pp);
}
-
out:
- if ((i = find_slot(vecs->pathvec, (void *)pp)) != -1)
- vector_del_slot(vecs->pathvec, i);
-
- free_path(pp);
-
return retval;
fail:
else {
if (ro == 1)
pp->mpp->force_readonly = 1;
- retval = update_path_groups(mpp, vecs, 0);
+ retval = reload_and_sync_map(mpp, vecs, 0);
if (retval == 2)
condlog(2, "%s: map removed during reload", pp->dev);
else {
return 1;
vector_foreach_slot (vecs->mpvec, mpp, i)
- if (update_multipath_table(mpp, vecs->pathvec, 1) ||
- update_multipath_status(mpp)) {
- remove_map(mpp, vecs, 1);
+ if (update_multipath_table(mpp, vecs->pathvec, 0) != DMP_OK ||
+ update_multipath_status(mpp) != DMP_OK) {
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
i--;
}
uev_pathfail_check(uev, vecs);
} else if (!strncmp(uev->action, "remove", 6)) {
r = uev_remove_map(uev, vecs);
+ } else if (!strncmp(uev->action, "add", 3)) {
+ const char *ev_name;
+ char *dm_name;
+ int major = -1, minor = -1;
+
+ /*
+ * If DM_NAME is not set for a valid map, trigger a
+ * change event. This can happen during coldplug
+ * if udev was killed between handling the 'add' and
+ * 'change' events before.
+ */
+ ev_name = uevent_get_dm_name(uev);
+ if (!ev_name) {
+ major = uevent_get_major(uev);
+ minor = uevent_get_minor(uev);
+ dm_name = dm_mapname(major, minor);
+ if (dm_name && *dm_name) {
+ condlog(2, "%s: received incomplete 'add' uevent, triggering change",
+ dm_name);
+ udev_device_set_sysattr_value(uev->udev,
+ "uevent",
+ "change");
+ free(dm_name);
+ }
+ }
}
goto out;
}
set_handler_callback(DEL+PATH, cli_del_path);
set_handler_callback(ADD+MAP, cli_add_map);
set_handler_callback(DEL+MAP, cli_del_map);
+ set_handler_callback(DEL+MAPS, cli_del_maps);
set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group);
set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure);
set_handler_callback(SUSPEND+MAP, cli_suspend);
/*
* caller must have locked the path list before calling that function
*/
-static int
+static void
reinstate_path (struct path * pp)
{
- int ret = 0;
-
if (!pp->mpp)
- return 0;
+ return;
- 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);
- ret = 1;
- } else {
+ else {
condlog(2, "%s: reinstated", pp->dev_t);
update_queue_mode_add_path(pp->mpp);
}
- return ret;
}
static void
return 1;
}
-int update_path_groups(struct multipath *mpp, struct vectors *vecs, int refresh)
+static int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
+ int is_daemon)
+{
+ char params[PARAMS_SIZE] = {0};
+ struct path *pp;
+ int i, r;
+
+ update_mpp_paths(mpp, vecs->pathvec);
+ if (refresh) {
+ vector_foreach_slot (mpp->paths, pp, i) {
+ struct config *conf = get_multipath_config();
+ pthread_cleanup_push(put_multipath_config, conf);
+ r = pathinfo(pp, conf, DI_PRIO);
+ pthread_cleanup_pop(1);
+ if (r) {
+ condlog(2, "%s: failed to refresh pathinfo",
+ mpp->alias);
+ return 1;
+ }
+ }
+ }
+ if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
+ condlog(0, "%s: failed to setup map", mpp->alias);
+ return 1;
+ }
+ select_action(mpp, vecs->mpvec, 1);
+
+ r = domap(mpp, params, is_daemon);
+ if (r == DOMAP_FAIL || r == DOMAP_RETRY) {
+ condlog(3, "%s: domap (%u) failure "
+ "for reload map", mpp->alias, r);
+ return 1;
+ }
+
+ return 0;
+}
+
+int reload_and_sync_map(struct multipath *mpp,
+ struct vectors *vecs, int refresh)
{
if (reload_map(vecs, mpp, refresh, 1))
return 1;
int marginal_pathgroups, marginal_changed = 0;
int ret;
- if ((pp->initialized == INIT_OK ||
- pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp)
+ if (((pp->initialized == INIT_OK ||
+ pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp) ||
+ pp->initialized == INIT_REMOVED)
return 0;
if (pp->tick)
/*
* Synchronize with kernel state
*/
- if (update_multipath_strings(pp->mpp, vecs->pathvec, 1)) {
- condlog(1, "%s: Could not synchronize with kernel state",
- pp->dev);
+ ret = update_multipath_strings(pp->mpp, vecs->pathvec);
+ if (ret != DMP_OK) {
+ if (ret == DMP_NOT_FOUND) {
+ /* multipath device missing. Likely removed */
+ condlog(1, "%s: multipath device '%s' not found",
+ pp->dev, pp->mpp->alias);
+ return 0;
+ } else
+ condlog(1, "%s: Couldn't synchronize with kernel state",
+ pp->dev);
pp->dmstate = PSTATE_UNDEF;
}
/* if update_multipath_strings orphaned the path, quit early */
/*
* reinstate this path
*/
- if (!disable_reinstate && reinstate_path(pp)) {
- condlog(3, "%s: reload map", pp->dev);
- ev_add_path(pp, vecs, 1);
- pp->tick = 1;
- return 0;
- }
+ if (!disable_reinstate)
+ reinstate_path(pp);
new_path_up = 1;
if (oldchkrstate != PATH_UP && oldchkrstate != PATH_GHOST)
else if (newstate == PATH_UP || newstate == PATH_GHOST) {
if ((pp->dmstate == PSTATE_FAILED ||
pp->dmstate == PSTATE_UNDEF) &&
- !disable_reinstate) {
+ !disable_reinstate)
/* Clear IO errors */
- if (reinstate_path(pp)) {
- condlog(3, "%s: reload map", pp->dev);
- ev_add_path(pp, vecs, 1);
- pp->tick = 1;
- return 0;
- }
- } else {
+ reinstate_path(pp);
+ else {
LOG_MSG(4, verbosity, pp);
if (pp->checkint != max_checkint) {
/*
*/
condlog(4, "path prio refresh");
- if (marginal_changed)
- update_path_groups(pp->mpp, vecs, 1);
+ if (marginal_changed) {
+ condlog(2, "%s: path is %s marginal", pp->dev,
+ (pp->marginal)? "now" : "no longer");
+ reload_and_sync_map(pp->mpp, vecs, 1);
+ }
else if (update_prio(pp, new_path_up) &&
(pp->mpp->pgpolicyfn == (pgpolicyfn *)group_by_prio) &&
- pp->mpp->pgfailback == -FAILBACK_IMMEDIATE)
- update_path_groups(pp->mpp, vecs, !new_path_up);
- else if (need_switch_pathgroup(pp->mpp, 0)) {
+ pp->mpp->pgfailback == -FAILBACK_IMMEDIATE) {
+ condlog(2, "%s: path priorities changed. reloading",
+ pp->mpp->alias);
+ reload_and_sync_map(pp->mpp, vecs, !new_path_up);
+ } else if (need_switch_pathgroup(pp->mpp, 0)) {
if (pp->mpp->pgfailback > 0 &&
(new_path_up || pp->mpp->failback_tick <= 0))
pp->mpp->failback_tick =
struct timespec last_time;
struct config *conf;
int foreign_tick = 0;
+#ifdef USE_SYSTEMD
bool use_watchdog;
+#endif
pthread_cleanup_push(rcu_unregister, NULL);
rcu_register_thread();
/* use_watchdog is set from process environment and never changes */
conf = get_multipath_config();
+#ifdef USE_SYSTEMD
use_watchdog = conf->use_watchdog;
+#endif
put_multipath_config(conf);
while (1) {
conf = get_multipath_config();
max_checkint = conf->max_checkint;
put_multipath_config(conf);
- if (diff_time.tv_sec > max_checkint)
+ if (diff_time.tv_sec > (time_t)max_checkint)
condlog(1, "path checkers took longer "
"than %lu seconds, consider "
"increasing max_polling_interval",
*/
vector_foreach_slot(vecs->mpvec, mpp, i) {
if (wait_for_events(mpp, vecs)) {
- remove_map(mpp, vecs, 1);
+ remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
i--;
continue;
}
conf->verbosity = verbosity;
if (bindings_read_only)
conf->bindings_read_only = bindings_read_only;
+ check_alias_settings(conf);
+
uxsock_timeout = conf->uxsock_timeout;
old = rcu_dereference(multipath_conf);
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{
condlog(1, "%s: No key found. Device may not be registered.", pp->dev);
- ret = MPATH_PR_SUCCESS;
goto out;
}
condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ",
{
condlog(0, "%s: Either device not registered or ", pp->dev);
condlog(0, "host is not authorised for registration. Skip path");
- ret = MPATH_PR_OTHER;
goto out;
}
- param= malloc(sizeof(struct prout_param_descriptor));
- memset(param, 0 , sizeof(struct prout_param_descriptor));
+ param = (struct prout_param_descriptor *)MALLOC(sizeof(struct prout_param_descriptor));
+ if (!param)
+ goto out;
+
param->sa_flags = mpp->sa_flags;
memcpy(param->sa_key, &mpp->reservation_key, 8);
param->num_transportid = 0;
#define MAPGCINT 5
enum daemon_status {
- DAEMON_INIT,
+ DAEMON_INIT = 0,
DAEMON_START,
DAEMON_CONFIGURE,
DAEMON_IDLE,
DAEMON_RUNNING,
DAEMON_SHUTDOWN,
+ DAEMON_STATUS_SIZE,
};
struct prout_param_descriptor;
int ev_remove_path (struct path *, struct vectors *, int);
int ev_add_map (char *, const char *, struct vectors *);
int ev_remove_map (char *, char *, int, struct vectors *);
+int flush_map(struct multipath *, struct vectors *, int);
int set_config_state(enum daemon_status);
void * mpath_alloc_prin_response(int prin_sa);
int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
int reset);
#define setup_multipath(vecs, mpp) __setup_multipath(vecs, mpp, 1)
int update_multipath (struct vectors *vecs, char *mapname, int reset);
-int update_path_groups(struct multipath *mpp, struct vectors *vecs,
- int refresh);
+int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs,
+ int refresh);
#endif /* MAIN_H */
return;
condlog(3, "%s: stop event checker thread (%lu)", mpp->alias,
- mpp->waiter);
+ (unsigned long)mpp->waiter);
thread = mpp->waiter;
mpp->waiter = (pthread_t)0;
pthread_cleanup_push(cleanup_lock, &waiter_lock);
pthread_testcancel();
r = dm_task_run(waiter->dmt);
+ if (!r)
+ dm_log_error(2, DM_DEVICE_WAITEVENT, waiter->dmt);
pthread_testcancel();
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
-Wno-unused-parameter $(W_MISSING_INITIALIZERS)
-LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka
+LIBDEPS += -L$(multipathdir) -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
- alias directio
+ alias directio valid devt
+HELPERS := test-lib.o test-log.o
.SILENT: $(TESTS:%=%.o)
.PRECIOUS: $(TESTS:%=%-test)
all: $(TESTS:%=%.out)
+valgrind: $(TESTS:%=%.vgr)
# test-specific compiler flags
# XYZ-test_FLAGS: Additional compiler flags for this test
dmevents-test_LIBDEPS = -lpthread -ldevmapper -lurcu
hwtable-test_TESTDEPS := test-lib.o
hwtable-test_OBJDEPS := ../libmultipath/discovery.o ../libmultipath/blacklist.o \
- ../libmultipath/prio.o ../libmultipath/callout.o ../libmultipath/structs.o
+ ../libmultipath/structs.o
hwtable-test_LIBDEPS := -ludev -lpthread -ldl
blacklist-test_TESTDEPS := test-log.o
blacklist-test_OBJDEPS := ../libmultipath/blacklist.o
vpd-test_LIBDEPS := -ludev -lpthread -ldl
alias-test_TESTDEPS := test-log.o
alias-test_LIBDEPS := -lpthread -ldl
+valid-test_OBJDEPS := ../libmultipath/valid.o
+valid-test_LIBDEPS := -ludev -lpthread -ldl
+devt-test_LIBDEPS := -ludev
ifneq ($(DIO_TEST_DEV),)
directio-test_LIBDEPS := -laio
endif
$(CC) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
lib/libchecktur.so:
- mkdir lib
- ln -t lib ../libmultipath/{checkers,prioritizers,foreign}/*.so
+ mkdir -p lib
+ ln ../libmultipath/*/*.so lib
%.out: %-test lib/libchecktur.so
@echo == running $< ==
@LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) ./$< >$@
-OBJS = $(TESTS:%=%.o) test-lib.o
+%.vgr: %-test lib/libchecktur.so
+ @echo == running valgrind for $< ==
+ @LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) \
+ valgrind --leak-check=full --error-exitcode=128 ./$< >$@ 2>&1
+
+OBJS = $(TESTS:%=%.o) $(HELPERS)
test_clean:
- $(RM) $(TESTS:%=%.out)
+ $(RM) $(TESTS:%=%.out) $(TESTS:%=%.vgr)
+
+valgrind_clean:
+ $(RM) $(TESTS:%=%.vgr)
-clean: test_clean dep_clean
+clean: test_clean valgrind_clean dep_clean
$(RM) $(TESTS:%=%-test) $(OBJS) *.o.wrap
$(RM) -rf lib
`<testname>.out`. The test programs are called `<testname>-test`, and can
be run standalone e.g. for debugging purposes.
+## Running tests under valgrind
+
+The unit tests can be run under the valgrind debugger with `make valgrind`
+in the `tests` directory, or `make valgrind-test` in the top directory.
+If valgrind detects a bad memory access or leak, the test will fail. The
+output of the test run, including valgrind output, is stored as
+`<testname>.vgr`.
+
## Notes on individual tests
### Tests that require root permissions
buf[0] = '\0';
will_return(__wrap_fgets, "MPATHa WWID0\n");
- expect_condlog(3, "Found matching alias [MPATHa] in bindings file.\n"
+ expect_condlog(3, "Found matching alias [MPATHa] in bindings file. "
"Setting wwid to WWID0\n");
rc = rlookup_binding(NULL, buf, "MPATHa");
assert_int_equal(rc, 0);
will_return(__wrap_fgets, "MPATHa WWID0\n");
will_return(__wrap_fgets, "MPATHz WWID26\n");
will_return(__wrap_fgets, "MPATHb WWID2\n");
- expect_condlog(3, "Found matching alias [MPATHb] in bindings file.\n"
+ expect_condlog(3, "Found matching alias [MPATHb] in bindings file. "
"Setting wwid to WWID2\n");
rc = rlookup_binding(NULL, buf, "MPATHb");
assert_int_equal(rc, 0);
alias = allocate_binding(0, "WWIDa", 1, "MPATH");
assert_ptr_not_equal(alias, NULL);
assert_string_equal(alias, "MPATHa");
+ free(alias);
}
static void al_zz(void **state)
alias = allocate_binding(0, "WWIDzz", 26*26 + 26, "MPATH");
assert_ptr_not_equal(alias, NULL);
assert_string_equal(alias, "MPATHzz");
+ free(alias);
}
static void al_0(void **state)
will_return(__wrap_write, strlen(ln) - 1);
expect_value(__wrap_ftruncate, length, offset);
will_return(__wrap_ftruncate, 0);
- expect_condlog(0, "Cannot write binding to bindings file : Success\n");
+ expect_condlog(0, "Cannot write binding to bindings file :");
alias = allocate_binding(0, "WWIDa", 1, "MPATH");
assert_ptr_equal(alias, NULL);
return *(const char **)list_entry;
}
+vector elist_property_default;
+vector blist_devnode_default;
vector blist_devnode_sdb;
+vector blist_devnode_sdb_inv;
vector blist_all;
vector blist_device_foo_bar;
+vector blist_device_foo_inv_bar;
+vector blist_device_foo_bar_inv;
vector blist_device_all;
vector blist_wwid_xyzzy;
+vector blist_wwid_xyzzy_inv;
vector blist_protocol_fcp;
+vector blist_protocol_fcp_inv;
vector blist_property_wwn;
+vector blist_property_wwn_inv;
static int setup(void **state)
{
+ struct config conf;
+
+ memset(&conf, 0, sizeof(conf));
+ conf.blist_devnode = vector_alloc();
+ if (!conf.blist_devnode)
+ return -1;
+ conf.elist_property = vector_alloc();
+ if (!conf.elist_property)
+ return -1;
+ if (setup_default_blist(&conf) != 0)
+ return -1;
+ elist_property_default = conf.elist_property;
+ blist_devnode_default = conf.blist_devnode;
+
blist_devnode_sdb = vector_alloc();
if (!blist_devnode_sdb ||
- store_ble(blist_devnode_sdb, strdup("sdb"), ORIGIN_CONFIG))
+ store_ble(blist_devnode_sdb, "sdb", ORIGIN_CONFIG))
+ return -1;
+ blist_devnode_sdb_inv = vector_alloc();
+ if (!blist_devnode_sdb_inv ||
+ store_ble(blist_devnode_sdb_inv, "!sdb", ORIGIN_CONFIG))
return -1;
blist_all = vector_alloc();
- if (!blist_all || store_ble(blist_all, strdup(".*"), ORIGIN_CONFIG))
+ if (!blist_all || store_ble(blist_all, ".*", ORIGIN_CONFIG))
return -1;
blist_device_foo_bar = vector_alloc();
if (!blist_device_foo_bar || alloc_ble_device(blist_device_foo_bar) ||
- set_ble_device(blist_device_foo_bar, strdup("foo"), strdup("bar"),
- ORIGIN_CONFIG))
+ set_ble_device(blist_device_foo_bar, "foo", "bar", ORIGIN_CONFIG))
+ return -1;
+ blist_device_foo_inv_bar = vector_alloc();
+ if (!blist_device_foo_inv_bar ||
+ alloc_ble_device(blist_device_foo_inv_bar) ||
+ set_ble_device(blist_device_foo_inv_bar, "!foo", "bar", ORIGIN_CONFIG))
+ return -1;
+ blist_device_foo_bar_inv = vector_alloc();
+ if (!blist_device_foo_bar_inv ||
+ alloc_ble_device(blist_device_foo_bar_inv) ||
+ set_ble_device(blist_device_foo_bar_inv, "foo", "!bar", ORIGIN_CONFIG))
return -1;
blist_device_all = vector_alloc();
if (!blist_device_all || alloc_ble_device(blist_device_all) ||
- set_ble_device(blist_device_all, strdup(".*"), strdup(".*"),
- ORIGIN_CONFIG))
+ set_ble_device(blist_device_all, ".*", ".*", ORIGIN_CONFIG))
return -1;
blist_wwid_xyzzy = vector_alloc();
if (!blist_wwid_xyzzy ||
- store_ble(blist_wwid_xyzzy, strdup("xyzzy"), ORIGIN_CONFIG))
+ store_ble(blist_wwid_xyzzy, "xyzzy", ORIGIN_CONFIG))
+ return -1;
+ blist_wwid_xyzzy_inv = vector_alloc();
+ if (!blist_wwid_xyzzy_inv ||
+ store_ble(blist_wwid_xyzzy_inv, "!xyzzy", ORIGIN_CONFIG))
return -1;
blist_protocol_fcp = vector_alloc();
if (!blist_protocol_fcp ||
- store_ble(blist_protocol_fcp, strdup("scsi:fcp"), ORIGIN_CONFIG))
+ store_ble(blist_protocol_fcp, "scsi:fcp", ORIGIN_CONFIG))
+ return -1;
+ blist_protocol_fcp_inv = vector_alloc();
+ if (!blist_protocol_fcp_inv ||
+ store_ble(blist_protocol_fcp_inv, "!scsi:fcp", ORIGIN_CONFIG))
return -1;
blist_property_wwn = vector_alloc();
if (!blist_property_wwn ||
- store_ble(blist_property_wwn, strdup("ID_WWN"), ORIGIN_CONFIG))
+ store_ble(blist_property_wwn, "ID_WWN", ORIGIN_CONFIG))
+ return -1;
+ blist_property_wwn_inv = vector_alloc();
+ if (!blist_property_wwn_inv ||
+ store_ble(blist_property_wwn_inv, "!ID_WWN", ORIGIN_CONFIG))
return -1;
return 0;
static int teardown(void **state)
{
+ free_blacklist(elist_property_default);
+ free_blacklist(blist_devnode_default);
free_blacklist(blist_devnode_sdb);
+ free_blacklist(blist_devnode_sdb_inv);
free_blacklist(blist_all);
free_blacklist_device(blist_device_foo_bar);
+ free_blacklist_device(blist_device_foo_inv_bar);
+ free_blacklist_device(blist_device_foo_bar_inv);
free_blacklist_device(blist_device_all);
free_blacklist(blist_wwid_xyzzy);
+ free_blacklist(blist_wwid_xyzzy_inv);
free_blacklist(blist_protocol_fcp);
+ free_blacklist(blist_protocol_fcp_inv);
free_blacklist(blist_property_wwn);
+ free_blacklist(blist_property_wwn_inv);
return 0;
}
expect_condlog(3, "sdb: device node name blacklisted\n");
assert_int_equal(filter_devnode(blist_devnode_sdb, NULL, "sdb"),
MATCH_DEVNODE_BLIST);
+ assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdb"),
+ MATCH_NOTHING);
+ expect_condlog(3, "sdc: device node name blacklisted\n");
+ assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdc"),
+ MATCH_DEVNODE_BLIST);
}
static void test_devnode_whitelist(void **state)
MATCH_NOTHING);
}
+static void test_devnode_default(void **state)
+{
+ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "sdaa"),
+ MATCH_NOTHING);
+ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "nvme0n1"),
+ MATCH_NOTHING);
+ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "dasda"),
+ MATCH_NOTHING);
+ expect_condlog(3, "hda: device node name blacklisted\n");
+ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "hda"),
+ MATCH_DEVNODE_BLIST);
+}
+
static void test_device_blacklist(void **state)
{
expect_condlog(3, "sdb: (foo:bar) vendor/product blacklisted\n");
assert_int_equal(filter_device(blist_device_foo_bar, NULL, "foo",
"bar", "sdb"),
MATCH_DEVICE_BLIST);
+ assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "foo",
+ "bar", "sdb"),
+ MATCH_NOTHING);
+ assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo",
+ "bar", "sdb"),
+ MATCH_NOTHING);
+ expect_condlog(3, "sdb: (baz:bar) vendor/product blacklisted\n");
+ assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "baz",
+ "bar", "sdb"),
+ MATCH_DEVICE_BLIST);
+ expect_condlog(3, "sdb: (foo:baz) vendor/product blacklisted\n");
+ assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo",
+ "baz", "sdb"),
+ MATCH_DEVICE_BLIST);
}
static void test_device_whitelist(void **state)
expect_condlog(3, "sdb: wwid xyzzy blacklisted\n");
assert_int_equal(filter_wwid(blist_wwid_xyzzy, NULL, "xyzzy", "sdb"),
MATCH_WWID_BLIST);
+ assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "xyzzy",
+ "sdb"), MATCH_NOTHING);
+ expect_condlog(3, "sdb: wwid plugh blacklisted\n");
+ assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "plugh",
+ "sdb"), MATCH_WWID_BLIST);
}
static void test_wwid_whitelist(void **state)
expect_condlog(3, "sdb: protocol scsi:fcp blacklisted\n");
assert_int_equal(filter_protocol(blist_protocol_fcp, NULL, &pp),
MATCH_PROTOCOL_BLIST);
+ assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp),
+ MATCH_NOTHING);
+ pp.sg_id.proto_id = SCSI_PROTOCOL_ATA;
+ expect_condlog(3, "sdb: protocol scsi:ata blacklisted\n");
+ assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp),
+ MATCH_PROTOCOL_BLIST);
}
static void test_protocol_whitelist(void **state)
static void test_property_blacklist(void **state)
{
static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
+ static struct udev_device udev_inv = { "sdb", { "ID_WWN", NULL } };
conf.blist_property = blist_property_wwn;
expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n");
assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
MATCH_PROPERTY_BLIST);
+ conf.blist_property = blist_property_wwn_inv;
+ expect_condlog(3, "sdb: udev property ID_FOO blacklisted\n");
+ assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
+ MATCH_PROPERTY_BLIST);
+ assert_int_equal(filter_property(&conf, &udev_inv, 3, "ID_SERIAL"),
+ MATCH_NOTHING);
}
/* the property check works different in that you check all the property
cmocka_unit_test(test_devnode_blacklist),
cmocka_unit_test(test_devnode_whitelist),
cmocka_unit_test(test_devnode_missing),
+ cmocka_unit_test(test_devnode_default),
cmocka_unit_test(test_device_blacklist),
cmocka_unit_test(test_device_whitelist),
cmocka_unit_test(test_device_missing),
--- /dev/null
+/*
+ * Copyright (c) 2020 Martin Wilck, SUSE
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <cmocka.h>
+#include <libudev.h>
+#include <sys/sysmacros.h>
+#include "util.h"
+#include "debug.h"
+
+#include "globals.c"
+
+static int get_one_devt(char *devt, size_t len)
+{
+ struct udev_enumerate *enm;
+ int r, ret = -1;
+ struct udev_list_entry *first;
+ struct udev_device *u_dev;
+ const char *path;
+ dev_t devnum;
+
+ enm = udev_enumerate_new(udev);
+ if (!enm)
+ return -1;
+ r = udev_enumerate_add_match_subsystem(enm, "block");
+ r = udev_enumerate_scan_devices(enm);
+ if (r < 0)
+ goto out;
+ first = udev_enumerate_get_list_entry(enm);
+ if (!first)
+ goto out;
+ path = udev_list_entry_get_name(first);
+ u_dev = udev_device_new_from_syspath(udev, path);
+ if (!u_dev)
+ goto out;
+ devnum = udev_device_get_devnum(u_dev);
+ snprintf(devt, len, "%d:%d",
+ major(devnum), minor(devnum));
+ udev_device_unref(u_dev);
+ condlog(3, "found block device: %s", devt);
+ ret = 0;
+out:
+ udev_enumerate_unref(enm);
+ return ret;
+}
+
+int setup(void **state)
+{
+ static char dev_t[BLK_DEV_SIZE];
+
+ udev = udev_new();
+ if (udev == NULL)
+ return -1;
+ *state = dev_t;
+ return get_one_devt(dev_t, sizeof(dev_t));
+}
+
+int teardown(void **state)
+{
+ udev_unref(udev);
+ return 0;
+}
+
+static void test_devt2devname_devt_good(void **state)
+{
+ char dummy[BLK_DEV_SIZE];
+
+ assert_int_equal(devt2devname(dummy, sizeof(dummy), *state), 0);
+}
+
+static void test_devt2devname_devname_null(void **state)
+{
+ assert_int_equal(devt2devname(NULL, 0, ""), 1);
+}
+
+/* buffer length 0 */
+static void test_devt2devname_length_0(void **state)
+{
+ char dummy[] = "";
+
+ assert_int_equal(devt2devname(dummy, 0, ""), 1);
+}
+
+/* buffer too small */
+static void test_devt2devname_length_1(void **state)
+{
+ char dummy[] = "";
+
+ assert_int_equal(devt2devname(dummy, sizeof(dummy), *state), 1);
+}
+
+static void test_devt2devname_devt_null(void **state)
+{
+ char dummy[32];
+
+ assert_int_equal(devt2devname(dummy, sizeof(dummy), NULL), 1);
+}
+
+static void test_devt2devname_devt_empty(void **state)
+{
+ char dummy[32];
+
+ assert_int_equal(devt2devname(dummy, sizeof(dummy), ""), 1);
+}
+
+static void test_devt2devname_devt_invalid_1(void **state)
+{
+ char dummy[32];
+
+ assert_int_equal(devt2devname(dummy, sizeof(dummy), "foo"), 1);
+}
+
+static void test_devt2devname_devt_invalid_2(void **state)
+{
+ char dummy[32];
+
+ assert_int_equal(devt2devname(dummy, sizeof(dummy), "1234"), 1);
+}
+
+static void test_devt2devname_devt_invalid_3(void **state)
+{
+ char dummy[32];
+
+ assert_int_equal(devt2devname(dummy, sizeof(dummy), "0:0"), 1);
+}
+
+static void test_devt2devname_real(void **state)
+{
+ struct udev_enumerate *enm;
+ int r;
+ struct udev_list_entry *first, *item;
+ unsigned int i = 0;
+
+ enm = udev_enumerate_new(udev);
+ assert_non_null(enm);
+ r = udev_enumerate_add_match_subsystem(enm, "block");
+ assert_in_range(r, 0, INT_MAX);
+ r = udev_enumerate_scan_devices(enm);
+ first = udev_enumerate_get_list_entry(enm);
+ udev_list_entry_foreach(item, first) {
+ const char *path = udev_list_entry_get_name(item);
+ struct udev_device *u_dev;
+ dev_t devnum;
+ char devt[BLK_DEV_SIZE];
+ char devname[FILE_NAME_SIZE];
+
+ u_dev = udev_device_new_from_syspath(udev, path);
+ assert_non_null(u_dev);
+ devnum = udev_device_get_devnum(u_dev);
+ snprintf(devt, sizeof(devt), "%d:%d",
+ major(devnum), minor(devnum));
+ r = devt2devname(devname, sizeof(devname), devt);
+ assert_int_equal(r, 0);
+ assert_string_equal(devname, udev_device_get_sysname(u_dev));
+ i++;
+ udev_device_unref(u_dev);
+ }
+ udev_enumerate_unref(enm);
+ condlog(2, "devt2devname test passed for %u block devices", i);
+}
+
+static int devt2devname_tests(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_devt2devname_devt_good),
+ cmocka_unit_test(test_devt2devname_devname_null),
+ cmocka_unit_test(test_devt2devname_length_0),
+ cmocka_unit_test(test_devt2devname_length_1),
+ cmocka_unit_test(test_devt2devname_devt_null),
+ cmocka_unit_test(test_devt2devname_devt_empty),
+ cmocka_unit_test(test_devt2devname_devt_invalid_1),
+ cmocka_unit_test(test_devt2devname_devt_invalid_2),
+ cmocka_unit_test(test_devt2devname_devt_invalid_3),
+ cmocka_unit_test(test_devt2devname_real),
+ };
+
+ return cmocka_run_group_tests(tests, setup, teardown);
+}
+
+int main(void)
+{
+ int ret = 0;
+
+ ret += devt2devname_tests();
+ return ret;
+}
int ioctx_count = 0;
struct io_event mock_events[AIO_GROUP_SIZE]; /* same as the checker max */
int ev_off = 0;
-struct timespec zero_timeout = {0};
+struct timespec zero_timeout = { .tv_sec = 0 };
struct timespec full_timeout = { .tv_sec = -1 };
int __real_ioctl(int fd, unsigned long request, void *argp);
/* tests initializing, then resetting, and then initializing again */
static void test_init_reset_init(void **state)
{
- struct checker c = {0};
+ struct checker c = {.cls = NULL};
struct aio_group *aio_grp, *tmp_grp;
assert_true(list_empty(&aio_grp_list));
static void test_init_free(void **state)
{
int i, count = 0;
- struct checker c[4096] = {0};
- struct aio_group *aio_grp;
+ struct checker c[4096] = {{.cls = NULL}};
+ struct aio_group *aio_grp = NULL;
assert_true(list_empty(&aio_grp_list));
will_return(__wrap_io_setup, 0);
static void test_multi_init_free(void **state)
{
int i, count;
- struct checker c[4096] = {0};
+ struct checker c[4096] = {{.cls = NULL}};
struct aio_group *aio_grp;
assert_true(list_empty(&aio_grp_list));
/* simple single checker sync test */
static void test_check_state_simple(void **state)
{
- struct checker c = {0};
+ struct checker c = {.cls = NULL};
struct async_req *req;
int res = 0;
/* test sync timeout */
static void test_check_state_timeout(void **state)
{
- struct checker c = {0};
+ struct checker c = {.cls = NULL};
struct aio_group *aio_grp;
assert_true(list_empty(&aio_grp_list));
/* test async timeout */
static void test_check_state_async_timeout(void **state)
{
- struct checker c = {0};
+ struct checker c = {.cls = NULL};
struct aio_group *aio_grp;
assert_true(list_empty(&aio_grp_list));
/* test freeing checkers with outstanding requests */
static void test_free_with_pending(void **state)
{
- struct checker c[2] = {0};
+ struct checker c[2] = {{.cls = NULL}};
struct aio_group *aio_grp;
struct async_req *req;
int res = 0;
/* test removing orpahed aio_group on free */
static void test_orphaned_aio_group(void **state)
{
- struct checker c[AIO_GROUP_SIZE] = {0};
+ struct checker c[AIO_GROUP_SIZE] = {{.cls = NULL}};
struct aio_group *aio_grp, *tmp_grp;
int i;
* checker */
static void test_timeout_cancel_failed(void **state)
{
- struct checker c[2] = {0};
+ struct checker c[2] = {{.cls = NULL}};
struct aio_group *aio_grp;
struct async_req *reqs[2];
int res[] = {0,0};
* checker */
static void test_async_timeout_cancel_failed(void **state)
{
- struct checker c[2] = {0};
+ struct checker c[2] = {{.cls = NULL}};
struct async_req *reqs[2];
int res[] = {0,0};
int i;
/* test orphaning a request, and having another checker clean it up */
static void test_orphan_checker_cleanup(void **state)
{
- struct checker c[2] = {0};
+ struct checker c[2] = {{.cls = NULL}};
struct async_req *reqs[2];
int res[] = {0,0};
struct aio_group *aio_grp;
static void test_check_state_blksize(void **state)
{
int i;
- struct checker c[3] = {0};
+ struct checker c[3] = {{.cls = NULL}};
int blksize[] = {4096, 1024, 512};
struct async_req *reqs[3];
int res[] = {0,1,0};
static void test_check_state_async(void **state)
{
int i;
- struct checker c[257] = {0};
+ struct checker c[257] = {{.cls = NULL}};
struct async_req *reqs[257];
int res[257] = {0};
#include "test-lib.h"
#include "print.h"
#include "util.h"
+#include "foreign.h"
#define N_CONF_FILES 2
free_hwt(*state);
*state = NULL;
+ cleanup_prio();
+ cleanup_checkers();
+ cleanup_foreign();
return 0;
}
/* "local" configuration */
hwtable = get_used_hwes(hwt->vecs->pathvec);
cfg1 = snprint_config(conf, NULL, hwtable, hwt->vecs->mpvec);
+ vector_free(hwtable);
}
assert_non_null(cfg1);
free_strvec(v);
}
+static void test19(void **state)
+{
+#define QUOTED19 "!value"
+ vector v = alloc_strvec("key \"" QUOTED19 "\"");
+ char *val;
+
+ assert_int_equal(VECTOR_SIZE(v), 4);
+ assert_string_equal(VECTOR_SLOT(v, 0), "key");
+ assert_true(is_quote(VECTOR_SLOT(v, 1)));
+ assert_string_equal(VECTOR_SLOT(v, 2), QUOTED19);
+ assert_true(is_quote(VECTOR_SLOT(v, 3)));
+ assert_int_equal(validate_config_strvec(v, test_file), 0);
+
+ val = set_value(v);
+ assert_string_equal(val, QUOTED19);
+
+ free(val);
+ free_strvec(v);
+}
+
+static void test20(void **state)
+{
+#define QUOTED20 "#value"
+ vector v = alloc_strvec("key \"" QUOTED20 "\"");
+ char *val;
+
+ assert_int_equal(VECTOR_SIZE(v), 4);
+ assert_string_equal(VECTOR_SLOT(v, 0), "key");
+ assert_true(is_quote(VECTOR_SLOT(v, 1)));
+ assert_string_equal(VECTOR_SLOT(v, 2), QUOTED20);
+ assert_true(is_quote(VECTOR_SLOT(v, 3)));
+ assert_int_equal(validate_config_strvec(v, test_file), 0);
+
+ val = set_value(v);
+ assert_string_equal(val, QUOTED20);
+
+ free(val);
+ free_strvec(v);
+}
+
int test_config_parser(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(test16),
cmocka_unit_test(test17),
cmocka_unit_test(test18),
+ cmocka_unit_test(test19),
+ cmocka_unit_test(test20),
};
return cmocka_run_group_tests(tests, setup, teardown);
}
#include "test-lib.h"
const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO);
-const char default_devnode[] = "sdTEST";
+const char default_devnode[] = "sdxTEST";
const char default_wwid[] = "TEST-WWID";
/* default_wwid should be a substring of default_wwid_1! */
const char default_wwid_1[] = "TEST-WWID-1";
return 0;
}
-bool __wrap_is_claimed_by_foreign(struct udev_device *ud)
-{
- condlog(5, "%s: %p", __func__, ud);
- return false;
-}
-
struct udev_list_entry
*__wrap_udev_device_get_properties_list_entry(struct udev_device *ud)
{
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
+#include <string.h>
#include <cmocka.h>
#include "log.h"
#include "test-log.h"
{
char buff[MAX_MSG_SIZE];
va_list ap;
+ char *expected;
- assert_int_equal(prio, mock_type(int));
+ check_expected(prio);
va_start(ap, fmt);
vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
va_end(ap);
- assert_string_equal(buff, mock_ptr_type(char *));
+ expected = mock_ptr_type(char *);
+ assert_memory_equal(buff, expected, strlen(expected));
}
void expect_condlog(int prio, char *string)
{
- will_return(__wrap_dlog, prio);
+ expect_value(__wrap_dlog, prio, prio);
will_return(__wrap_dlog, string);
}
-
#include "globals.c"
-/* Private prototypes missing in uevent.h */
-struct uevent * alloc_uevent(void);
-void uevent_get_wwid(struct uevent *uev);
-
/* Stringify helpers */
#define _str_(x) #x
#define str(x) _str_(x)
#include <setjmp.h>
#include <stdlib.h>
#include <cmocka.h>
+#include <endian.h>
#include "util.h"
#include "globals.c"
assert_int_equal(basenamecpy("baz/qux", NULL, sizeof(dst)), 0);
}
+static int test_basenamecpy(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_basenamecpy_good0),
+ cmocka_unit_test(test_basenamecpy_good1),
+ cmocka_unit_test(test_basenamecpy_good2),
+ cmocka_unit_test(test_basenamecpy_good3),
+ cmocka_unit_test(test_basenamecpy_good4),
+ cmocka_unit_test(test_basenamecpy_good5),
+ cmocka_unit_test(test_basenamecpy_good6),
+ cmocka_unit_test(test_basenamecpy_good7),
+ cmocka_unit_test(test_basenamecpy_bad0),
+ cmocka_unit_test(test_basenamecpy_bad1),
+ cmocka_unit_test(test_basenamecpy_bad2),
+ cmocka_unit_test(test_basenamecpy_bad3),
+ cmocka_unit_test(test_basenamecpy_bad4),
+ cmocka_unit_test(test_basenamecpy_bad5),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+/*
+ * On big endian systems, if bitfield_t is 32bit, we need
+ * to swap the two 32 bit parts of a 64bit value to make
+ * the tests below work.
+ */
+static uint64_t maybe_swap(uint64_t v)
+{
+ uint32_t *s = (uint32_t *)&v;
+
+ if (sizeof(bitfield_t) == 4)
+ /* this is identity for little endian */
+ return ((uint64_t)s[1] << 32) | s[0];
+ else
+ return v;
+}
+
static void test_bitmask_1(void **state)
{
- uint64_t arr[BITARR_SZ];
+ struct bitfield *bf;
+ uint64_t *arr;
int i, j, k, m, b;
- memset(arr, 0, sizeof(arr));
+ bf = alloc_bitfield(BITARR_SZ * 64);
+ assert_non_null(bf);
+ assert_int_equal(bf->len, BITARR_SZ * 64);
+ arr = (uint64_t *)bf->bits;
for (j = 0; j < BITARR_SZ; j++) {
for (i = 0; i < 64; i++) {
b = 64 * j + i;
- assert(!is_bit_set_in_array(b, arr));
- set_bit_in_array(b, arr);
+ assert(!is_bit_set_in_bitfield(b, bf));
+ set_bit_in_bitfield(b, bf);
for (k = 0; k < BITARR_SZ; k++) {
+#if 0
printf("b = %d j = %d k = %d a = %"PRIx64"\n",
b, j, k, arr[k]);
+#endif
if (k == j)
- assert_int_equal(arr[j], 1ULL << i);
+ assert_int_equal(maybe_swap(arr[j]), 1ULL << i);
else
assert_int_equal(arr[k], 0ULL);
}
for (m = 0; m < 64; m++)
if (i == m)
- assert(is_bit_set_in_array(64 * j + m,
- arr));
+ assert(is_bit_set_in_bitfield(64 * j + m,
+ bf));
else
- assert(!is_bit_set_in_array(64 * j + m,
- arr));
- clear_bit_in_array(b, arr);
- assert(!is_bit_set_in_array(b, arr));
+ assert(!is_bit_set_in_bitfield(64 * j + m,
+ bf));
+ clear_bit_in_bitfield(b, bf);
+ assert(!is_bit_set_in_bitfield(b, bf));
for (k = 0; k < BITARR_SZ; k++)
assert_int_equal(arr[k], 0ULL);
}
}
+ free(bf);
}
static void test_bitmask_2(void **state)
{
- uint64_t arr[BITARR_SZ];
+ struct bitfield *bf;
+ uint64_t *arr;
int i, j, k, m, b;
- memset(arr, 0, sizeof(arr));
+ bf = alloc_bitfield(BITARR_SZ * 64);
+ assert_non_null(bf);
+ assert_int_equal(bf->len, BITARR_SZ * 64);
+ arr = (uint64_t *)bf->bits;
for (j = 0; j < BITARR_SZ; j++) {
for (i = 0; i < 64; i++) {
b = 64 * j + i;
- assert(!is_bit_set_in_array(b, arr));
- set_bit_in_array(b, arr);
+ assert(!is_bit_set_in_bitfield(b, bf));
+ set_bit_in_bitfield(b, bf);
for (m = 0; m < 64; m++)
if (m <= i)
- assert(is_bit_set_in_array(64 * j + m,
- arr));
+ assert(is_bit_set_in_bitfield(64 * j + m,
+ bf));
else
- assert(!is_bit_set_in_array(64 * j + m,
- arr));
- assert(is_bit_set_in_array(b, arr));
+ assert(!is_bit_set_in_bitfield(64 * j + m,
+ bf));
+ assert(is_bit_set_in_bitfield(b, bf));
for (k = 0; k < BITARR_SZ; k++) {
if (k < j || (k == j && i == 63))
assert_int_equal(arr[k], ~0ULL);
assert_int_equal(arr[k], 0ULL);
else
assert_int_equal(
- arr[k],
+ maybe_swap(arr[k]),
(1ULL << (i + 1)) - 1);
}
}
for (j = 0; j < BITARR_SZ; j++) {
for (i = 0; i < 64; i++) {
b = 64 * j + i;
- assert(is_bit_set_in_array(b, arr));
- clear_bit_in_array(b, arr);
+ assert(is_bit_set_in_bitfield(b, bf));
+ clear_bit_in_bitfield(b, bf);
for (m = 0; m < 64; m++)
if (m <= i)
- assert(!is_bit_set_in_array(64 * j + m,
- arr));
+ assert(!is_bit_set_in_bitfield(64 * j + m,
+ bf));
else
- assert(is_bit_set_in_array(64 * j + m,
- arr));
- assert(!is_bit_set_in_array(b, arr));
+ assert(is_bit_set_in_bitfield(64 * j + m,
+ bf));
+ assert(!is_bit_set_in_bitfield(b, bf));
for (k = 0; k < BITARR_SZ; k++) {
if (k < j || (k == j && i == 63))
assert_int_equal(arr[k], 0ULL);
assert_int_equal(arr[k], ~0ULL);
else
assert_int_equal(
- arr[k],
+ maybe_swap(arr[k]),
~((1ULL << (i + 1)) - 1));
}
}
}
+ free(bf);
+}
+
+/*
+ * Test operations on a 0-length bitfield
+ */
+static void test_bitmask_len_0(void **state)
+{
+ struct bitfield *bf;
+
+ bf = alloc_bitfield(0);
+ assert_null(bf);
+}
+
+/*
+ * We use uint32_t in the "small bitmask" tests below.
+ * This means that we may have to swap 32bit words if bitfield_t
+ * is 64bit wide.
+ */
+static unsigned int maybe_swap_idx(unsigned int i)
+{
+ if (BYTE_ORDER == LITTLE_ENDIAN || sizeof(bitfield_t) == 4)
+ return i;
+ else
+ /* 0<->1, 2<->3, ... */
+ return i + (i % 2 == 0 ? 1 : -1);
+}
+
+static void _test_bitmask_small(unsigned int n)
+{
+ struct bitfield *bf;
+ uint32_t *arr;
+ unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
+
+ assert(sizeof(bitfield_t) == 4 || sizeof(bitfield_t) == 8);
+ assert(n <= 64);
+ assert(n >= 1);
+
+ bf = alloc_bitfield(n);
+ assert_non_null(bf);
+ assert_int_equal(bf->len, n);
+ arr = (uint32_t *)bf->bits;
+
+ for (i = 0; i < size; i++)
+ assert_int_equal(arr[i], 0);
+
+ set_bit_in_bitfield(n + 1, bf);
+ for (i = 0; i < size; i++)
+ assert_int_equal(arr[i], 0);
+
+ set_bit_in_bitfield(n, bf);
+ for (i = 0; i < size; i++)
+ assert_int_equal(arr[i], 0);
+
+ set_bit_in_bitfield(n - 1, bf);
+ for (i = 0; i < size; i++) {
+ unsigned int k = (n - 1) / 32;
+ unsigned int j = (n - 1) - k * 32;
+ unsigned int i1 = maybe_swap_idx(i);
+
+ if (i == k)
+ assert_int_equal(arr[i1], 1UL << j);
+ else
+ assert_int_equal(arr[i1], 0);
+ }
+
+ clear_bit_in_bitfield(n - 1, bf);
+ for (i = 0; i < size; i++)
+ assert_int_equal(arr[i], 0);
+
+ set_bit_in_bitfield(0, bf);
+ assert_int_equal(arr[maybe_swap_idx(0)], 1);
+ for (i = 1; i < size; i++)
+ assert_int_equal(arr[maybe_swap_idx(i)], 0);
+
+ free(bf);
}
-int test_basenamecpy(void)
+static void _test_bitmask_small_2(unsigned int n)
+{
+ struct bitfield *bf;
+ uint32_t *arr;
+ unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
+
+ assert(n <= 128);
+ assert(n >= 65);
+
+ bf = alloc_bitfield(n);
+ assert_non_null(bf);
+ assert_int_equal(bf->len, n);
+ arr = (uint32_t *)bf->bits;
+
+ for (i = 0; i < size; i++)
+ assert_int_equal(arr[i], 0);
+
+ set_bit_in_bitfield(n + 1, bf);
+ for (i = 0; i < size; i++)
+ assert_int_equal(arr[i], 0);
+
+ set_bit_in_bitfield(n, bf);
+ for (i = 0; i < size; i++)
+ assert_int_equal(arr[i], 0);
+
+ set_bit_in_bitfield(n - 1, bf);
+ assert_int_equal(arr[0], 0);
+ for (i = 0; i < size; i++) {
+ unsigned int k = (n - 1) / 32;
+ unsigned int j = (n - 1) - k * 32;
+ unsigned int i1 = maybe_swap_idx(i);
+
+ if (i == k)
+ assert_int_equal(arr[i1], 1UL << j);
+ else
+ assert_int_equal(arr[i1], 0);
+ }
+
+ set_bit_in_bitfield(0, bf);
+ for (i = 0; i < size; i++) {
+ unsigned int k = (n - 1) / 32;
+ unsigned int j = (n - 1) - k * 32;
+ unsigned int i1 = maybe_swap_idx(i);
+
+ if (i == k && k == 0)
+ assert_int_equal(arr[i1], (1UL << j) | 1);
+ else if (i == k)
+ assert_int_equal(arr[i1], 1UL << j);
+ else if (i == 0)
+ assert_int_equal(arr[i1], 1);
+ else
+ assert_int_equal(arr[i1], 0);
+ }
+
+ set_bit_in_bitfield(64, bf);
+ for (i = 0; i < size; i++) {
+ unsigned int k = (n - 1) / 32;
+ unsigned int j = (n - 1) - k * 32;
+ unsigned int i1 = maybe_swap_idx(i);
+
+ if (i == k && (k == 0 || k == 2))
+ assert_int_equal(arr[i1], (1UL << j) | 1);
+ else if (i == k)
+ assert_int_equal(arr[i1], 1UL << j);
+ else if (i == 2 || i == 0)
+ assert_int_equal(arr[i1], 1);
+ else
+ assert_int_equal(arr[i1], 0);
+ }
+
+ clear_bit_in_bitfield(0, bf);
+ for (i = 0; i < size; i++) {
+ unsigned int k = (n - 1) / 32;
+ unsigned int j = (n - 1) - k * 32;
+ unsigned int i1 = maybe_swap_idx(i);
+
+ if (i == k && k == 2)
+ assert_int_equal(arr[i1], (1UL << j) | 1);
+ else if (i == k)
+ assert_int_equal(arr[i1], 1UL << j);
+ else if (i == 2)
+ assert_int_equal(arr[i1], 1);
+ else
+ assert_int_equal(arr[i1], 0);
+ }
+
+ free(bf);
+}
+
+static void test_bitmask_len_1(void **state)
+{
+ _test_bitmask_small(1);
+}
+
+static void test_bitmask_len_2(void **state)
+{
+ _test_bitmask_small(2);
+}
+
+static void test_bitmask_len_3(void **state)
+{
+ _test_bitmask_small(3);
+}
+
+static void test_bitmask_len_23(void **state)
+{
+ _test_bitmask_small(23);
+}
+
+static void test_bitmask_len_63(void **state)
+{
+ _test_bitmask_small(63);
+}
+
+static void test_bitmask_len_64(void **state)
+{
+ _test_bitmask_small(63);
+}
+
+static void test_bitmask_len_65(void **state)
+{
+ _test_bitmask_small_2(65);
+}
+
+static void test_bitmask_len_66(void **state)
+{
+ _test_bitmask_small_2(66);
+}
+
+static void test_bitmask_len_67(void **state)
+{
+ _test_bitmask_small_2(67);
+}
+
+static void test_bitmask_len_103(void **state)
+{
+ _test_bitmask_small_2(103);
+}
+
+static void test_bitmask_len_126(void **state)
+{
+ _test_bitmask_small_2(126);
+}
+
+static void test_bitmask_len_127(void **state)
+{
+ _test_bitmask_small_2(127);
+}
+
+static void test_bitmask_len_128(void **state)
+{
+ _test_bitmask_small_2(128);
+}
+
+
+static int test_bitmasks(void)
{
const struct CMUnitTest tests[] = {
- cmocka_unit_test(test_basenamecpy_good0),
- cmocka_unit_test(test_basenamecpy_good1),
- cmocka_unit_test(test_basenamecpy_good2),
- cmocka_unit_test(test_basenamecpy_good3),
- cmocka_unit_test(test_basenamecpy_good4),
- cmocka_unit_test(test_basenamecpy_good5),
- cmocka_unit_test(test_basenamecpy_good6),
- cmocka_unit_test(test_basenamecpy_good7),
- cmocka_unit_test(test_basenamecpy_bad0),
- cmocka_unit_test(test_basenamecpy_bad1),
- cmocka_unit_test(test_basenamecpy_bad2),
- cmocka_unit_test(test_basenamecpy_bad3),
- cmocka_unit_test(test_basenamecpy_bad4),
- cmocka_unit_test(test_basenamecpy_bad5),
cmocka_unit_test(test_bitmask_1),
cmocka_unit_test(test_bitmask_2),
+ cmocka_unit_test(test_bitmask_len_0),
+ cmocka_unit_test(test_bitmask_len_1),
+ cmocka_unit_test(test_bitmask_len_2),
+ cmocka_unit_test(test_bitmask_len_3),
+ cmocka_unit_test(test_bitmask_len_23),
+ cmocka_unit_test(test_bitmask_len_63),
+ cmocka_unit_test(test_bitmask_len_64),
+ cmocka_unit_test(test_bitmask_len_65),
+ cmocka_unit_test(test_bitmask_len_66),
+ cmocka_unit_test(test_bitmask_len_67),
+ cmocka_unit_test(test_bitmask_len_103),
+ cmocka_unit_test(test_bitmask_len_126),
+ cmocka_unit_test(test_bitmask_len_127),
+ cmocka_unit_test(test_bitmask_len_128),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
-static const char src_str[] = "Hello";
+#define DST_STR "Hello"
+static const char dst_str[] = DST_STR;
+/* length of src_str and dst_str should be different */
+static const char src_str[] = " World";
+/* Must be big enough to hold dst_str and src_str */
+#define ARRSZ 16
+#define FILL '@'
/* strlcpy with length 0 */
static void test_strlcpy_0(void **state)
{
- char tst[] = "word";
+ char tst[] = DST_STR;
int rc;
rc = strlcpy(tst, src_str, 0);
assert_int_equal(rc, strlen(src_str));
- assert_string_equal(tst, "word");
+ assert_string_equal(tst, dst_str);
}
/* strlcpy with length 1 */
static void test_strlcpy_1(void **state)
{
- char tst[] = "word";
+ char tst[] = DST_STR;
int rc;
rc = strlcpy(tst, src_str, 1);
assert_int_equal(rc, strlen(src_str));
assert_int_equal(tst[0], '\0');
- assert_string_equal(tst + 1, "ord");
+ assert_string_equal(tst + 1, dst_str + 1);
}
/* strlcpy with length 2 */
static void test_strlcpy_2(void **state)
{
- char tst[] = "word";
+ char tst[] = DST_STR;
int rc;
rc = strlcpy(tst, src_str, 2);
assert_int_equal(rc, strlen(src_str));
assert_int_equal(tst[0], src_str[0]);
assert_int_equal(tst[1], '\0');
- assert_string_equal(tst + 2, "rd");
+ assert_string_equal(tst + 2, dst_str + 2);
}
/* strlcpy with dst length < src length */
static void test_strlcpy_3(void **state)
{
- char tst[] = "word";
+ char tst[] = DST_STR;
int rc;
rc = strlcpy(tst, src_str, sizeof(tst));
const int sz = sizeof(src_str);
tst = malloc(sz);
+ assert_non_null(tst);
memset(tst, 'f', sizeof(src_str));
rc = strlcpy(tst, src_str, sz);
const int sz = sizeof(src_str);
tst = malloc(sz + 2);
+ assert_non_null(tst);
memset(tst, 'f', sz + 2);
rc = strlcpy(tst, src_str, sz + 2);
/* strlcpy with empty src */
static void test_strlcpy_7(void **state)
{
- char tst[] = "word";
+ char tst[] = DST_STR;
static const char empty[] = "";
int rc;
rc = strlcpy(tst, empty, sizeof(tst));
assert_int_equal(rc, strlen(empty));
assert_string_equal(empty, tst);
- assert_string_equal(tst + 1, "ord");
+ assert_string_equal(tst + 1, dst_str + 1);
}
/* strlcpy with empty src, length 0 */
static void test_strlcpy_8(void **state)
{
- char tst[] = "word";
+ char tst[] = DST_STR;
static const char empty[] = "";
int rc;
rc = strlcpy(tst, empty, 0);
assert_int_equal(rc, strlen(empty));
- assert_string_equal("word", tst);
+ assert_string_equal(dst_str, tst);
}
static int test_strlcpy(void)
return cmocka_run_group_tests(tests, NULL, NULL);
}
+
+/* 0-terminated string, filled with non-0 after the terminator */
+static void prep_buf(char *buf, size_t size, const char *word)
+{
+ memset(buf, FILL, size);
+ assert_in_range(strlen(word), 0, size - 1);
+ memcpy(buf, word, strlen(word) + 1);
+}
+
+/* strlcat with size 0, dst not 0-terminated */
+static void test_strlcat_0(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, src_str, 0);
+ assert_int_equal(rc, strlen(src_str));
+ assert_string_equal(tst, dst_str);
+ assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with length 1, dst not 0-terminated */
+static void test_strlcat_1(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, src_str, 1);
+ assert_int_equal(rc, 1 + strlen(src_str));
+ assert_string_equal(tst, dst_str);
+ assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with length = dst - 1 */
+static void test_strlcat_2(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, src_str, strlen(dst_str));
+ assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+ assert_string_equal(tst, dst_str);
+ assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with length = dst */
+static void test_strlcat_3(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, src_str, strlen(dst_str) + 1);
+ assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+ assert_string_equal(tst, dst_str);
+ assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with len = dst + 1 */
+static void test_strlcat_4(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, src_str, strlen(dst_str) + 2);
+ assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+ assert_false(strncmp(tst, dst_str, strlen(dst_str)));
+ assert_int_equal(tst[strlen(dst_str)], src_str[0]);
+ assert_int_equal(tst[strlen(dst_str) + 1], '\0');
+ assert_int_equal(tst[strlen(dst_str) + 2], FILL);
+}
+
+/* strlcat with len = needed - 1 */
+static void test_strlcat_5(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, src_str, strlen(dst_str) + strlen(src_str));
+ assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+ assert_false(strncmp(tst, dst_str, strlen(dst_str)));
+ assert_false(strncmp(tst + strlen(dst_str), src_str,
+ strlen(src_str) - 1));
+ assert_int_equal(tst[strlen(dst_str) + strlen(src_str) - 1], '\0');
+ assert_int_equal(tst[strlen(dst_str) + strlen(src_str)], FILL);
+}
+
+/* strlcat with exactly sufficient space */
+static void test_strlcat_6(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, src_str, strlen(dst_str) + strlen(src_str) + 1);
+ assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+ assert_false(strncmp(tst, dst_str, strlen(dst_str)));
+ assert_string_equal(tst + strlen(dst_str), src_str);
+ assert_int_equal(tst[strlen(dst_str) + strlen(src_str) + 1], FILL);
+}
+
+/* strlcat with sufficient space */
+static void test_strlcat_7(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, src_str, sizeof(tst));
+ assert_int_equal(rc, strlen(src_str) + strlen(dst_str));
+ assert_false(strncmp(tst, dst_str, strlen(dst_str)));
+ assert_string_equal(tst + strlen(dst_str), src_str);
+}
+
+/* strlcat with 0-length string */
+static void test_strlcat_8(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), dst_str);
+ rc = strlcat(tst, "", sizeof(tst));
+ assert_int_equal(rc, strlen(dst_str));
+ assert_string_equal(tst, dst_str);
+ assert_int_equal(tst[sizeof(dst_str)], FILL);
+}
+
+/* strlcat with empty dst */
+static void test_strlcat_9(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), "");
+ rc = strlcat(tst, src_str, ARRSZ);
+ assert_int_equal(rc, strlen(src_str));
+ assert_string_equal(tst, src_str);
+ assert_int_equal(tst[sizeof(src_str)], FILL);
+}
+
+/* strlcat with empty dst and src */
+static void test_strlcat_10(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), "");
+ rc = strlcat(tst, "", ARRSZ);
+ assert_int_equal(rc, 0);
+ assert_string_equal(tst, "");
+ assert_int_equal(tst[1], FILL);
+}
+
+/* strlcat with no space to store 0 */
+static void test_strlcat_11(void **state)
+{
+ char tst[ARRSZ];
+ int rc;
+
+ prep_buf(tst, sizeof(tst), "");
+ tst[0] = FILL;
+ rc = strlcat(tst, src_str, 0);
+ assert_int_equal(rc, strlen(src_str));
+ assert_int_equal(tst[0], FILL);
+}
+
+static int test_strlcat(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_strlcat_0),
+ cmocka_unit_test(test_strlcat_1),
+ cmocka_unit_test(test_strlcat_2),
+ cmocka_unit_test(test_strlcat_3),
+ cmocka_unit_test(test_strlcat_4),
+ cmocka_unit_test(test_strlcat_5),
+ cmocka_unit_test(test_strlcat_6),
+ cmocka_unit_test(test_strlcat_7),
+ cmocka_unit_test(test_strlcat_8),
+ cmocka_unit_test(test_strlcat_9),
+ cmocka_unit_test(test_strlcat_10),
+ cmocka_unit_test(test_strlcat_11),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+static void test_strchop_nochop(void **state)
+{
+ char hello[] = "hello";
+
+ assert_int_equal(strchop(hello), 5);
+ assert_string_equal(hello, "hello");
+}
+
+static void test_strchop_newline(void **state)
+{
+ char hello[] = "hello\n";
+
+ assert_int_equal(strchop(hello), 5);
+ assert_string_equal(hello, "hello");
+}
+
+static void test_strchop_space(void **state)
+{
+ char hello[] = " ello ";
+
+ assert_int_equal(strchop(hello), 5);
+ assert_string_equal(hello, " ello");
+}
+
+static void test_strchop_mix(void **state)
+{
+ char hello[] = " el\no \t \n\n \t \n";
+
+ assert_int_equal(strchop(hello), 5);
+ assert_string_equal(hello, " el\no");
+}
+
+static void test_strchop_blank(void **state)
+{
+ char hello[] = " \t \n\n \t \n";
+
+ assert_int_equal(strchop(hello), 0);
+ assert_string_equal(hello, "");
+}
+
+static void test_strchop_empty(void **state)
+{
+ char hello[] = "";
+
+ assert_int_equal(strchop(hello), 0);
+ assert_string_equal(hello, "");
+}
+
+static int test_strchop(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_strchop_nochop),
+ cmocka_unit_test(test_strchop_newline),
+ cmocka_unit_test(test_strchop_space),
+ cmocka_unit_test(test_strchop_mix),
+ cmocka_unit_test(test_strchop_blank),
+ cmocka_unit_test(test_strchop_empty),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
int main(void)
{
int ret = 0;
ret += test_basenamecpy();
+ ret += test_bitmasks();
ret += test_strlcpy();
+ ret += test_strlcat();
+ ret += test_strchop();
return ret;
}
--- /dev/null
+/*
+ * Copyright (c) 2020 Benjamin Marzinski, Redhat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <cmocka.h>
+#include "globals.c"
+#include "util.h"
+#include "discovery.h"
+#include "wwids.h"
+#include "blacklist.h"
+#include "valid.h"
+
+int test_fd;
+struct udev_device {
+ int unused;
+} test_udev;
+
+bool __wrap_sysfs_is_multipathed(struct path *pp, bool set_wwid)
+{
+ bool is_multipathed = mock_type(bool);
+ assert_non_null(pp);
+ assert_int_not_equal(strlen(pp->dev), 0);
+ if (is_multipathed && set_wwid)
+ strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+ return is_multipathed;
+}
+
+int __wrap___mpath_connect(int nonblocking)
+{
+ bool connected = mock_type(bool);
+ assert_int_equal(nonblocking, 1);
+ if (connected)
+ return test_fd;
+ errno = mock_type(int);
+ return -1;
+}
+
+int __wrap_systemd_service_enabled(const char *dev)
+{
+ return (int)mock_type(bool);
+}
+
+/* There's no point in checking the return value here */
+int __wrap_mpath_disconnect(int fd)
+{
+ assert_int_equal(fd, test_fd);
+ return 0;
+}
+
+struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
+{
+ bool passed = mock_type(bool);
+ assert_string_equal(sysname, mock_ptr_type(char *));
+ if (passed)
+ return &test_udev;
+ return NULL;
+}
+
+int __wrap_pathinfo(struct path *pp, struct config *conf, int mask)
+{
+ int ret = mock_type(int);
+ assert_string_equal(pp->dev, mock_ptr_type(char *));
+ assert_int_equal(mask, DI_SYSFS | DI_WWID | DI_BLACKLIST);
+ if (ret == PATHINFO_OK) {
+ pp->uid_attribute = "ID_TEST";
+ strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+ } else
+ memset(pp->wwid, 0, WWID_SIZE);
+ return ret;
+}
+
+int __wrap_filter_property(struct config *conf, struct udev_device *udev,
+ int lvl, const char *uid_attribute)
+{
+ int ret = mock_type(int);
+ assert_string_equal(uid_attribute, "ID_TEST");
+ return ret;
+}
+
+int __wrap_is_failed_wwid(const char *wwid)
+{
+ int ret = mock_type(int);
+ assert_string_equal(wwid, mock_ptr_type(char *));
+ return ret;
+}
+
+int __wrap_check_wwids_file(char *wwid, int write_wwid)
+{
+ bool passed = mock_type(bool);
+ assert_int_equal(write_wwid, 0);
+ assert_string_equal(wwid, mock_ptr_type(char *));
+ if (passed)
+ return 0;
+ else
+ return -1;
+}
+
+int __wrap_dm_map_present_by_uuid(const char *uuid)
+{
+ int ret = mock_type(int);
+ assert_string_equal(uuid, mock_ptr_type(char *));
+ return ret;
+}
+
+enum {
+ STAGE_IS_MULTIPATHED,
+ STAGE_CHECK_MULTIPATHD,
+ STAGE_GET_UDEV_DEVICE,
+ STAGE_PATHINFO,
+ STAGE_FILTER_PROPERTY,
+ STAGE_IS_FAILED,
+ STAGE_CHECK_WWIDS,
+ STAGE_UUID_PRESENT,
+};
+
+enum {
+ CHECK_MPATHD_RUNNING,
+ CHECK_MPATHD_EAGAIN,
+ CHECK_MPATHD_ENABLED,
+ CHECK_MPATHD_SKIP,
+};
+
+/* setup the test to continue past the given stage in is_path_valid() */
+static void setup_passing(char *name, char *wwid, unsigned int check_multipathd,
+ unsigned int stage)
+{
+ will_return(__wrap_sysfs_is_multipathed, false);
+ if (stage == STAGE_IS_MULTIPATHED)
+ return;
+ if (check_multipathd == CHECK_MPATHD_RUNNING)
+ will_return(__wrap___mpath_connect, true);
+ else if (check_multipathd == CHECK_MPATHD_EAGAIN) {
+ will_return(__wrap___mpath_connect, false);
+ will_return(__wrap___mpath_connect, EAGAIN);
+ } else if (check_multipathd == CHECK_MPATHD_ENABLED) {
+ will_return(__wrap___mpath_connect, false);
+ will_return(__wrap___mpath_connect, ECONNREFUSED);
+ will_return(__wrap_systemd_service_enabled, true);
+ }
+ /* nothing for CHECK_MPATHD_SKIP */
+ if (stage == STAGE_CHECK_MULTIPATHD)
+ return;
+ will_return(__wrap_udev_device_new_from_subsystem_sysname, true);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname,
+ name);
+ if (stage == STAGE_GET_UDEV_DEVICE)
+ return;
+ will_return(__wrap_pathinfo, PATHINFO_OK);
+ will_return(__wrap_pathinfo, name);
+ will_return(__wrap_pathinfo, wwid);
+ if (stage == STAGE_PATHINFO)
+ return;
+ will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_EXCEPT);
+ if (stage == STAGE_FILTER_PROPERTY)
+ return;
+ will_return(__wrap_is_failed_wwid, WWID_IS_NOT_FAILED);
+ will_return(__wrap_is_failed_wwid, wwid);
+ if (stage == STAGE_IS_FAILED)
+ return;
+ will_return(__wrap_check_wwids_file, false);
+ will_return(__wrap_check_wwids_file, wwid);
+ if (stage == STAGE_CHECK_WWIDS)
+ return;
+ will_return(__wrap_dm_map_present_by_uuid, 0);
+ will_return(__wrap_dm_map_present_by_uuid, wwid);
+}
+
+static void test_bad_arguments(void **state)
+{
+ struct path pp;
+ char too_long[FILE_NAME_SIZE + 1];
+
+ memset(&pp, 0, sizeof(pp));
+ /* test NULL pointers */
+ assert_int_equal(is_path_valid("test", &conf, NULL, true),
+ PATH_IS_ERROR);
+ assert_int_equal(is_path_valid("test", NULL, &pp, true),
+ PATH_IS_ERROR);
+ assert_int_equal(is_path_valid(NULL, &conf, &pp, true),
+ PATH_IS_ERROR);
+ /* test undefined find_multipaths */
+ conf.find_multipaths = FIND_MULTIPATHS_UNDEF;
+ assert_int_equal(is_path_valid("test", &conf, &pp, true),
+ PATH_IS_ERROR);
+ /* test name too long */
+ memset(too_long, 'x', sizeof(too_long));
+ too_long[sizeof(too_long) - 1] = '\0';
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ assert_int_equal(is_path_valid(too_long, &conf, &pp, true),
+ PATH_IS_ERROR);
+}
+
+static void test_sysfs_is_multipathed(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test_wwid";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ /* test for already existing multiapthed device */
+ will_return(__wrap_sysfs_is_multipathed, true);
+ will_return(__wrap_sysfs_is_multipathed, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_VALID_NO_CHECK);
+ assert_string_equal(pp.dev, name);
+ assert_string_equal(pp.wwid, wwid);
+ /* test for wwid device with empty wwid */
+ will_return(__wrap_sysfs_is_multipathed, true);
+ will_return(__wrap_sysfs_is_multipathed, "");
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_ERROR);
+}
+
+static void test_check_multipathd(void **state)
+{
+ struct path pp;
+ char *name = "test";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ /* test failed check to see if multipathd is active */
+ will_return(__wrap_sysfs_is_multipathed, false);
+ will_return(__wrap___mpath_connect, false);
+ will_return(__wrap___mpath_connect, ECONNREFUSED);
+ will_return(__wrap_systemd_service_enabled, false);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_NOT_VALID);
+ assert_string_equal(pp.dev, name);
+ /* test pass because service is enabled. fail getting udev */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, NULL, CHECK_MPATHD_ENABLED, STAGE_CHECK_MULTIPATHD);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname,
+ name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_ERROR);
+ assert_string_equal(pp.dev, name);
+ /* test pass because connect returned EAGAIN. fail getting udev */
+ setup_passing(name, NULL, CHECK_MPATHD_EAGAIN, STAGE_CHECK_MULTIPATHD);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname,
+ name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_ERROR);
+ /* test pass because connect succeeded. fail getting udev */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, NULL, CHECK_MPATHD_RUNNING, STAGE_CHECK_MULTIPATHD);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname,
+ name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_ERROR);
+ assert_string_equal(pp.dev, name);
+}
+
+static void test_pathinfo(void **state)
+{
+ struct path pp;
+ char *name = "test";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ /* Test pathinfo blacklisting device */
+ setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+ will_return(__wrap_pathinfo, PATHINFO_SKIPPED);
+ will_return(__wrap_pathinfo, name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ /* Test pathinfo failing */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+ will_return(__wrap_pathinfo, PATHINFO_FAILED);
+ will_return(__wrap_pathinfo, name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_ERROR);
+ /* Test blank wwid */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+ will_return(__wrap_pathinfo, PATHINFO_OK);
+ will_return(__wrap_pathinfo, name);
+ will_return(__wrap_pathinfo, "");
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+}
+
+static void test_filter_property(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ /* test blacklist property */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+ will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+ /* test missing property */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+ will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_MISSING);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ /* test MATCH_NOTHING fail on is_failed_wwid */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+ will_return(__wrap_filter_property, MATCH_NOTHING);
+ will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
+ will_return(__wrap_is_failed_wwid, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+}
+
+static void test_is_failed_wwid(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ /* Test wwid failed */
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY);
+ will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
+ will_return(__wrap_is_failed_wwid, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+ /* test is_failed_wwid error */
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY);
+ will_return(__wrap_is_failed_wwid, WWID_FAILED_ERROR);
+ will_return(__wrap_is_failed_wwid, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_ERROR);
+}
+
+static void test_greedy(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ /* test greedy success with checking multipathd */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_GREEDY;
+ setup_passing(name, wwid, CHECK_MPATHD_RUNNING, STAGE_IS_FAILED);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+ /* test greedy success without checking multiapthd */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_IS_FAILED);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_VALID);
+}
+
+static void test_check_wwids(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ setup_passing(name, wwid, CHECK_MPATHD_EAGAIN, STAGE_IS_FAILED);
+ will_return(__wrap_check_wwids_file, true);
+ will_return(__wrap_check_wwids_file, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_VALID_NO_CHECK);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+}
+
+static void test_check_uuid_present(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ setup_passing(name, wwid, CHECK_MPATHD_ENABLED, STAGE_CHECK_WWIDS);
+ will_return(__wrap_dm_map_present_by_uuid, 1);
+ will_return(__wrap_dm_map_present_by_uuid, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+}
+
+
+static void test_find_multipaths(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ /* test find_multipaths = FIND_MULTIPATHS_STRICT */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+ /* test find_multipaths = FIND_MULTIPATHS_OFF */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_OFF;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ /* test find_multipaths = FIND_MULTIPATHS_ON */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_ON;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ /* test find_multipaths = FIND_MULTIPATHS_SMART */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_SMART;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_MAYBE_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+}
+
+int test_valid(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_bad_arguments),
+ cmocka_unit_test(test_sysfs_is_multipathed),
+ cmocka_unit_test(test_check_multipathd),
+ cmocka_unit_test(test_pathinfo),
+ cmocka_unit_test(test_filter_property),
+ cmocka_unit_test(test_is_failed_wwid),
+ cmocka_unit_test(test_greedy),
+ cmocka_unit_test(test_check_wwids),
+ cmocka_unit_test(test_check_uuid_present),
+ cmocka_unit_test(test_find_multipaths),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+int main(void)
+{
+ int ret = 0;
+ ret += test_valid();
+ return ret;
+}
char wwid[WWID_SIZE];
};
+static regex_t space_re;
static int setup(void **state)
{
struct vpdtest *vt = malloc(sizeof(*vt));
+ int rc;
if (vt == NULL)
return -1;
*state = vt;
+ rc = regcomp(&space_re, " +", REG_EXTENDED);
+ assert_int_equal(rc, 0);
return 0;
}
free(vt);
*state = NULL;
+ regfree(&space_re);
return 0;
}
{
char *dst = calloc(1, strlen(src) + 1);
char *p;
- static regex_t *re;
regmatch_t match;
- int rc;
+ int rc = 0;
assert_non_null(dst);
- if (re == NULL) {
- re = calloc(1, sizeof(*re));
- assert_non_null(re);
- rc = regcomp(re, " +", REG_EXTENDED);
- assert_int_equal(rc, 0);
- }
- for (rc = regexec(re, src, 1, &match, 0), p = dst;
+ for (rc = regexec(&space_re, src, 1, &match, 0), p = dst;
rc == 0;
- src += match.rm_eo, rc = regexec(re, src, 1, &match, 0)) {
+ src += match.rm_eo, rc = regexec(&space_re, src, 1, &match, 0)) {
memcpy(p, src, match.rm_so);
p += match.rm_so;
*p = '_';