summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJinWang An <jinwang.an@samsung.com>2023-01-13 15:40:26 +0900
committerJinWang An <jinwang.an@samsung.com>2023-01-13 15:40:26 +0900
commit04a3d5d40c15f411ed71c6c7d28b5aa133bff4e8 (patch)
tree770dfdd842044ad918b6330061d6f4c2f0d31909
parent8f0f489d4cfb845f4a5ef00f84d20ddcee0fb228 (diff)
downloadmultipath-tools-04a3d5d40c15f411ed71c6c7d28b5aa133bff4e8.tar.gz
multipath-tools-04a3d5d40c15f411ed71c6c7d28b5aa133bff4e8.tar.bz2
multipath-tools-04a3d5d40c15f411ed71c6c7d28b5aa133bff4e8.zip
Imported Upstream version 0.9.2upstream/0.9.2
-rw-r--r--.gitignore2
-rw-r--r--Makefile.inc4
-rw-r--r--README.md15
-rw-r--r--kpartx/kpartx.82
-rw-r--r--kpartx/kpartx.c11
-rw-r--r--libmpathpersist/mpath_persistent_reserve_in.32
-rw-r--r--libmpathpersist/mpath_persistent_reserve_out.32
-rw-r--r--libmpathutil/parser.c8
-rw-r--r--libmultipath/configure.c28
-rw-r--r--libmultipath/defaults.h2
-rw-r--r--libmultipath/discovery.c31
-rw-r--r--libmultipath/dmparser.c2
-rw-r--r--libmultipath/libmultipath.version2
-rw-r--r--libmultipath/print.c6
-rw-r--r--libmultipath/propsel.c55
-rw-r--r--libmultipath/structs.c155
-rw-r--r--libmultipath/structs.h44
-rw-r--r--libmultipath/structs_vec.c7
-rw-r--r--libmultipath/version.h4
-rw-r--r--mpathpersist/main.c6
-rw-r--r--mpathpersist/mpathpersist.82
-rw-r--r--multipath/Makefile11
-rw-r--r--multipath/multipath.82
-rw-r--r--multipath/multipath.conf.523
-rw-r--r--multipath/multipath.rules.in (renamed from multipath/multipath.rules)4
-rw-r--r--multipath/tmpfiles.conf.in1
-rw-r--r--multipathd/callbacks.c126
-rw-r--r--multipathd/cli.c223
-rw-r--r--multipathd/cli.h191
-rw-r--r--multipathd/cli_handlers.c70
-rw-r--r--multipathd/fpin_handlers.c2
-rw-r--r--multipathd/multipathc.c185
-rw-r--r--multipathd/uxlsnr.c15
-rw-r--r--tests/Makefile8
-rw-r--r--tests/cli.c237
-rw-r--r--tests/features.c549
36 files changed, 1515 insertions, 522 deletions
diff --git a/.gitignore b/.gitignore
index 821c3e6..83f8a55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,8 @@ cscope.files
cscope.out
kpartx/kpartx
multipath/multipath
+multipath/multipath.rules
+multipath/tmpfiles.conf
multipathd/multipathd
multipathd/multipathc
mpathpersist/mpathpersist
diff --git a/Makefile.inc b/Makefile.inc
index 6399beb..5602506 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -83,9 +83,11 @@ exec_prefix = $(prefix)
usr_prefix = $(prefix)
bindir = $(exec_prefix)/sbin
libudevdir = $(prefix)/$(SYSTEMDPATH)/udev
+tmpfilesdir = $(prefix)/$(SYSTEMDPATH)/tmpfiles.d
udevrulesdir = $(libudevdir)/rules.d
modulesloaddir = $(prefix)/$(SYSTEMDPATH)/modules-load.d
multipathdir = $(TOPDIR)/libmultipath
+daemondir = $(TOPDIR)/multipathd
mpathutildir = $(TOPDIR)/libmpathutil
man8dir = $(prefix)/usr/share/man/man8
man5dir = $(prefix)/usr/share/man/man5
@@ -104,6 +106,7 @@ includedir = $(prefix)/usr/include
pkgconfdir = $(usrlibdir)/pkgconfig
plugindir := $(prefix)/$(LIB)/multipath
configdir := $(prefix)/etc/multipath/conf.d
+runtimedir := /$(RUN)
GZIP_PROG = gzip -9 -c
RM = rm -f
@@ -148,6 +151,7 @@ WARNFLAGS := -Werror -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implici
$(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS)
CPPFLAGS := $(FORTIFY_OPT) \
-DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(plugindir)\" -DRUN_DIR=\"${RUN}\" \
+ -DRUNTIME_DIR=\"$(runtimedir)\" \
-DCONFIG_DIR=\"$(configdir)\" -DEXTRAVERSION=\"$(EXTRAVERSION)\" -MMD -MP
CFLAGS := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
BIN_CFLAGS = -fPIE -DPIE
diff --git a/README.md b/README.md
index b05b133..d003e1d 100644
--- a/README.md
+++ b/README.md
@@ -52,10 +52,17 @@ To get latest devel code:
Building multipath-tools
========================
-Prerequisites: development packages of for `libdevmapper`, `libreadline`,
-`libaio`, `libudev`, `libjson-c`, `liburcu`, and `libsystemd`.
+Prerequisites: development packages of for `libdevmapper`, `libaio`, `libudev`,
+`libjson-c`, `liburcu`, and `libsystemd`.
-To build multipath-tools, type:
+To enable commandline history and TAB completion in the interactive mode *(which
+is entered with `multipathd -k` or `multipathc`)* you might also set `READLINE`
+make variable to `libedit` or `libreadline`, like `make READLINE=libreadline`.
+That requires a development package for the library you chose. Note that using
+libreadline may [make binary indistributable due to license
+incompatibility](https://github.com/opensvc/multipath-tools/issues/36).
+
+Then, build and install multipath-tools with:
make
make DESTDIR="/my/target/dir" install
@@ -165,7 +172,7 @@ To enable ALUA, the following options should be changed:
"LUN Affinity" and "ALUA" should be changed to "Enable", "Redundancy Type"
must be "Active-Active".
-- LSI/Engenio/NetApp RDAC class, as NetApp SANtricity E/EF Series and OEM arrays:
+- LSI/Engenio/NetApp RDAC class, as NetApp SANtricity E/EF Series and rebranded arrays:
"Select operating system:" should be changed to "Linux DM-MP (Kernel 3.10 or later)".
- NetApp ONTAP:
diff --git a/kpartx/kpartx.8 b/kpartx/kpartx.8
index 08bb349..2b144a7 100644
--- a/kpartx/kpartx.8
+++ b/kpartx/kpartx.8
@@ -5,7 +5,7 @@
.\"
.\" ----------------------------------------------------------------------------
.
-.TH KPARTX 8 2016-10-28 "Linux"
+.TH KPARTX 8 2019-04-27 "Linux"
.
.
.\" ----------------------------------------------------------------------------
diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c
index 3c49999..1d568c9 100644
--- a/kpartx/kpartx.c
+++ b/kpartx/kpartx.c
@@ -441,12 +441,7 @@ main(int argc, char **argv){
if (n >= 0)
printf("%s: %d slices\n", ptp->type, n);
#endif
-
- if (n > 0) {
- close(fd);
- fd = -1;
- }
- else
+ if (n <= 0)
continue;
switch(what) {
@@ -666,9 +661,9 @@ main(int argc, char **argv){
if (n > 0)
break;
}
+ if (fd != -1)
+ close(fd);
if (what == LIST && loopcreated) {
- if (fd != -1)
- close(fd);
if (del_loop(device)) {
if (verbose)
fprintf(stderr, "can't del loop : %s\n",
diff --git a/libmpathpersist/mpath_persistent_reserve_in.3 b/libmpathpersist/mpath_persistent_reserve_in.3
index 4691bde..c168cae 100644
--- a/libmpathpersist/mpath_persistent_reserve_in.3
+++ b/libmpathpersist/mpath_persistent_reserve_in.3
@@ -5,7 +5,7 @@
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MPATH_PERSISTENT_RESERVE_IN 3 2016-11-01 "Linux"
+.TH MPATH_PERSISTENT_RESERVE_IN 3 2018-06-15 "Linux"
.
.
.\" ----------------------------------------------------------------------------
diff --git a/libmpathpersist/mpath_persistent_reserve_out.3 b/libmpathpersist/mpath_persistent_reserve_out.3
index 55b00b0..f20be31 100644
--- a/libmpathpersist/mpath_persistent_reserve_out.3
+++ b/libmpathpersist/mpath_persistent_reserve_out.3
@@ -5,7 +5,7 @@
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MPATH_PERSISTENT_RESERVE_OUT 3 2016-11-01 "Linux"
+.TH MPATH_PERSISTENT_RESERVE_OUT 3 2018-06-15 "Linux"
.
.
.\" ----------------------------------------------------------------------------
diff --git a/libmpathutil/parser.c b/libmpathutil/parser.c
index 014d9b8..8d3ac53 100644
--- a/libmpathutil/parser.c
+++ b/libmpathutil/parser.c
@@ -152,7 +152,7 @@ int
snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
const void *data)
{
- int r;
+ int r = 0;
char *f;
struct config *conf;
STRBUF_ON_STACK(sbuf);
@@ -190,8 +190,10 @@ snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
}
} while (*fmt++);
out:
- return __append_strbuf_str(buff, get_strbuf_str(&sbuf),
- get_strbuf_len(&sbuf));
+ if (r >= 0)
+ r = __append_strbuf_str(buff, get_strbuf_str(&sbuf),
+ get_strbuf_len(&sbuf));
+ return r;
}
static const char quote_marker[] = { '\0', '"', '\0' };
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 8af7cd7..e5249fc 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -218,10 +218,11 @@ int rr_optimize_path_order(struct pathgroup *pgp)
total_paths = VECTOR_SIZE(pgp->paths);
vector_foreach_slot(pgp->paths, pp, i) {
- if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP &&
- pp->sg_id.proto_id != SCSI_PROTOCOL_SAS &&
- pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI &&
- pp->sg_id.proto_id != SCSI_PROTOCOL_SRP) {
+ if (pp->bus != SYSFS_BUS_SCSI ||
+ (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP &&
+ pp->sg_id.proto_id != SCSI_PROTOCOL_SAS &&
+ pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI &&
+ pp->sg_id.proto_id != SCSI_PROTOCOL_SRP)) {
/* return success as default path order
* is maintained in path group
*/
@@ -259,6 +260,7 @@ int rr_optimize_path_order(struct pathgroup *pgp)
int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
{
struct pathgroup * pgp;
+ struct path *pp;
struct config *conf;
int i, marginal_pathgroups;
char *save_attr;
@@ -274,6 +276,14 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
if (mpp->disable_queueing && VECTOR_SIZE(mpp->paths) != 0)
mpp->disable_queueing = 0;
+ /* Force QUEUE_MODE_BIO for maps with nvme:tcp paths */
+ vector_foreach_slot(mpp->paths, pp, i) {
+ if (pp->bus == SYSFS_BUS_NVME &&
+ pp->sg_id.proto_id == NVME_PROTOCOL_TCP) {
+ mpp->queue_mode = QUEUE_MODE_BIO;
+ break;
+ }
+ }
/*
* If this map was created with add_map_without_path(),
* mpp->hwe might not be set yet.
@@ -1075,6 +1085,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
struct config *conf = NULL;
int allow_queueing;
struct bitfield *size_mismatch_seen;
+ struct multipath * cmpp;
/* ignore refwwid if it's empty */
if (refwwid && !strlen(refwwid))
@@ -1146,6 +1157,13 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
continue;
}
+ cmpp = find_mp_by_wwid(curmp, pp1->wwid);
+ if (cmpp && cmpp->queue_mode == QUEUE_MODE_RQ &&
+ pp1->bus == SYSFS_BUS_NVME && pp1->sg_id.proto_id ==
+ NVME_PROTOCOL_TCP) {
+ orphan_path(pp1, "nvme:tcp path not allowed with request queue_mode multipath device");
+ continue;
+ }
/*
* at this point, we know we really got a new mp
*/
@@ -1184,6 +1202,8 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
}
verify_paths(mpp);
+ if (cmpp)
+ mpp->queue_mode = cmpp->queue_mode;
if (setup_map(mpp, &params, vecs)) {
remove_map(mpp, vecs->pathvec, NULL);
continue;
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
index 7979f20..3d552b3 100644
--- a/libmultipath/defaults.h
+++ b/libmultipath/defaults.h
@@ -68,7 +68,7 @@
#define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings"
#define DEFAULT_WWIDS_FILE "/etc/multipath/wwids"
#define DEFAULT_PRKEYS_FILE "/etc/multipath/prkeys"
-#define MULTIPATH_SHM_BASE "/dev/shm/multipath/"
+#define MULTIPATH_SHM_BASE RUNTIME_DIR "/multipath/"
static inline char *set_default(char *str)
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 15560f8..f3fcced 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -504,10 +504,11 @@ int sysfs_get_host_adapter_name(const struct path *pp, char *adapter_name)
proto_id = pp->sg_id.proto_id;
- if (proto_id != SCSI_PROTOCOL_FCP &&
- proto_id != SCSI_PROTOCOL_SAS &&
- proto_id != SCSI_PROTOCOL_ISCSI &&
- proto_id != SCSI_PROTOCOL_SRP) {
+ if (pp->bus != SYSFS_BUS_SCSI ||
+ (proto_id != SCSI_PROTOCOL_FCP &&
+ proto_id != SCSI_PROTOCOL_SAS &&
+ proto_id != SCSI_PROTOCOL_ISCSI &&
+ proto_id != SCSI_PROTOCOL_SRP)) {
return 1;
}
/* iscsi doesn't have adapter info in sysfs
@@ -1538,6 +1539,7 @@ nvme_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
struct udev_device *parent;
const char *attr_path = NULL;
const char *attr;
+ int i;
if (pp->udev)
attr_path = udev_device_get_sysname(pp->udev);
@@ -1560,6 +1562,18 @@ nvme_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
attr = udev_device_get_sysattr_value(parent, "cntlid");
pp->sg_id.channel = attr ? atoi(attr) : 0;
+ attr = udev_device_get_sysattr_value(parent, "transport");
+ if (attr) {
+ for (i = 0; i < NVME_PROTOCOL_UNSPEC; i++){
+ if (protocol_name[SYSFS_BUS_NVME + i] &&
+ !strcmp(attr,
+ protocol_name[SYSFS_BUS_NVME + i] + 5)) {
+ pp->sg_id.proto_id = i;
+ break;
+ }
+ }
+ }
+
snprintf(pp->vendor_id, SCSI_VENDOR_SIZE, "NVME");
snprintf(pp->product_id, PATH_PRODUCT_SIZE, "%s",
udev_device_get_sysattr_value(parent, "model"));
@@ -1810,11 +1824,14 @@ sysfs_pathinfo(struct path *pp, const struct _vector *hwtable)
pp->bus = SYSFS_BUS_CCISS;
if (!strncmp(pp->dev,"dasd", 4))
pp->bus = SYSFS_BUS_CCW;
- if (!strncmp(pp->dev,"sd", 2))
+ if (!strncmp(pp->dev,"sd", 2)) {
pp->bus = SYSFS_BUS_SCSI;
- if (!strncmp(pp->dev,"nvme", 4))
+ pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
+ }
+ if (!strncmp(pp->dev,"nvme", 4)) {
pp->bus = SYSFS_BUS_NVME;
-
+ pp->sg_id.proto_id = NVME_PROTOCOL_UNSPEC;
+ }
switch (pp->bus) {
case SYSFS_BUS_SCSI:
return scsi_sysfs_pathinfo(pp, hwtable);
diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c
index 50d13c0..3b37a92 100644
--- a/libmultipath/dmparser.c
+++ b/libmultipath/dmparser.c
@@ -151,6 +151,8 @@ int disassemble_map(const struct _vector *pathvec,
free(word);
}
+ mpp->queue_mode = strstr(mpp->features, "queue_mode bio") ?
+ QUEUE_MODE_BIO : QUEUE_MODE_RQ;
/*
* hwhandler
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 8a447f7..3d86ecb 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -31,7 +31,7 @@
* The new version inherits the previous ones.
*/
-LIBMULTIPATH_16.0.0 {
+LIBMULTIPATH_17.0.0 {
global:
/* symbols referenced by multipath and multipathd */
add_foreign;
diff --git a/libmultipath/print.c b/libmultipath/print.c
index 68a793e..d7d522c 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -650,7 +650,8 @@ snprint_host_attr (struct strbuf *buff, const struct path * pp, char *attr)
const char *value = NULL;
int ret;
- if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
+ if (pp->bus != SYSFS_BUS_SCSI ||
+ pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
return append_strbuf_str(buff, "[undef]");
sprintf(host_id, "host%d", pp->sg_id.host_no);
host_dev = udev_device_new_from_subsystem_sysname(udev, "fc_host",
@@ -689,7 +690,8 @@ snprint_tgt_wwpn (struct strbuf *buff, const struct path * pp)
const char *value = NULL;
int ret;
- if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
+ if (pp->bus != SYSFS_BUS_SCSI ||
+ pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
return append_strbuf_str(buff, "[undef]");
sprintf(rport_id, "rport-%d:%d-%d",
pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index 98e3aad..d4f1989 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -26,6 +26,7 @@
#include "strbuf.h"
#include <inttypes.h>
#include <libudev.h>
+#include <ctype.h>
pgpolicyfn *pgpolicies[] = {
NULL,
@@ -413,6 +414,59 @@ void reconcile_features_with_options(const char *id, char **features, int* no_pa
}
}
+static void reconcile_features_with_queue_mode(struct multipath *mp)
+{
+ char *space = NULL, *val = NULL, *mode_str = NULL, *feat;
+ int features_mode = QUEUE_MODE_UNDEF;
+
+ if (!mp->features)
+ return;
+
+ pthread_cleanup_push(cleanup_free_ptr, &space);
+ pthread_cleanup_push(cleanup_free_ptr, &val);
+ pthread_cleanup_push(cleanup_free_ptr, &mode_str);
+
+ if (!(feat = strstr(mp->features, "queue_mode")) ||
+ feat == mp->features || !isspace(*(feat - 1)) ||
+ sscanf(feat, "queue_mode%m[ \f\n\r\t\v]%ms", &space, &val) != 2)
+ goto sync_mode;
+ if (asprintf(&mode_str, "queue_mode%s%s", space, val) < 0) {
+ condlog(1, "failed to allocate space for queue_mode feature string");
+ mode_str = NULL; /* value undefined on failure */
+ goto exit;
+ }
+
+ if (!strcmp(val, "rq") || !strcmp(val, "mq"))
+ features_mode = QUEUE_MODE_RQ;
+ else if (!strcmp(val, "bio"))
+ features_mode = QUEUE_MODE_BIO;
+ if (features_mode == QUEUE_MODE_UNDEF) {
+ condlog(2, "%s: ignoring invalid feature '%s'",
+ mp->alias, mode_str);
+ goto sync_mode;
+ }
+
+ if (mp->queue_mode == QUEUE_MODE_UNDEF)
+ mp->queue_mode = features_mode;
+ if (mp->queue_mode == features_mode)
+ goto exit;
+
+ condlog(2,
+ "%s: ignoring feature '%s' because queue_mode is set to '%s'",
+ mp->alias, mode_str,
+ (mp->queue_mode == QUEUE_MODE_RQ)? "rq" : "bio");
+
+sync_mode:
+ if (mode_str)
+ remove_feature(&mp->features, mode_str);
+ if (mp->queue_mode == QUEUE_MODE_BIO)
+ add_feature(&mp->features, "queue_mode bio");
+exit:
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+ pthread_cleanup_pop(1);
+}
+
int select_features(struct config *conf, struct multipath *mp)
{
const char *origin;
@@ -428,6 +482,7 @@ out:
reconcile_features_with_options(mp->alias, &mp->features,
&mp->no_path_retry,
&mp->retain_hwhandler);
+ reconcile_features_with_queue_mode(mp);
condlog(3, "%s: features = \"%s\" %s", mp->alias, mp->features, origin);
return 0;
}
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 49621cb..7a2ff58 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -6,6 +6,7 @@
#include <unistd.h>
#include <libdevmapper.h>
#include <libudev.h>
+#include <ctype.h>
#include "checkers.h"
#include "vector.h"
@@ -24,7 +25,6 @@ const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = {
[SYSFS_BUS_UNDEF] = "undef",
[SYSFS_BUS_CCW] = "ccw",
[SYSFS_BUS_CCISS] = "cciss",
- [SYSFS_BUS_NVME] = "nvme",
[SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp",
[SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi",
[SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa",
@@ -36,6 +36,13 @@ const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = {
[SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata",
[SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb",
[SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec",
+ [SYSFS_BUS_NVME + NVME_PROTOCOL_PCIE] = "nvme:pcie",
+ [SYSFS_BUS_NVME + NVME_PROTOCOL_RDMA] = "nvme:rdma",
+ [SYSFS_BUS_NVME + NVME_PROTOCOL_FC] = "nvme:fc",
+ [SYSFS_BUS_NVME + NVME_PROTOCOL_TCP] = "nvme:tcp",
+ [SYSFS_BUS_NVME + NVME_PROTOCOL_LOOP] = "nvme:loop",
+ [SYSFS_BUS_NVME + NVME_PROTOCOL_APPLE_NVME] = "nvme:apple-nvme",
+ [SYSFS_BUS_NVME + NVME_PROTOCOL_UNSPEC] = "nvme:unspec",
};
struct adapter_group *
@@ -115,7 +122,7 @@ alloc_path (void)
pp->sg_id.channel = -1;
pp->sg_id.scsi_id = -1;
pp->sg_id.lun = SCSI_INVALID_LUN;
- pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
+ pp->sg_id.proto_id = PROTOCOL_UNSET;
pp->fd = -1;
pp->tpgs = TPGS_UNDEF;
pp->priority = PRIO_UNDEF;
@@ -601,23 +608,33 @@ int add_feature(char **f, const char *n)
{
int c = 0, d, l;
char *e, *t;
+ const char *p;
if (!f)
return 1;
/* Nothing to do */
- if (!n || *n == '0')
+ if (!n || *n == '\0')
return 0;
- if (strchr(n, ' ') != NULL) {
- condlog(0, "internal error: feature \"%s\" contains spaces", n);
+ l = strlen(n);
+ if (isspace(*n) || isspace(*(n + l - 1))) {
+ condlog(0, "internal error: feature \"%s\" has leading or trailing spaces", n);
return 1;
}
+ p = n;
+ d = 1;
+ while (*p != '\0') {
+ if (isspace(*p) && !isspace(*(p + 1)) && *(p + 1) != '\0')
+ d++;
+ p++;
+ }
+
/* default feature is null */
if(!*f)
{
- l = asprintf(&t, "1 %s", n);
+ l = asprintf(&t, "%0d %s", d, n);
if(l == -1)
return 1;
@@ -626,35 +643,24 @@ int add_feature(char **f, const char *n)
}
/* Check if feature is already present */
- if (strstr(*f, n))
- return 0;
+ e = *f;
+ while ((e = strstr(e, n)) != NULL) {
+ if (isspace(*(e - 1)) &&
+ (isspace(*(e + l)) || *(e + l) == '\0'))
+ return 0;
+ e += l;
+ }
/* Get feature count */
c = strtoul(*f, &e, 10);
- if (*f == e || (*e != ' ' && *e != '\0')) {
+ if (*f == e || (!isspace(*e) && *e != '\0')) {
condlog(0, "parse error in feature string \"%s\"", *f);
return 1;
}
-
- /* Add 1 digit and 1 space */
- l = strlen(e) + strlen(n) + 2;
-
- c++;
- /* Check if we need more digits for feature count */
- for (d = c; d >= 10; d /= 10)
- l++;
-
- t = calloc(1, l + 1);
- if (!t)
+ c += d;
+ if (asprintf(&t, "%0d%s %s", c, e, n) < 0)
return 1;
- /* e: old feature string with leading space, or "" */
- if (*e == ' ')
- while (*(e + 1) == ' ')
- e++;
-
- snprintf(t, l + 1, "%0d%s %s", c, e, n);
-
free(*f);
*f = t;
@@ -663,7 +669,7 @@ int add_feature(char **f, const char *n)
int remove_feature(char **f, const char *o)
{
- int c = 0, d, l;
+ int c = 0, d;
char *e, *p, *n;
const char *q;
@@ -674,33 +680,35 @@ int remove_feature(char **f, const char *o)
if (!o || *o == '\0')
return 0;
- /* Check if not present */
- if (!strstr(*f, o))
+ d = strlen(o);
+ if (isspace(*o) || isspace(*(o + d - 1))) {
+ condlog(0, "internal error: feature \"%s\" has leading or trailing spaces", o);
+ return 1;
+ }
+
+ /* Check if present and not part of a larger feature token*/
+ p = *f + 1; /* the size must be at the start of the features string */
+ while ((p = strstr(p, o)) != NULL) {
+ if (isspace(*(p - 1)) &&
+ (isspace(*(p + d)) || *(p + d) == '\0'))
+ break;
+ p += d;
+ }
+ if (!p)
return 0;
/* Get feature count */
c = strtoul(*f, &e, 10);
- if (*f == e)
- /* parse error */
+ if (*f == e || !isspace(*e)) {
+ condlog(0, "parse error in feature string \"%s\"", *f);
return 1;
-
- /* Normalize features */
- while (*o == ' ') {
- o++;
}
- /* Just spaces, return */
- if (*o == '\0')
- return 0;
- q = o + strlen(o);
- while (*q == ' ')
- q--;
- d = (int)(q - o);
/* Update feature count */
c--;
q = o;
- while (q[0] != '\0') {
- if (q[0] == ' ' && q[1] != ' ' && q[1] != '\0')
+ while (*q != '\0') {
+ if (isspace(*q) && !isspace(*(q + 1)) && *(q + 1) != '\0')
c--;
q++;
}
@@ -714,15 +722,8 @@ int remove_feature(char **f, const char *o)
goto out;
}
- /* Search feature to be removed */
- e = strstr(*f, o);
- if (!e)
- /* Not found, return */
- return 0;
-
/* Update feature count space */
- l = strlen(*f) - d;
- n = malloc(l + 1);
+ n = malloc(strlen(*f) - d + 1);
if (!n)
return 1;
@@ -732,36 +733,16 @@ int remove_feature(char **f, const char *o)
* Copy existing features up to the feature
* about to be removed
*/
- p = strchr(*f, ' ');
- if (!p) {
- /* Internal error, feature string inconsistent */
- free(n);
- return 1;
- }
- while (*p == ' ')
- p++;
- p--;
- if (e != p) {
- do {
- e--;
- d++;
- } while (*e == ' ');
- e++; d--;
- strncat(n, p, (size_t)(e - p));
- p += (size_t)(e - p);
- }
+ strncat(n, e, (size_t)(p - e));
/* Skip feature to be removed */
p += d;
-
/* Copy remaining features */
- if (strlen(p)) {
- while (*p == ' ')
- p++;
- if (strlen(p)) {
- p--;
- strcat(n, p);
- }
- }
+ while (isspace(*p))
+ p++;
+ if (*p != '\0')
+ strcat(n, p);
+ else
+ strchop(n);
out:
free(*f);
@@ -771,11 +752,17 @@ out:
}
unsigned int bus_protocol_id(const struct path *pp) {
- if (!pp || pp->bus < 0 || pp->bus > SYSFS_BUS_SCSI)
+ if (!pp || pp->bus < 0 || pp->bus > SYSFS_BUS_NVME)
return SYSFS_BUS_UNDEF;
- if (pp->bus != SYSFS_BUS_SCSI)
+ if (pp->bus != SYSFS_BUS_SCSI && pp->bus != SYSFS_BUS_NVME)
return pp->bus;
- if ((int)pp->sg_id.proto_id < 0 || pp->sg_id.proto_id > SCSI_PROTOCOL_UNSPEC)
+ if (pp->sg_id.proto_id < 0)
+ return SYSFS_BUS_UNDEF;
+ if (pp->bus == SYSFS_BUS_SCSI &&
+ pp->sg_id.proto_id > SCSI_PROTOCOL_UNSPEC)
+ return SYSFS_BUS_UNDEF;
+ if (pp->bus == SYSFS_BUS_NVME &&
+ pp->sg_id.proto_id > NVME_PROTOCOL_UNSPEC)
return SYSFS_BUS_UNDEF;
- return SYSFS_BUS_SCSI + pp->sg_id.proto_id;
+ return pp->bus + pp->sg_id.proto_id;
}
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 5a713d4..9e2c1ab 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -56,15 +56,6 @@ enum failback_mode {
FAILBACK_FOLLOWOVER
};
-/* SYSFS_BUS_SCSI should be last, see bus_protocol_id() */
-enum sysfs_buses {
- SYSFS_BUS_UNDEF,
- SYSFS_BUS_CCW,
- SYSFS_BUS_CCISS,
- SYSFS_BUS_NVME,
- SYSFS_BUS_SCSI,
-};
-
enum pathstates {
PSTATE_UNDEF,
PSTATE_FAILED,
@@ -170,6 +161,14 @@ enum max_sectors_kb_states {
MAX_SECTORS_KB_MIN = 4, /* can't be smaller than page size */
};
+enum queue_mode_states {
+ QUEUE_MODE_UNDEF = 0,
+ QUEUE_MODE_BIO,
+ QUEUE_MODE_RQ,
+};
+
+#define PROTOCOL_UNSET -1
+
enum scsi_protocol {
SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */
SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */
@@ -182,14 +181,32 @@ enum scsi_protocol {
SCSI_PROTOCOL_ATA = 8,
SCSI_PROTOCOL_USB = 9, /* USB Attached SCSI (UAS), and others */
SCSI_PROTOCOL_UNSPEC = 0xa, /* No specific protocol */
+ SCSI_PROTOCOL_END = 0xb, /* offset of the next sysfs_buses entry */
+};
+
+/* values from /sys/class/nvme/nvmeX */
+enum nvme_protocol {
+ NVME_PROTOCOL_PCIE = 0,
+ NVME_PROTOCOL_RDMA = 1,
+ NVME_PROTOCOL_FC = 2,
+ NVME_PROTOCOL_TCP = 3,
+ NVME_PROTOCOL_LOOP = 4,
+ NVME_PROTOCOL_APPLE_NVME = 5,
+ NVME_PROTOCOL_UNSPEC = 6, /* unknown protocol */
+};
+
+enum sysfs_buses {
+ SYSFS_BUS_UNDEF,
+ SYSFS_BUS_CCW,
+ SYSFS_BUS_CCISS,
+ SYSFS_BUS_SCSI,
+ SYSFS_BUS_NVME = SYSFS_BUS_SCSI + SCSI_PROTOCOL_END,
};
/*
* Linear ordering of bus/protocol
- * This assumes that SYSFS_BUS_SCSI is last in enum sysfs_buses
- * SCSI is the only bus type for which we distinguish protocols.
*/
-#define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC)
+#define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_NVME + NVME_PROTOCOL_UNSPEC)
unsigned int bus_protocol_id(const struct path *pp);
extern const char * const protocol_name[];
@@ -285,7 +302,7 @@ struct sg_id {
uint64_t lun;
short h_cmd_per_lun;
short d_queue_depth;
- enum scsi_protocol proto_id;
+ int proto_id;
int transport_id;
};
@@ -396,6 +413,7 @@ struct multipath {
int needs_paths_uevent;
int ghost_delay;
int ghost_delay_tick;
+ int queue_mode;
uid_t uid;
gid_t gid;
mode_t mode;
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 645896c..5a61876 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -264,6 +264,13 @@ int adopt_paths(vector pathvec, struct multipath *mpp)
}
if (pp->initialized == INIT_REMOVED)
continue;
+ if (mpp->queue_mode == QUEUE_MODE_RQ &&
+ pp->bus == SYSFS_BUS_NVME &&
+ pp->sg_id.proto_id == NVME_PROTOCOL_TCP) {
+ condlog(2, "%s: mulitpath device %s created with request queue_mode. Unable to add nvme:tcp paths",
+ pp->dev, mpp->alias);
+ continue;
+ }
if (!mpp->paths && !(mpp->paths = vector_alloc()))
goto err;
diff --git a/libmultipath/version.h b/libmultipath/version.h
index fcc36d7..b5df67b 100644
--- a/libmultipath/version.h
+++ b/libmultipath/version.h
@@ -20,9 +20,9 @@
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000901
+#define VERSION_CODE 0x000902
/* MMDDYY, in hex */
-#define DATE_CODE 0x090716
+#define DATE_CODE 0x0A1816
#define PROG "multipath-tools"
diff --git a/mpathpersist/main.c b/mpathpersist/main.c
index 894e8c9..b661790 100644
--- a/mpathpersist/main.c
+++ b/mpathpersist/main.c
@@ -178,7 +178,6 @@ static int handle_args(int argc, char * argv[], int nline)
const char *device_name = NULL;
int num_prin_sa = 0;
int num_prout_sa = 0;
- int num_prout_param = 0;
int prin_flag = 0;
int prout_flag = 0;
int ret = 0;
@@ -263,11 +262,9 @@ static int handle_args(int argc, char * argv[], int nline)
case 'Y':
param_alltgpt = 1;
- ++num_prout_param;
break;
case 'Z':
param_aptpl = 1;
- ++num_prout_param;
break;
case 'K':
if (1 != sscanf (optarg, "%" SCNx64 "", &param_rk))
@@ -276,7 +273,6 @@ static int handle_args(int argc, char * argv[], int nline)
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
- ++num_prout_param;
break;
case 'S':
@@ -286,7 +282,6 @@ static int handle_args(int argc, char * argv[], int nline)
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
- ++num_prout_param;
break;
case 'P':
@@ -306,7 +301,6 @@ static int handle_args(int argc, char * argv[], int nline)
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
- ++num_prout_param;
break;
case 's':
diff --git a/mpathpersist/mpathpersist.8 b/mpathpersist/mpathpersist.8
index 7b57459..d594422 100644
--- a/mpathpersist/mpathpersist.8
+++ b/mpathpersist/mpathpersist.8
@@ -5,7 +5,7 @@
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MPATHPERSIST 8 2019-05-27 "Linux"
+.TH MPATHPERSIST 8 2021-11-12 "Linux"
.
.
.\" ----------------------------------------------------------------------------
diff --git a/multipath/Makefile b/multipath/Makefile
index 46b7553..116348e 100644
--- a/multipath/Makefile
+++ b/multipath/Makefile
@@ -13,7 +13,7 @@ EXEC = multipath
OBJS = main.o
-all: $(EXEC)
+all: $(EXEC) multipath.rules tmpfiles.conf
$(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
$(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS)
@@ -23,9 +23,11 @@ install:
$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
- $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(udevrulesdir)/56-multipath.rules
+ $(INSTALL_PROGRAM) -m 644 multipath.rules $(DESTDIR)$(udevrulesdir)/56-multipath.rules
$(INSTALL_PROGRAM) -d $(DESTDIR)$(modulesloaddir)
$(INSTALL_PROGRAM) -m 644 modules-load.conf $(DESTDIR)$(modulesloaddir)/multipath.conf
+ $(INSTALL_PROGRAM) -d $(DESTDIR)$(tmpfilesdir)
+ $(INSTALL_PROGRAM) -m 644 tmpfiles.conf $(DESTDIR)$(tmpfilesdir)/multipath.conf
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
$(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir)
@@ -46,9 +48,12 @@ uninstall:
$(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5
clean: dep_clean
- $(RM) core *.o $(EXEC)
+ $(RM) core *.o $(EXEC) multipath.rules tmpfiles.conf
include $(wildcard $(OBJS:.o=.d))
dep_clean:
$(RM) $(OBJS:.o=.d)
+
+%: %.in
+ sed 's,@RUNTIME_DIR@,$(runtimedir),' $< >$@
diff --git a/multipath/multipath.8 b/multipath/multipath.8
index 4c7e988..88149d5 100644
--- a/multipath/multipath.8
+++ b/multipath/multipath.8
@@ -5,7 +5,7 @@
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MULTIPATH 8 2018-10-10 "Linux"
+.TH MULTIPATH 8 2021-11-12 "Linux"
.
.
.\" ----------------------------------------------------------------------------
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index acdd1ae..1fea9d5 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -6,7 +6,7 @@
.\" Update the date below if you make any significant change.
.\" ----------------------------------------------------------------------------
.
-.TH MULTIPATH.CONF 5 2021-09-08 Linux
+.TH MULTIPATH.CONF 5 2022-09-09 Linux
.
.
.\" ----------------------------------------------------------------------------
@@ -320,7 +320,7 @@ Generate the path priority for NetApp ONTAP class, and rebranded arrays.
.I rdac
(Hardware-dependent)
Generate the path priority for LSI/Engenio/NetApp RDAC class as NetApp SANtricity
-E/EF Series, and rebranded arrays.
+E/EF Series and rebranded arrays, with "Linux DM-MP (Kernel 3.9 or earlier)" option.
.TP
.I hp_sw
(Hardware-dependent)
@@ -459,8 +459,13 @@ precedence. See KNOWN ISSUES.
<mode> can be \fIbio\fR, \fIrq\fR or \fImq\fR, which corresponds to
bio-based, request-based, and block-multiqueue (blk-mq) request-based,
respectively.
-The default depends on the kernel parameter \fBdm_mod.use_blk_mq\fR. It is
-\fImq\fR if the latter is set, and \fIrq\fR otherwise.
+Before kernel 4.20 The default depends on the kernel parameter
+\fBdm_mod.use_blk_mq\fR. It is \fImq\fR if the latter is set, and \fIrq\fR
+otherwise. Since kernel 4.20, \fIrq\fR and \fImq\fR both correspond to
+block-multiqueue. Once a multipath device has been created, its queue_mode
+cannot be changed. \fInvme:tcp\fR paths are only supported in multipath
+devices with queue_mode set to \fIbio\fR. multipath will automatically
+set this when creating a device with \fInvme:tcp\fR paths.
.TP
The default is: \fB<unset>\fR
.RE
@@ -1374,7 +1379,9 @@ Regular expression for the protocol of a device to be excluded/included.
The protocol strings that multipath recognizes are \fIscsi:fcp\fR,
\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR,
\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR,
-\fIscsi:unspec\fR, \fIccw\fR, \fIcciss\fR, \fInvme\fR, and \fIundef\fR.
+\fIscsi:unspec\fR, \fInvme:pcie\fR, \fInvme:rdma\fR, \fInvme:fc\fR,
+\fInvme:tcp\fR, \fInvme:loop\fR, \fInvme:apple-nvme\fR, \fInvme:unspec\fR,
+\fIccw\fR, \fIcciss\fR, and \fIundef\fR.
The protocol that a path is using can be viewed by running
\fBmultipathd show paths format "%d %P"\fR
.RE
@@ -1568,7 +1575,7 @@ with Failover Mode 1 (Passive Not Ready(PNR)).
.I 1 rdac
(Hardware-dependent)
Hardware handler for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
-Series, and rebranded arrays.
+Series and rebranded arrays, with "Linux DM-MP (Kernel 3.9 or earlier)" option.
.TP
.I 1 hp_sw
(Hardware-dependent)
@@ -1770,7 +1777,9 @@ The protocol subsection recognizes the following mandatory attribute:
The protocol string of the path device. The possible values are \fIscsi:fcp\fR,
\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR,
\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR,
-\fIscsi:unspec\fR, \fIccw\fR, \fIcciss\fR, \fInvme\fR, and \fIundef\fR. This is
+\fIscsi:unspec\fR, \fInvme:pcie\fR, \fInvme:rdma\fR, \fInvme:fc\fR,
+\fInvme:tcp\fR, \fInvme:loop\fR, \fInvme:apple-nvme\fR, \fInvme:unspec\fR,
+\fIccw\fR, \fIcciss\fR, and \fIundef\fR. This is
\fBnot\fR a regular expression. the path device protocol string must match
exactly. The protocol that a path is using can be viewed by running
\fBmultipathd show paths format "%d %P"\fR
diff --git a/multipath/multipath.rules b/multipath/multipath.rules.in
index f993d99..8d3cf33 100644
--- a/multipath/multipath.rules
+++ b/multipath/multipath.rules.in
@@ -1,8 +1,8 @@
# Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath
SUBSYSTEM!="block", GOTO="end_mpath"
KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath"
-ACTION=="remove", TEST=="/dev/shm/multipath/find_multipaths/$major:$minor", \
- RUN+="/usr/bin/rm -f /dev/shm/multipath/find_multipaths/$major:$minor"
+ACTION=="remove", TEST=="@RUNTIME_DIR@/multipath/find_multipaths/$major:$minor", \
+ RUN+="/usr/bin/rm -f @RUNTIME_DIR@/multipath/find_multipaths/$major:$minor"
ACTION!="add|change", GOTO="end_mpath"
IMPORT{cmdline}="nompath"
diff --git a/multipath/tmpfiles.conf.in b/multipath/tmpfiles.conf.in
new file mode 100644
index 0000000..21be438
--- /dev/null
+++ b/multipath/tmpfiles.conf.in
@@ -0,0 +1 @@
+d @RUNTIME_DIR@/multipath 0700 root root -
diff --git a/multipathd/callbacks.c b/multipathd/callbacks.c
index 0bd76b7..fb87b28 100644
--- a/multipathd/callbacks.c
+++ b/multipathd/callbacks.c
@@ -1,60 +1,72 @@
void init_handler_callbacks(void)
{
- set_handler_callback(LIST+PATHS, HANDLER(cli_list_paths));
- set_handler_callback(LIST+PATHS+FMT, HANDLER(cli_list_paths_fmt));
- set_handler_callback(LIST+PATHS+RAW+FMT, HANDLER(cli_list_paths_raw));
- set_handler_callback(LIST+PATH, HANDLER(cli_list_path));
- set_handler_callback(LIST+MAPS, HANDLER(cli_list_maps));
- set_handler_callback(LIST+STATUS, HANDLER(cli_list_status));
- set_unlocked_handler_callback(LIST+DAEMON, HANDLER(cli_list_daemon));
- set_handler_callback(LIST+MAPS+STATUS, HANDLER(cli_list_maps_status));
- set_handler_callback(LIST+MAPS+STATS, HANDLER(cli_list_maps_stats));
- set_handler_callback(LIST+MAPS+FMT, HANDLER(cli_list_maps_fmt));
- set_handler_callback(LIST+MAPS+RAW+FMT, HANDLER(cli_list_maps_raw));
- set_handler_callback(LIST+MAPS+TOPOLOGY, HANDLER(cli_list_maps_topology));
- set_handler_callback(LIST+TOPOLOGY, HANDLER(cli_list_maps_topology));
- set_handler_callback(LIST+MAPS+JSON, HANDLER(cli_list_maps_json));
- set_handler_callback(LIST+MAP+TOPOLOGY, HANDLER(cli_list_map_topology));
- set_handler_callback(LIST+MAP+FMT, HANDLER(cli_list_map_fmt));
- set_handler_callback(LIST+MAP+RAW+FMT, HANDLER(cli_list_map_fmt));
- set_handler_callback(LIST+MAP+JSON, HANDLER(cli_list_map_json));
- set_handler_callback(LIST+CONFIG+LOCAL, HANDLER(cli_list_config_local));
- set_handler_callback(LIST+CONFIG, HANDLER(cli_list_config));
- set_handler_callback(LIST+BLACKLIST, HANDLER(cli_list_blacklist));
- set_handler_callback(LIST+DEVICES, HANDLER(cli_list_devices));
- set_handler_callback(LIST+WILDCARDS, HANDLER(cli_list_wildcards));
- set_handler_callback(RESET+MAPS+STATS, HANDLER(cli_reset_maps_stats));
- set_handler_callback(RESET+MAP+STATS, HANDLER(cli_reset_map_stats));
- set_handler_callback(ADD+PATH, HANDLER(cli_add_path));
- set_handler_callback(DEL+PATH, HANDLER(cli_del_path));
- set_handler_callback(ADD+MAP, HANDLER(cli_add_map));
- set_handler_callback(DEL+MAP, HANDLER(cli_del_map));
- set_handler_callback(DEL+MAPS, HANDLER(cli_del_maps));
- set_handler_callback(SWITCH+MAP+GROUP, HANDLER(cli_switch_group));
- set_unlocked_handler_callback(RECONFIGURE, HANDLER(cli_reconfigure));
- set_unlocked_handler_callback(RECONFIGURE+ALL, HANDLER(cli_reconfigure_all));
- set_handler_callback(SUSPEND+MAP, HANDLER(cli_suspend));
- set_handler_callback(RESUME+MAP, HANDLER(cli_resume));
- set_handler_callback(RESIZE+MAP, HANDLER(cli_resize));
- set_handler_callback(RELOAD+MAP, HANDLER(cli_reload));
- set_handler_callback(RESET+MAP, HANDLER(cli_reassign));
- set_handler_callback(REINSTATE+PATH, HANDLER(cli_reinstate));
- set_handler_callback(FAIL+PATH, HANDLER(cli_fail));
- set_handler_callback(DISABLEQ+MAP, HANDLER(cli_disable_queueing));
- set_handler_callback(RESTOREQ+MAP, HANDLER(cli_restore_queueing));
- set_handler_callback(DISABLEQ+MAPS, HANDLER(cli_disable_all_queueing));
- set_handler_callback(RESTOREQ+MAPS, HANDLER(cli_restore_all_queueing));
- set_unlocked_handler_callback(QUIT, HANDLER(cli_quit));
- set_unlocked_handler_callback(SHUTDOWN, HANDLER(cli_shutdown));
- set_handler_callback(GETPRSTATUS+MAP, HANDLER(cli_getprstatus));
- set_handler_callback(SETPRSTATUS+MAP, HANDLER(cli_setprstatus));
- set_handler_callback(UNSETPRSTATUS+MAP, HANDLER(cli_unsetprstatus));
- set_handler_callback(FORCEQ+DAEMON, HANDLER(cli_force_no_daemon_q));
- set_handler_callback(RESTOREQ+DAEMON, HANDLER(cli_restore_no_daemon_q));
- set_handler_callback(GETPRKEY+MAP, HANDLER(cli_getprkey));
- set_handler_callback(SETPRKEY+MAP+KEY, HANDLER(cli_setprkey));
- set_handler_callback(UNSETPRKEY+MAP, HANDLER(cli_unsetprkey));
- set_handler_callback(SETMARGINAL+PATH, HANDLER(cli_set_marginal));
- set_handler_callback(UNSETMARGINAL+PATH, HANDLER(cli_unset_marginal));
- set_handler_callback(UNSETMARGINAL+MAP, HANDLER(cli_unset_all_marginal));
+ set_handler_callback(VRB_LIST | Q1_PATHS, HANDLER(cli_list_paths));
+ set_handler_callback(VRB_LIST | Q1_PATHS | Q2_FMT, HANDLER(cli_list_paths_fmt));
+ set_handler_callback(VRB_LIST | Q1_PATHS | Q2_RAW | Q3_FMT,
+ HANDLER(cli_list_paths_raw));
+ set_handler_callback(VRB_LIST | Q1_PATH, HANDLER(cli_list_path));
+ set_handler_callback(VRB_LIST | Q1_MAPS, HANDLER(cli_list_maps));
+ set_handler_callback(VRB_LIST | Q1_STATUS, HANDLER(cli_list_status));
+ set_unlocked_handler_callback(VRB_LIST | Q1_DAEMON, HANDLER(cli_list_daemon));
+ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_STATUS,
+ HANDLER(cli_list_maps_status));
+ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_STATS,
+ HANDLER(cli_list_maps_stats));
+ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_FMT, HANDLER(cli_list_maps_fmt));
+ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_RAW | Q3_FMT,
+ HANDLER(cli_list_maps_raw));
+ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_TOPOLOGY,
+ HANDLER(cli_list_maps_topology));
+ set_handler_callback(VRB_LIST | Q1_TOPOLOGY, HANDLER(cli_list_maps_topology));
+ set_handler_callback(VRB_LIST | Q1_MAPS | Q2_JSON, HANDLER(cli_list_maps_json));
+ set_handler_callback(VRB_LIST | Q1_MAP | Q2_TOPOLOGY,
+ HANDLER(cli_list_map_topology));
+ set_handler_callback(VRB_LIST | Q1_MAP | Q2_FMT, HANDLER(cli_list_map_fmt));
+ set_handler_callback(VRB_LIST | Q1_MAP | Q2_RAW | Q3_FMT,
+ HANDLER(cli_list_map_fmt));
+ set_handler_callback(VRB_LIST | Q1_MAP | Q2_JSON, HANDLER(cli_list_map_json));
+ set_handler_callback(VRB_LIST | Q1_CONFIG | Q2_LOCAL,
+ HANDLER(cli_list_config_local));
+ set_handler_callback(VRB_LIST | Q1_CONFIG, HANDLER(cli_list_config));
+ set_handler_callback(VRB_LIST | Q1_BLACKLIST, HANDLER(cli_list_blacklist));
+ set_handler_callback(VRB_LIST | Q1_DEVICES, HANDLER(cli_list_devices));
+ set_handler_callback(VRB_LIST | Q1_WILDCARDS, HANDLER(cli_list_wildcards));
+ set_handler_callback(VRB_RESET | Q1_MAPS | Q2_STATS,
+ HANDLER(cli_reset_maps_stats));
+ set_handler_callback(VRB_RESET | Q1_MAP | Q2_STATS,
+ HANDLER(cli_reset_map_stats));
+ set_handler_callback(VRB_ADD | Q1_PATH, HANDLER(cli_add_path));
+ set_handler_callback(VRB_DEL | Q1_PATH, HANDLER(cli_del_path));
+ set_handler_callback(VRB_ADD | Q1_MAP, HANDLER(cli_add_map));
+ set_handler_callback(VRB_DEL | Q1_MAP, HANDLER(cli_del_map));
+ set_handler_callback(VRB_DEL | Q1_MAPS, HANDLER(cli_del_maps));
+ set_handler_callback(VRB_SWITCH | Q1_MAP | Q2_GROUP, HANDLER(cli_switch_group));
+ set_unlocked_handler_callback(VRB_RECONFIGURE, HANDLER(cli_reconfigure));
+ set_unlocked_handler_callback(VRB_RECONFIGURE | Q1_ALL,
+ HANDLER(cli_reconfigure_all));
+ set_handler_callback(VRB_SUSPEND | Q1_MAP, HANDLER(cli_suspend));
+ set_handler_callback(VRB_RESUME | Q1_MAP, HANDLER(cli_resume));
+ set_handler_callback(VRB_RESIZE | Q1_MAP, HANDLER(cli_resize));
+ set_handler_callback(VRB_RELOAD | Q1_MAP, HANDLER(cli_reload));
+ set_handler_callback(VRB_RESET | Q1_MAP, HANDLER(cli_reassign));
+ set_handler_callback(VRB_REINSTATE | Q1_PATH, HANDLER(cli_reinstate));
+ set_handler_callback(VRB_FAIL | Q1_PATH, HANDLER(cli_fail));
+ set_handler_callback(VRB_DISABLEQ | Q1_MAP, HANDLER(cli_disable_queueing));
+ set_handler_callback(VRB_RESTOREQ | Q1_MAP, HANDLER(cli_restore_queueing));
+ set_handler_callback(VRB_DISABLEQ | Q1_MAPS, HANDLER(cli_disable_all_queueing));
+ set_handler_callback(VRB_RESTOREQ | Q1_MAPS, HANDLER(cli_restore_all_queueing));
+ set_unlocked_handler_callback(VRB_QUIT, HANDLER(cli_quit));
+ set_unlocked_handler_callback(VRB_SHUTDOWN, HANDLER(cli_shutdown));
+ set_handler_callback(VRB_GETPRSTATUS | Q1_MAP, HANDLER(cli_getprstatus));
+ set_handler_callback(VRB_SETPRSTATUS | Q1_MAP, HANDLER(cli_setprstatus));
+ set_handler_callback(VRB_UNSETPRSTATUS | Q1_MAP, HANDLER(cli_unsetprstatus));
+ set_handler_callback(VRB_FORCEQ | Q1_DAEMON, HANDLER(cli_force_no_daemon_q));
+ set_handler_callback(VRB_RESTOREQ | Q1_DAEMON, HANDLER(cli_restore_no_daemon_q));
+ set_handler_callback(VRB_GETPRKEY | Q1_MAP, HANDLER(cli_getprkey));
+ set_handler_callback(VRB_SETPRKEY | Q1_MAP | Q2_KEY, HANDLER(cli_setprkey));
+ set_handler_callback(VRB_UNSETPRKEY | Q1_MAP, HANDLER(cli_unsetprkey));
+ set_handler_callback(VRB_SETMARGINAL | Q1_PATH, HANDLER(cli_set_marginal));
+ set_handler_callback(VRB_UNSETMARGINAL | Q1_PATH, HANDLER(cli_unset_marginal));
+ set_handler_callback(VRB_UNSETMARGINAL | Q1_MAP,
+ HANDLER(cli_unset_all_marginal));
}
diff --git a/multipathd/cli.c b/multipathd/cli.c
index 5d25ddb..0c89b7c 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -30,6 +30,8 @@ vector get_handlers(void)
{
return handlers;
}
+/* See KEY_INVALID in cli.h */
+#define INVALID_FINGERPRINT ((uint32_t)(0))
static struct key *
alloc_key (void)
@@ -44,7 +46,7 @@ alloc_handler (void)
}
static int
-add_key (vector vec, char * str, uint64_t code, int has_param)
+add_key (vector vec, char * str, uint8_t code, int has_param)
{
struct key * kw;
@@ -74,7 +76,7 @@ out:
return 1;
}
-static struct handler *add_handler(uint64_t fp, cli_handler *fn, bool locked)
+static struct handler *add_handler(uint32_t fp, cli_handler *fn, bool locked)
{
struct handler * h;
@@ -97,11 +99,13 @@ static struct handler *add_handler(uint64_t fp, cli_handler *fn, bool locked)
}
static struct handler *
-find_handler (uint64_t fp)
+find_handler (uint32_t fp)
{
int i;
struct handler *h;
+ if (fp == INVALID_FINGERPRINT)
+ return NULL;
vector_foreach_slot (handlers, h, i)
if (h->fingerprint == fp)
return h;
@@ -110,22 +114,22 @@ find_handler (uint64_t fp)
}
int
-__set_handler_callback (uint64_t fp, cli_handler *fn, bool locked)
+__set_handler_callback (uint32_t fp, cli_handler *fn, bool locked)
{
struct handler *h;
+ assert(fp != INVALID_FINGERPRINT);
assert(find_handler(fp) == NULL);
h = add_handler(fp, fn, locked);
if (!h) {
- condlog(0, "%s: failed to set handler for code %"PRIu64,
+ condlog(0, "%s: failed to set handler for code %"PRIu32,
__func__, fp);
return 1;
}
return 0;
}
-static void
-free_key (struct key * kw)
+void free_key (struct key * kw)
{
if (kw->str)
free(kw->str);
@@ -170,56 +174,56 @@ load_keys (void)
if (!keys)
return 1;
- r += add_key(keys, "list", LIST, 0);
- r += add_key(keys, "show", LIST, 0);
- r += add_key(keys, "add", ADD, 0);
- r += add_key(keys, "remove", DEL, 0);
- r += add_key(keys, "del", DEL, 0);
- r += add_key(keys, "switch", SWITCH, 0);
- r += add_key(keys, "switchgroup", SWITCH, 0);
- r += add_key(keys, "suspend", SUSPEND, 0);
- r += add_key(keys, "resume", RESUME, 0);
- r += add_key(keys, "reinstate", REINSTATE, 0);
- r += add_key(keys, "fail", FAIL, 0);
- r += add_key(keys, "resize", RESIZE, 0);
- r += add_key(keys, "reset", RESET, 0);
- r += add_key(keys, "reload", RELOAD, 0);
- r += add_key(keys, "forcequeueing", FORCEQ, 0);
- r += add_key(keys, "disablequeueing", DISABLEQ, 0);
- r += add_key(keys, "restorequeueing", RESTOREQ, 0);
- r += add_key(keys, "paths", PATHS, 0);
- r += add_key(keys, "maps", MAPS, 0);
- r += add_key(keys, "multipaths", MAPS, 0);
- r += add_key(keys, "path", PATH, 1);
- r += add_key(keys, "map", MAP, 1);
- r += add_key(keys, "multipath", MAP, 1);
- r += add_key(keys, "group", GROUP, 1);
- r += add_key(keys, "reconfigure", RECONFIGURE, 0);
- r += add_key(keys, "daemon", DAEMON, 0);
- r += add_key(keys, "status", STATUS, 0);
- r += add_key(keys, "stats", STATS, 0);
- r += add_key(keys, "topology", TOPOLOGY, 0);
- r += add_key(keys, "config", CONFIG, 0);
- r += add_key(keys, "blacklist", BLACKLIST, 0);
- r += add_key(keys, "devices", DEVICES, 0);
- r += add_key(keys, "raw", RAW, 0);
- r += add_key(keys, "wildcards", WILDCARDS, 0);
- r += add_key(keys, "quit", QUIT, 0);
- r += add_key(keys, "exit", QUIT, 0);
- r += add_key(keys, "shutdown", SHUTDOWN, 0);
- r += add_key(keys, "getprstatus", GETPRSTATUS, 0);
- r += add_key(keys, "setprstatus", SETPRSTATUS, 0);
- r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0);
- r += add_key(keys, "format", FMT, 1);
- r += add_key(keys, "json", JSON, 0);
- r += add_key(keys, "getprkey", GETPRKEY, 0);
- r += add_key(keys, "setprkey", SETPRKEY, 0);
- r += add_key(keys, "unsetprkey", UNSETPRKEY, 0);
- r += add_key(keys, "key", KEY, 1);
- r += add_key(keys, "local", LOCAL, 0);
- r += add_key(keys, "setmarginal", SETMARGINAL, 0);
- r += add_key(keys, "unsetmarginal", UNSETMARGINAL, 0);
- r += add_key(keys, "all", ALL, 0);
+ r += add_key(keys, "list", VRB_LIST, 0);
+ r += add_key(keys, "show", VRB_LIST, 0);
+ r += add_key(keys, "add", VRB_ADD, 0);
+ r += add_key(keys, "remove", VRB_DEL, 0);
+ r += add_key(keys, "del", VRB_DEL, 0);
+ r += add_key(keys, "switch", VRB_SWITCH, 0);
+ r += add_key(keys, "switchgroup", VRB_SWITCH, 0);
+ r += add_key(keys, "suspend", VRB_SUSPEND, 0);
+ r += add_key(keys, "resume", VRB_RESUME, 0);
+ r += add_key(keys, "reinstate", VRB_REINSTATE, 0);
+ r += add_key(keys, "fail", VRB_FAIL, 0);
+ r += add_key(keys, "resize", VRB_RESIZE, 0);
+ r += add_key(keys, "reset", VRB_RESET, 0);
+ r += add_key(keys, "reload", VRB_RELOAD, 0);
+ r += add_key(keys, "forcequeueing", VRB_FORCEQ, 0);
+ r += add_key(keys, "disablequeueing", VRB_DISABLEQ, 0);
+ r += add_key(keys, "restorequeueing", VRB_RESTOREQ, 0);
+ r += add_key(keys, "paths", KEY_PATHS, 0);
+ r += add_key(keys, "maps", KEY_MAPS, 0);
+ r += add_key(keys, "multipaths", KEY_MAPS, 0);
+ r += add_key(keys, "path", KEY_PATH, 1);
+ r += add_key(keys, "map", KEY_MAP, 1);
+ r += add_key(keys, "multipath", KEY_MAP, 1);
+ r += add_key(keys, "group", KEY_GROUP, 1);
+ r += add_key(keys, "reconfigure", VRB_RECONFIGURE, 0);
+ r += add_key(keys, "daemon", KEY_DAEMON, 0);
+ r += add_key(keys, "status", KEY_STATUS, 0);
+ r += add_key(keys, "stats", KEY_STATS, 0);
+ r += add_key(keys, "topology", KEY_TOPOLOGY, 0);
+ r += add_key(keys, "config", KEY_CONFIG, 0);
+ r += add_key(keys, "blacklist", KEY_BLACKLIST, 0);
+ r += add_key(keys, "devices", KEY_DEVICES, 0);
+ r += add_key(keys, "raw", KEY_RAW, 0);
+ r += add_key(keys, "wildcards", KEY_WILDCARDS, 0);
+ r += add_key(keys, "quit", VRB_QUIT, 0);
+ r += add_key(keys, "exit", VRB_QUIT, 0);
+ r += add_key(keys, "shutdown", VRB_SHUTDOWN, 0);
+ r += add_key(keys, "getprstatus", VRB_GETPRSTATUS, 0);
+ r += add_key(keys, "setprstatus", VRB_SETPRSTATUS, 0);
+ r += add_key(keys, "unsetprstatus", VRB_UNSETPRSTATUS, 0);
+ r += add_key(keys, "format", KEY_FMT, 1);
+ r += add_key(keys, "json", KEY_JSON, 0);
+ r += add_key(keys, "getprkey", VRB_GETPRKEY, 0);
+ r += add_key(keys, "setprkey", VRB_SETPRKEY, 0);
+ r += add_key(keys, "unsetprkey", VRB_UNSETPRKEY, 0);
+ r += add_key(keys, "key", KEY_KEY, 1);
+ r += add_key(keys, "local", KEY_LOCAL, 0);
+ r += add_key(keys, "setmarginal", VRB_SETMARGINAL, 0);
+ r += add_key(keys, "unsetmarginal", VRB_UNSETMARGINAL, 0);
+ r += add_key(keys, "all", KEY_ALL, 0);
if (r) {
@@ -255,15 +259,29 @@ struct key *find_key (const char * str)
return foundkw;
}
+static void cleanup_strvec(vector *arg)
+{
+ free_strvec(*arg);
+}
+
+static void cleanup_keys(vector *arg)
+{
+ free_keys(*arg);
+}
+
/*
- * get_cmdvec
+ * get_cmdvec() - parse input
+ *
+ * @cmd: a command string to be parsed
+ * @v: a vector of keywords with parameters
*
* returns:
* ENOMEM: not enough memory to allocate command
- * ESRCH: command not found
+ * ESRCH: keyword not found at end of input
+ * ENOENT: keyword not found somewhere else
* EINVAL: argument missing for command
*/
-int get_cmdvec (char *cmd, vector *v)
+int get_cmdvec (char *cmd, vector *v, bool allow_incomplete)
{
int i;
int r = 0;
@@ -271,18 +289,11 @@ int get_cmdvec (char *cmd, vector *v)
char * buff;
struct key * kw = NULL;
struct key * cmdkw = NULL;
- vector cmdvec, strvec;
-
- strvec = alloc_strvec(cmd);
- if (!strvec)
- return ENOMEM;
-
- cmdvec = vector_alloc();
+ vector cmdvec __attribute__((cleanup(cleanup_keys))) = vector_alloc();
+ vector strvec __attribute__((cleanup(cleanup_strvec))) = alloc_strvec(cmd);
- if (!cmdvec) {
- free_strvec(strvec);
+ if (!strvec || !cmdvec)
return ENOMEM;
- }
vector_foreach_slot(strvec, buff, i) {
if (is_quote(buff))
@@ -294,18 +305,18 @@ int get_cmdvec (char *cmd, vector *v)
}
kw = find_key(buff);
if (!kw) {
- r = ESRCH;
- goto out;
+ r = i == VECTOR_SIZE(strvec) - 1 ? ESRCH : ENOENT;
+ break;
}
cmdkw = alloc_key();
if (!cmdkw) {
r = ENOMEM;
- goto out;
+ break;
}
if (!vector_alloc_slot(cmdvec)) {
free(cmdkw);
r = ENOMEM;
- goto out;
+ break;
}
vector_set_slot(cmdvec, cmdkw);
cmdkw->code = kw->code;
@@ -313,32 +324,31 @@ int get_cmdvec (char *cmd, vector *v)
if (kw->has_param)
get_param = 1;
}
- if (get_param) {
+ if (get_param)
r = EINVAL;
- goto out;
- }
- *v = cmdvec;
- free_strvec(strvec);
- return 0;
-out:
- free_strvec(strvec);
- free_keys(cmdvec);
+ if (r && !allow_incomplete)
+ return r;
+
+ *v = cmdvec;
+ cmdvec = NULL;
return r;
}
-uint64_t fingerprint(const struct _vector *vec)
+uint32_t fingerprint(const struct _vector *vec)
{
int i;
- uint64_t fp = 0;
+ uint32_t fp = 0;
struct key * kw;
- if (!vec)
- return 0;
-
- vector_foreach_slot(vec, kw, i)
- fp += kw->code;
+ if (!vec || VECTOR_SIZE(vec) > 4)
+ return INVALID_FINGERPRINT;
+ vector_foreach_slot(vec, kw, i) {
+ if (i >= 4)
+ break;
+ fp |= (uint32_t)kw->code << (8 * i);
+ }
return fp;
}
@@ -377,8 +387,8 @@ genhelp_sprint_aliases (struct strbuf *reply, vector keys,
static int
do_genhelp(struct strbuf *reply, const char *cmd, int error) {
- int i, j;
- uint64_t fp;
+ int i, j, k;
+ uint32_t fp;
struct handler * h;
struct key * kw;
int rc = 0;
@@ -404,17 +414,24 @@ do_genhelp(struct strbuf *reply, const char *cmd, int error) {
vector_foreach_slot (handlers, h, i) {
fp = h->fingerprint;
- vector_foreach_slot (keys, kw, j) {
- if ((kw->code & fp)) {
- fp -= kw->code;
- if (print_strbuf(reply, " %s", kw->str) < 0 ||
- genhelp_sprint_aliases(reply, keys, kw) < 0)
- return -1;
-
- if (kw->has_param) {
- if (print_strbuf(reply, " $%s",
- kw->str) < 0)
+ for (k = 0; k < 4; k++, fp >>= 8) {
+ uint32_t code = fp & 0xff;
+
+ if (!code)
+ break;
+
+ vector_foreach_slot (keys, kw, j) {
+ if ((uint32_t)kw->code == code) {
+ if (print_strbuf(reply, " %s", kw->str) < 0 ||
+ genhelp_sprint_aliases(reply, keys, kw) < 0)
return -1;
+
+ if (kw->has_param) {
+ if (print_strbuf(reply, " $%s",
+ kw->str) < 0)
+ return -1;
+ }
+ break;
}
}
}
@@ -432,7 +449,7 @@ void genhelp_handler(const char *cmd, int error, struct strbuf *reply)
}
char *
-get_keyparam (vector v, uint64_t code)
+get_keyparam (vector v, uint8_t code)
{
struct key * kw;
int i;
diff --git a/multipathd/cli.h b/multipathd/cli.h
index cb5bbe2..c6b79c9 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -3,98 +3,98 @@
#include <stdint.h>
+/*
+ * CLI commands consist of 4 bytes, a verb (byte 0) and up to
+ * 3 qualifiers (byte 1 - 3).
+ */
+
enum {
- __LIST, /* 0 */
- __ADD,
- __DEL,
- __SWITCH,
- __SUSPEND,
- __RESUME, /* 5 */
- __REINSTATE,
- __FAIL,
- __RESIZE,
- __RESET,
- __RELOAD, /* 10 */
- __FORCEQ,
- __DISABLEQ,
- __RESTOREQ,
- __PATHS,
- __MAPS, /* 15 */
- __PATH,
- __MAP,
- __GROUP,
- __RECONFIGURE,
- __DAEMON, /* 20 */
- __STATUS,
- __STATS,
- __TOPOLOGY,
- __CONFIG,
- __BLACKLIST, /* 25 */
- __DEVICES,
- __RAW,
- __WILDCARDS,
- __QUIT,
- __SHUTDOWN, /* 30 */
- __GETPRSTATUS,
- __SETPRSTATUS,
- __UNSETPRSTATUS,
- __FMT,
- __JSON, /* 35 */
- __GETPRKEY,
- __SETPRKEY,
- __UNSETPRKEY,
- __KEY,
- __LOCAL, /* 40 */
- __SETMARGINAL,
- __UNSETMARGINAL,
- __ALL,
+ /* See INVALID_FINGERPRINT in cli.c */
+ KEY_INVALID = 0,
+
+ /* Verbs */
+ VRB_LIST = 1,
+ VRB_ADD = 2,
+ VRB_DEL = 3,
+ VRB_RESET = 4,
+ VRB_SWITCH = 5,
+ VRB_RECONFIGURE = 6,
+ VRB_SUSPEND = 7,
+ VRB_RESUME = 8,
+ VRB_RESIZE = 9,
+ VRB_RELOAD = 10,
+ VRB_FAIL = 11,
+ VRB_REINSTATE = 12,
+ VRB_DISABLEQ = 13,
+ VRB_RESTOREQ = 14,
+ VRB_FORCEQ = 15,
+ VRB_GETPRSTATUS = 16,
+ VRB_SETPRSTATUS = 17,
+ VRB_UNSETPRSTATUS = 18,
+ VRB_GETPRKEY = 19,
+ VRB_SETPRKEY = 20,
+ VRB_UNSETPRKEY = 21,
+ VRB_SETMARGINAL = 22,
+ VRB_UNSETMARGINAL = 23,
+ VRB_SHUTDOWN = 24,
+ VRB_QUIT = 25,
+
+ /* Qualifiers, values must be different from verbs */
+ KEY_PATH = 65,
+ KEY_PATHS = 66,
+ KEY_MAP = 67,
+ KEY_MAPS = 68,
+ KEY_TOPOLOGY = 69,
+ KEY_CONFIG = 70,
+ KEY_BLACKLIST = 71,
+ KEY_DEVICES = 72,
+ KEY_WILDCARDS = 73,
+ KEY_ALL = 74,
+ KEY_DAEMON = 75,
+ KEY_FMT = 76,
+ KEY_RAW = 77,
+ KEY_STATUS = 78,
+ KEY_STATS = 79,
+ KEY_JSON = 80,
+ KEY_LOCAL = 81,
+ KEY_GROUP = 82,
+ KEY_KEY = 83,
};
-#define LIST (1ULL << __LIST)
-#define ADD (1ULL << __ADD)
-#define DEL (1ULL << __DEL)
-#define SWITCH (1ULL << __SWITCH)
-#define SUSPEND (1ULL << __SUSPEND)
-#define RESUME (1ULL << __RESUME)
-#define REINSTATE (1ULL << __REINSTATE)
-#define FAIL (1ULL << __FAIL)
-#define RESIZE (1ULL << __RESIZE)
-#define RESET (1ULL << __RESET)
-#define RELOAD (1ULL << __RELOAD)
-#define FORCEQ (1ULL << __FORCEQ)
-#define DISABLEQ (1ULL << __DISABLEQ)
-#define RESTOREQ (1ULL << __RESTOREQ)
-#define PATHS (1ULL << __PATHS)
-#define MAPS (1ULL << __MAPS)
-#define PATH (1ULL << __PATH)
-#define MAP (1ULL << __MAP)
-#define GROUP (1ULL << __GROUP)
-#define RECONFIGURE (1ULL << __RECONFIGURE)
-#define DAEMON (1ULL << __DAEMON)
-#define STATUS (1ULL << __STATUS)
-#define STATS (1ULL << __STATS)
-#define TOPOLOGY (1ULL << __TOPOLOGY)
-#define CONFIG (1ULL << __CONFIG)
-#define BLACKLIST (1ULL << __BLACKLIST)
-#define DEVICES (1ULL << __DEVICES)
-#define RAW (1ULL << __RAW)
-#define COUNT (1ULL << __COUNT)
-#define WILDCARDS (1ULL << __WILDCARDS)
-#define QUIT (1ULL << __QUIT)
-#define SHUTDOWN (1ULL << __SHUTDOWN)
-#define GETPRSTATUS (1ULL << __GETPRSTATUS)
-#define SETPRSTATUS (1ULL << __SETPRSTATUS)
-#define UNSETPRSTATUS (1ULL << __UNSETPRSTATUS)
-#define FMT (1ULL << __FMT)
-#define JSON (1ULL << __JSON)
-#define GETPRKEY (1ULL << __GETPRKEY)
-#define SETPRKEY (1ULL << __SETPRKEY)
-#define UNSETPRKEY (1ULL << __UNSETPRKEY)
-#define KEY (1ULL << __KEY)
-#define LOCAL (1ULL << __LOCAL)
-#define SETMARGINAL (1ULL << __SETMARGINAL)
-#define UNSETMARGINAL (1ULL << __UNSETMARGINAL)
-#define ALL (1ULL << __ALL)
+/*
+ * The shifted qualifiers determine valid positions of the
+ * keywords in the known commands. E.g. the only qualifier
+ * that's valid in position 3 is "fmt", e.g. "list maps raw fmt".
+ */
+enum {
+ /* byte 1: qualifier 1 */
+ Q1_PATH = KEY_PATH << 8,
+ Q1_PATHS = KEY_PATHS << 8,
+ Q1_MAP = KEY_MAP << 8,
+ Q1_MAPS = KEY_MAPS << 8,
+ Q1_TOPOLOGY = KEY_TOPOLOGY << 8,
+ Q1_CONFIG = KEY_CONFIG << 8,
+ Q1_BLACKLIST = KEY_BLACKLIST << 8,
+ Q1_DEVICES = KEY_DEVICES << 8,
+ Q1_WILDCARDS = KEY_WILDCARDS << 8,
+ Q1_ALL = KEY_ALL << 8,
+ Q1_DAEMON = KEY_DAEMON << 8,
+ Q1_STATUS = KEY_STATUS << 8,
+
+ /* byte 2: qualifier 2 */
+ Q2_FMT = KEY_FMT << 16,
+ Q2_RAW = KEY_RAW << 16,
+ Q2_STATUS = KEY_STATUS << 16,
+ Q2_STATS = KEY_STATS << 16,
+ Q2_TOPOLOGY = KEY_TOPOLOGY << 16,
+ Q2_JSON = KEY_JSON << 16,
+ Q2_LOCAL = KEY_LOCAL << 16,
+ Q2_GROUP = KEY_GROUP << 16,
+ Q2_KEY = KEY_KEY << 16,
+
+ /* byte 3: qualifier 3 */
+ Q3_FMT = KEY_FMT << 24,
+};
#define INITIAL_REPLY_LEN 1200
@@ -122,7 +122,7 @@ enum {
struct key {
char * str;
char * param;
- uint64_t code;
+ uint8_t code;
int has_param;
};
@@ -131,27 +131,28 @@ struct strbuf;
typedef int (cli_handler)(void *keywords, struct strbuf *reply, void *data);
struct handler {
- uint64_t fingerprint;
+ uint32_t fingerprint;
int locked;
cli_handler *fn;
};
int alloc_handlers (void);
-int __set_handler_callback (uint64_t fp, cli_handler *fn, bool locked);
+int __set_handler_callback (uint32_t fp, cli_handler *fn, bool locked);
#define set_handler_callback(fp, fn) __set_handler_callback(fp, fn, true)
#define set_unlocked_handler_callback(fp, fn) __set_handler_callback(fp, fn, false)
-int get_cmdvec (char *cmd, vector *v);
+int get_cmdvec (char *cmd, vector *v, bool allow_incomplete);
struct handler *find_handler_for_cmdvec(const struct _vector *v);
void genhelp_handler (const char *cmd, int error, struct strbuf *reply);
int load_keys (void);
-char * get_keyparam (vector v, uint64_t code);
+char * get_keyparam (vector v, uint8_t code);
+void free_key (struct key * kw);
void free_keys (vector vec);
void free_handlers (void);
int cli_init (void);
void cli_exit(void);
-uint64_t fingerprint(const struct _vector *vec);
+uint32_t fingerprint(const struct _vector *vec);
vector get_keys(void);
vector get_handlers(void);
struct key *find_key (const char * str);
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 5b8f647..5f0dd04 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -217,7 +217,7 @@ static int
cli_list_paths_fmt (void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * fmt = get_keyparam(v, FMT);
+ char * fmt = get_keyparam(v, KEY_FMT);
condlog(3, "list paths (operator)");
@@ -228,7 +228,7 @@ static int
cli_list_paths_raw (void *v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * fmt = get_keyparam(v, FMT);
+ char * fmt = get_keyparam(v, KEY_FMT);
condlog(3, "list paths (operator)");
@@ -239,7 +239,7 @@ static int
cli_list_path (void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, PATH);
+ char * param = get_keyparam(v, KEY_PATH);
struct path *pp;
param = convert_dev(param, 1);
@@ -257,7 +257,7 @@ cli_list_map_topology (void *v, struct strbuf *reply, void *data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
fieldwidth_t *p_width __attribute__((cleanup(cleanup_ucharp))) = NULL;
if ((p_width = alloc_path_layout()) == NULL)
@@ -289,7 +289,7 @@ cli_list_map_json (void *v, struct strbuf *reply, void *data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
param = convert_dev(param, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
@@ -391,7 +391,7 @@ static int
cli_list_maps_fmt (void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * fmt = get_keyparam(v, FMT);
+ char * fmt = get_keyparam(v, KEY_FMT);
condlog(3, "list maps (operator)");
@@ -402,7 +402,7 @@ static int
cli_list_maps_raw (void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * fmt = get_keyparam(v, FMT);
+ char * fmt = get_keyparam(v, KEY_FMT);
condlog(3, "list maps (operator)");
@@ -414,8 +414,8 @@ cli_list_map_fmt (void *v, struct strbuf *reply, void *data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
- char * fmt = get_keyparam(v, FMT);
+ char * param = get_keyparam(v, KEY_MAP);
+ char * fmt = get_keyparam(v, KEY_FMT);
fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL;
if ((width = alloc_multipath_layout()) == NULL)
@@ -499,7 +499,7 @@ cli_reset_map_stats (void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
struct multipath * mpp;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
param = convert_dev(param, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
@@ -543,7 +543,7 @@ static int
cli_add_path (void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, PATH);
+ char * param = get_keyparam(v, KEY_PATH);
struct path *pp;
int r;
struct config *conf;
@@ -663,7 +663,7 @@ static int
cli_del_path (void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, PATH);
+ char * param = get_keyparam(v, KEY_PATH);
struct path *pp;
int ret;
@@ -686,7 +686,7 @@ static int
cli_add_map (void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
int major = -1, minor = -1;
char dev_path[FILE_NAME_SIZE];
char *refwwid, *alias = NULL;
@@ -746,7 +746,7 @@ static int
cli_del_map (void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
int major, minor;
char *alias;
int rc;
@@ -794,7 +794,7 @@ static int
cli_reload(void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * mapname = get_keyparam(v, MAP);
+ char * mapname = get_keyparam(v, KEY_MAP);
struct multipath *mpp;
int minor;
@@ -847,7 +847,7 @@ static int
cli_resize(void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * mapname = get_keyparam(v, MAP);
+ char * mapname = get_keyparam(v, KEY_MAP);
struct multipath *mpp;
int minor;
unsigned long long size;
@@ -938,7 +938,7 @@ static int
cli_restore_queueing(void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * mapname = get_keyparam(v, MAP);
+ char * mapname = get_keyparam(v, KEY_MAP);
struct multipath *mpp;
int minor;
struct config *conf;
@@ -1001,7 +1001,7 @@ static int
cli_disable_queueing(void *v, struct strbuf *reply, void *data)
{
struct vectors * vecs = (struct vectors *)data;
- char * mapname = get_keyparam(v, MAP);
+ char * mapname = get_keyparam(v, KEY_MAP);
struct multipath *mpp;
int minor;
@@ -1048,8 +1048,8 @@ cli_disable_all_queueing(void *v, struct strbuf *reply, void *data)
static int
cli_switch_group(void * v, struct strbuf *reply, void * data)
{
- char * mapname = get_keyparam(v, MAP);
- int groupnum = atoi(get_keyparam(v, GROUP));
+ char * mapname = get_keyparam(v, KEY_MAP);
+ int groupnum = atoi(get_keyparam(v, KEY_GROUP));
mapname = convert_dev(mapname, 0);
condlog(2, "%s: switch to path group #%i (operator)", mapname, groupnum);
@@ -1079,7 +1079,7 @@ static int
cli_suspend(void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
int r;
struct multipath * mpp;
@@ -1109,7 +1109,7 @@ static int
cli_resume(void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
int r;
struct multipath * mpp;
uint16_t udev_flags;
@@ -1141,7 +1141,7 @@ static int
cli_reinstate(void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, PATH);
+ char * param = get_keyparam(v, KEY_PATH);
struct path * pp;
param = convert_dev(param, 1);
@@ -1164,7 +1164,7 @@ static int
cli_reassign (void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
struct multipath *mpp;
param = convert_dev(param, 0);
@@ -1188,7 +1188,7 @@ static int
cli_fail(void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, PATH);
+ char * param = get_keyparam(v, KEY_PATH);
struct path * pp;
int r;
@@ -1284,7 +1284,7 @@ cli_getprstatus (void * v, struct strbuf *reply, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
param = convert_dev(param, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
@@ -1307,7 +1307,7 @@ cli_setprstatus(void * v, struct strbuf *reply, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
param = convert_dev(param, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
@@ -1329,7 +1329,7 @@ cli_unsetprstatus(void * v, struct strbuf *reply, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, MAP);
+ char * param = get_keyparam(v, KEY_MAP);
param = convert_dev(param, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
@@ -1350,7 +1350,7 @@ cli_getprkey(void * v, struct strbuf *reply, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char *mapname = get_keyparam(v, MAP);
+ char *mapname = get_keyparam(v, KEY_MAP);
uint64_t key;
mapname = convert_dev(mapname, 0);
@@ -1377,7 +1377,7 @@ cli_unsetprkey(void * v, struct strbuf *reply, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char *mapname = get_keyparam(v, MAP);
+ char *mapname = get_keyparam(v, KEY_MAP);
int ret;
struct config *conf;
@@ -1401,8 +1401,8 @@ cli_setprkey(void * v, struct strbuf *reply, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
- char *mapname = get_keyparam(v, MAP);
- char *keyparam = get_keyparam(v, KEY);
+ char *mapname = get_keyparam(v, KEY_MAP);
+ char *keyparam = get_keyparam(v, KEY_KEY);
uint64_t prkey;
uint8_t flags;
int ret;
@@ -1431,7 +1431,7 @@ cli_setprkey(void * v, struct strbuf *reply, void * data)
static int cli_set_marginal(void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, PATH);
+ char * param = get_keyparam(v, KEY_PATH);
struct path * pp;
param = convert_dev(param, 1);
@@ -1458,7 +1458,7 @@ static int cli_set_marginal(void * v, struct strbuf *reply, void * data)
static int cli_unset_marginal(void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * param = get_keyparam(v, PATH);
+ char * param = get_keyparam(v, KEY_PATH);
struct path * pp;
param = convert_dev(param, 1);
@@ -1485,7 +1485,7 @@ static int cli_unset_marginal(void * v, struct strbuf *reply, void * data)
static int cli_unset_all_marginal(void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
- char * mapname = get_keyparam(v, MAP);
+ char * mapname = get_keyparam(v, KEY_MAP);
struct multipath *mpp;
struct pathgroup * pgp;
struct path * pp;
diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c
index 03b2b9a..a2de301 100644
--- a/multipathd/fpin_handlers.c
+++ b/multipathd/fpin_handlers.c
@@ -227,7 +227,7 @@ static int fpin_chk_wwn_setpath_marginal(uint16_t host_num, struct vectors *ve
vector_foreach_slot(vecs->pathvec, pp, k) {
/* Checks the host number and also for the SCSI FCP */
- if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num != pp->sg_id.host_no)
+ if (pp->bus != SYSFS_BUS_SCSI || pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num != pp->sg_id.host_no)
continue;
sprintf(rport_id, "rport-%d:%d-%d",
pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
diff --git a/multipathd/multipathc.c b/multipathd/multipathc.c
index b3f7db0..38f2d6a 100644
--- a/multipathd/multipathc.c
+++ b/multipathd/multipathc.c
@@ -15,6 +15,7 @@
#include "uxsock.h"
#include "util.h"
#include "cli.h"
+#include "debug.h"
#ifdef USE_LIBEDIT
#include <editline/readline.h>
@@ -39,44 +40,68 @@
#endif
#if defined(USE_LIBREADLINE) || defined(USE_LIBEDIT)
-static int
-key_match_fingerprint (struct key * kw, uint64_t fp)
-{
- if (!fp)
- return 0;
-
- return ((fp & kw->code) == kw->code);
-}
-
/*
* This is the readline completion handler
*/
char *
key_generator (const char * str, int state)
{
- static int index, len, has_param;
- static uint64_t rlfp;
- struct key * kw;
- int i;
- struct handler *h;
- vector v = NULL;
- const vector keys = get_keys();
- const vector handlers = get_handlers();
+ static vector completions;
+ static int index;
+ char *word;
if (!state) {
+ uint32_t rlfp = 0, mask = 0;
+ int len = strlen(str), vlen = 0, i, j;
+ struct key * kw;
+ struct handler *h;
+ vector handlers = get_handlers();
+ vector keys = get_keys();
+ vector v = NULL;
+ int r = get_cmdvec(rl_line_buffer, &v, true);
+
index = 0;
- has_param = 0;
- rlfp = 0;
- len = strlen(str);
- int r = get_cmdvec(rl_line_buffer, &v);
+ if (completions)
+ vector_free(completions);
+
+ completions = vector_alloc();
+
+ if (!completions || r == ENOMEM) {
+ if (v)
+ vector_free(v);
+ return NULL;
+ }
+
+ /*
+ * Special case: get_cmdvec() ignores trailing whitespace,
+ * readline doesn't. get_cmdvec() will return "[show]" and
+ * ESRCH for both "show bogus\t" and "show bogus \t".
+ * The former case will fail below. In the latter case,
+ * We shouldn't offer completions.
+ */
+ if (r == ESRCH && !len)
+ r = ENOENT;
+
/*
* If a word completion is in progress, we don't want
* to take an exact keyword match in the fingerprint.
* For ex "show map[tab]" would validate "map" and discard
* "maps" as a valid candidate.
*/
- if (v && len)
- vector_del_slot(v, VECTOR_SIZE(v) - 1);
+ if (r != ESRCH && VECTOR_SIZE(v) && len) {
+ kw = VECTOR_SLOT(v, VECTOR_SIZE(v) - 1);
+ /*
+ * If kw->param is set, we were already parsing a
+ * parameter, not the keyword. Don't delete it.
+ */
+ if (!kw->param) {
+ free_key(kw);
+ vector_del_slot(v, VECTOR_SIZE(v) - 1);
+ if (r == EINVAL)
+ r = 0;
+ }
+ }
+
/*
* Clean up the mess if we dropped the last slot of a 1-slot
* vector
@@ -85,66 +110,86 @@ key_generator (const char * str, int state)
vector_free(v);
v = NULL;
}
- /*
- * If last keyword takes a param, don't even try to guess
- */
- if (r == EINVAL) {
- has_param = 1;
- return (strdup("(value)"));
- }
+
/*
* Compute a command fingerprint to find out possible completions.
* Once done, the vector is useless. Free it.
*/
if (v) {
rlfp = fingerprint(v);
+ vlen = VECTOR_SIZE(v);
+ if (vlen >= 4)
+ mask = ~0;
+ else
+ mask = (uint32_t)(1U << (8 * vlen)) - 1;
free_keys(v);
}
- }
- /*
- * No more completions for parameter placeholder.
- * Brave souls might try to add parameter completion by walking paths and
- * multipaths vectors.
- */
- if (has_param)
- return ((char *)NULL);
- /*
- * Loop through keywords for completion candidates
- */
- vector_foreach_slot_after (keys, kw, index) {
- if (!strncmp(kw->str, str, len)) {
- /*
- * Discard keywords already in the command line
- */
- if (key_match_fingerprint(kw, rlfp)) {
- struct key * curkw = find_key(str);
- if (!curkw || (curkw != kw))
+ condlog(4, "%s: line=\"%s\" str=\"%s\" r=%d fp=%08x mask=%08x",
+ __func__, rl_line_buffer, str, r, rlfp, mask);
+
+ /*
+ * If last keyword takes a param, don't even try to guess
+ * Brave souls might try to add parameter completion by walking
+ * paths and multipaths vectors.
+ */
+ if (r == EINVAL) {
+ if (len == 0 && vector_alloc_slot(completions))
+ vector_set_slot(completions,
+ strdup("VALUE"));
+
+ goto init_done;
+ }
+
+ if (r == ENOENT)
+ goto init_done;
+
+ vector_foreach_slot(handlers, h, i) {
+ uint8_t code;
+
+ if (rlfp != (h->fingerprint & mask))
+ continue;
+
+ if (vlen >= 4)
+ /*
+ * => mask == ~0 => rlfp == h->fingerprint
+ * Complete command. This must be the only match.
+ */
+ goto init_done;
+ else if (rlfp == h->fingerprint && r != ESRCH &&
+ !strcmp(str, "") &&
+ vector_alloc_slot(completions))
+ /* just completed */
+ vector_set_slot(completions, strdup(""));
+ else {
+ /* vlen must be 1, 2, or 3 */
+ code = (h->fingerprint >> vlen * 8);
+
+ if (code == KEY_INVALID)
continue;
- }
- /*
- * Discard keywords making syntax errors.
- *
- * nfp is the candidate fingerprint we try to
- * validate against all known command fingerprints.
- */
- uint64_t nfp = rlfp | kw->code;
- vector_foreach_slot(handlers, h, i) {
- if (!rlfp || ((h->fingerprint & nfp) == nfp)) {
- /*
- * At least one full command is
- * possible with this keyword :
- * Consider it validated
- */
- index++;
- return (strdup(kw->str));
+
+ vector_foreach_slot(keys, kw, j) {
+ if (kw->code != code ||
+ strncmp(kw->str, str, len))
+ continue;
+ if (vector_alloc_slot(completions))
+ vector_set_slot(completions,
+ strdup(kw->str));
}
}
+
}
+ vector_foreach_slot(completions, word, i)
+ condlog(4, "%s: %d -> \"%s\"", __func__, i, word);
+
}
- /*
- * No more candidates
- */
- return ((char *)NULL);
+
+init_done:
+ vector_foreach_slot_after(completions, word, index) {
+ index++;
+ return word;
+ }
+
+ return NULL;
}
#endif
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 23cb123..02e89fb 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -332,7 +332,7 @@ static int parse_cmd(struct client *c)
{
int r;
- r = get_cmdvec(c->cmd, &c->cmdvec);
+ r = get_cmdvec(c->cmd, &c->cmdvec, false);
if (r)
return -r;
@@ -412,6 +412,11 @@ static void set_client_state(struct client *c, int state)
c->expires = ts_zero;
/* reuse these fields for next data transfer */
c->len = c->cmd_len = 0;
+ /* cmdvec isn't needed any more */
+ if (c->cmdvec) {
+ free_keys(c->cmdvec);
+ c->cmdvec = NULL;
+ }
break;
default:
break;
@@ -484,7 +489,7 @@ static int client_state_machine(struct client *c, struct vectors *vecs,
/* Permission check */
struct key *kw = VECTOR_SLOT(c->cmdvec, 0);
- if (!c->is_root && kw->code != LIST) {
+ if (!c->is_root && kw->code != VRB_LIST) {
c->error = -EPERM;
condlog(0, "%s: cli[%d]: unauthorized cmd \"%s\"",
__func__, c->fd, c->cmd);
@@ -506,8 +511,6 @@ static int client_state_machine(struct client *c, struct vectors *vecs,
check_for_locked_work(c);
pthread_cleanup_pop(1);
condlog(4, "%s: cli[%d] grabbed lock", __func__, c->fd);
- free_keys(c->cmdvec);
- c->cmdvec = NULL;
set_client_state(c, CLT_SEND);
/* Wait for POLLOUT */
return STM_BREAK;
@@ -518,8 +521,6 @@ static int client_state_machine(struct client *c, struct vectors *vecs,
case CLT_WORK:
c->error = execute_handler(c, vecs);
- free_keys(c->cmdvec);
- c->cmdvec = NULL;
set_client_state(c, CLT_SEND);
/* Wait for POLLOUT */
return STM_BREAK;
@@ -541,7 +542,7 @@ static int client_state_machine(struct client *c, struct vectors *vecs,
if (c->len < c->cmd_len) {
const char *buf = get_strbuf_str(&c->reply);
- n = send(c->fd, buf + c->len, c->cmd_len, MSG_NOSIGNAL);
+ n = send(c->fd, buf + c->len, c->cmd_len - c->len, MSG_NOSIGNAL);
if (n == -1) {
if (!(errno == EAGAIN || errno == EINTR))
c->error = -ECONNRESET;
diff --git a/tests/Makefile b/tests/Makefile
index 109ea75..3a5b161 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -11,12 +11,13 @@ TEST_MISSING_INITIALIZERS = $(shell \
|| echo -Wno-missing-field-initializers)
W_MISSING_INITIALIZERS := $(call TEST_MISSING_INITIALIZERS)
-CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir) -DTESTCONFDIR=\"$(TESTDIR)/conf.d\"
+CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir) -I$(daemondir) \
+ -DTESTCONFDIR=\"$(TESTDIR)/conf.d\"
CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
LIBDEPS += -L. -L $(mpathutildir) -L$(mpathcmddir) -lmultipath -lmpathutil -lmpathcmd -lcmocka
TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
- alias directio valid devt mpathvalid strbuf sysfs
+ alias directio valid devt mpathvalid strbuf sysfs features cli
HELPERS := test-lib.o test-log.o
.SILENT: $(TESTS:%=%.o)
@@ -36,6 +37,7 @@ ifneq ($(DIO_TEST_DEV),)
directio-test_FLAGS := -DDIO_TEST_DEV=\"$(DIO_TEST_DEV)\"
endif
mpathvalid-test_FLAGS := -I$(mpathvaliddir)
+features-test_FLAGS := -I$(multipathdir)/nvme
# test-specific linker flags
# XYZ-test_TESTDEPS: test libraries containing __wrap_xyz functions
@@ -73,6 +75,8 @@ strbuf-test_OBJDEPS := $(mpathutildir)/strbuf.o
sysfs-test_TESTDEPS := test-log.o
sysfs-test_OBJDEPS := $(multipathdir)/sysfs.o $(mpathutildir)/util.o
sysfs-test_LIBDEPS := -ludev -lpthread -ldl
+features-test_LIBDEPS := -ludev -lpthread
+cli-test_OBJDEPS := $(daemondir)/cli.o
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
diff --git a/tests/cli.c b/tests/cli.c
new file mode 100644
index 0000000..9e2ad3c
--- /dev/null
+++ b/tests/cli.c
@@ -0,0 +1,237 @@
+/*
+ * 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 <errno.h>
+
+#include "vector.h"
+#include "cli.h"
+
+#include "globals.c"
+#define HANDLER(x) NULL
+#include "callbacks.c"
+
+/* See cli.c */
+#define INVALID_FINGERPRINT ((uint32_t)(0))
+
+static int setup(void **state)
+{
+ return cli_init();
+}
+
+static int teardown(void **state)
+{
+ cli_exit();
+ return 0;
+}
+
+/*
+ * @NAME: test name
+ * @CMD: test command
+ * @R: retcode of get_cmdvec()
+ * @FPR: fingerprint (only if R==0)
+ * @GOOD: expect to find handler (only if R==0)
+ */
+#define client_test(NAME, CMD, R, FPR, GOOD) \
+static void client_test_##NAME(void **state) \
+{ \
+ vector v = NULL; \
+ char cmd[] = CMD; \
+ \
+ assert_int_equal(get_cmdvec(cmd, &v, false), R); \
+ if (R == 0) { \
+ assert_ptr_not_equal(v, NULL); \
+ assert_int_equal(fingerprint(v), FPR); \
+ if (GOOD) \
+ assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL); \
+ else \
+ assert_ptr_equal(find_handler_for_cmdvec(v), NULL); \
+ free_keys(v); \
+ } else \
+ assert_ptr_equal(v, NULL); \
+}
+
+/*
+ * @NAME: test name
+ * @CMD: test command
+ * @FPR: fingerprint
+ * @PAR: value of parameter for keyword 1
+ */
+#define client_param(NAME, CMD, FPR, PAR) \
+static void client_param_##NAME(void **state) \
+{ \
+ vector v = NULL; \
+ char cmd[] = CMD; \
+ \
+ assert_int_equal(get_cmdvec(cmd, &v, false), 0); \
+ assert_ptr_not_equal(v, NULL); \
+ assert_int_equal(fingerprint(v), FPR); \
+ assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL); \
+ assert_string_equal(((struct key *)VECTOR_SLOT(v, 1))->param, PAR); \
+ free_keys(v); \
+}
+
+/*
+ * @NAME: test name
+ * @CMD: test command
+ * @FPR: fingerprint
+ * @PAR1: value of parameter for keyword 1
+ * @N: index of 2nd parameter keyword
+ * @PARN: value of parameter for keyword N
+ */
+#define client_2param(NAME, CMD, FPR, PAR1, N, PARN) \
+static void client_2param_##NAME(void **state) \
+{ \
+ vector v = NULL; \
+ char cmd[] = CMD; \
+ \
+ assert_int_equal(get_cmdvec(cmd, &v, false), 0); \
+ assert_ptr_not_equal(v, NULL); \
+ assert_int_equal(fingerprint(v), FPR); \
+ assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL); \
+ assert_string_equal(((struct key *)VECTOR_SLOT(v, 1))->param, PAR1); \
+ assert_string_equal(((struct key *)VECTOR_SLOT(v, N))->param, PARN); \
+ free_keys(v); \
+}
+
+static void client_test_null(void **state)
+{
+ vector v = NULL;
+
+ /* alloc_strvec() returns ENOMEM for NULL cmd */
+ assert_int_equal(get_cmdvec(NULL, &v, false), ENOMEM);
+ assert_ptr_equal(v, NULL);
+}
+
+/* alloc_strvec() returns ENOMEM for empty string */
+client_test(empty, "", ENOMEM, 0, false);
+client_test(bogus, "bogus", ESRCH, 0, false);
+client_test(list, "list", 0, VRB_LIST, false);
+client_test(show, "show", 0, VRB_LIST, false);
+client_test(s, "s", ESRCH, 0, false);
+/* partial match works if it's unique */
+client_test(sh, "sh", ESRCH, 0, false);
+client_test(sho, "sho", 0, VRB_LIST, false);
+client_test(add, "add", 0, VRB_ADD, false);
+client_test(resume, "resume", 0, VRB_RESUME, false);
+client_test(disablequeueing, "disablequeueing", 0, VRB_DISABLEQ, false);
+/* "disable" -> disablequeueing, "queueing" -> not found */
+client_test(disable_queueing, "disable queueing", ESRCH, 0, false);
+/* ENOENT because the not-found keyword is not last pos */
+client_test(queueing_disable, "queueing disable", ENOENT, 0, false);
+client_test(disable, "disable", 0, VRB_DISABLEQ, false);
+client_test(setprkey, "setprkey", 0, VRB_SETPRKEY, false);
+client_test(quit, "quit", 0, VRB_QUIT, true);
+client_test(exit, "exit", 0, VRB_QUIT, true);
+client_test(show_maps, "show maps", 0, VRB_LIST|Q1_MAPS, true);
+client_test(sh_maps, "sh maps", ENOENT, 0, 0);
+client_test(sho_maps, "sho maps", 0, VRB_LIST|Q1_MAPS, true);
+client_test(sho_multipaths, "sho multipaths", 0, VRB_LIST|Q1_MAPS, true);
+/* Needs a parameter */
+client_test(show_map, "show map", EINVAL, 0, 0);
+client_test(show_ma, "show ma", ESRCH, 0, 0);
+client_test(show_list_maps, "show list maps", 0,
+ VRB_LIST|(VRB_LIST<<8)|(KEY_MAPS<<16), false);
+client_test(show_maps_list, "show maps list", 0,
+ VRB_LIST|(VRB_LIST<<16)|(KEY_MAPS<<8), false);
+client_test(maps_show, "maps show ", 0, (VRB_LIST<<8)|KEY_MAPS, false);
+client_test(show_maps_list_json, "show maps list json", 0,
+ VRB_LIST|(VRB_LIST<<16)|(KEY_MAPS<<8)|(KEY_JSON<<24), false);
+/* More than 4 keywords */
+client_test(show_maps_list_json_raw, "show maps list json raw", 0,
+ INVALID_FINGERPRINT, false);
+client_test(show_list_show_list, "show list show list", 0,
+ VRB_LIST|(VRB_LIST<<8)|(VRB_LIST<<16)|(VRB_LIST<<24), false);
+client_test(show_list_show_list_show, "show list show list show", 0,
+ INVALID_FINGERPRINT, false);
+client_test(q_q_q_q, "q q q q", 0,
+ VRB_QUIT|(VRB_QUIT<<8)|(VRB_QUIT<<16)|(VRB_QUIT<<24), false);
+client_test(show_path_xy, "show path xy", 0, VRB_LIST|Q1_PATH, true);
+client_param(show_path_xy, "show path xy", VRB_LIST|Q1_PATH, "xy");
+client_param(show_path_xy_2, "show path \"xy\"", VRB_LIST|Q1_PATH, "xy");
+client_param(show_path_x_y, "show path \"x y\"", VRB_LIST|Q1_PATH, "x y");
+client_param(show_path_2inch, "show path \"2\"\"\"", VRB_LIST|Q1_PATH, "2\"");
+/* missing closing quote */
+client_param(show_path_2inch_1, "show path \"2\"\"", VRB_LIST|Q1_PATH, "2\"");
+client_test(show_map_xy, "show map xy", 0, VRB_LIST|Q1_MAP, false);
+client_test(show_map_xy_bogus, "show map xy bogus", ESRCH, 0, false);
+client_2param(show_map_xy_format_h, "show map xy form %h",
+ VRB_LIST|Q1_MAP|Q2_FMT, "xy", 2, "%h");
+client_2param(show_map_xy_raw_format_h, "show map xy raw form %h",
+ VRB_LIST|Q1_MAP|Q2_RAW|Q3_FMT, "xy", 3, "%h");
+client_test(show_map_xy_format_h_raw, "show map xy form %h raw", 0,
+ VRB_LIST|(KEY_MAP<<8)|(KEY_FMT<<16)|(KEY_RAW<<24), false);
+client_param(list_path_sda, "list path sda", VRB_LIST|Q1_PATH, "sda");
+client_param(add_path_sda, "add path sda", VRB_ADD|Q1_PATH, "sda");
+client_test(list_list_path_sda, "list list path sda", 0,
+ VRB_LIST|(VRB_LIST<<8)|(KEY_PATH<<16), false);
+
+static int client_tests(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(client_test_null),
+ cmocka_unit_test(client_test_empty),
+ cmocka_unit_test(client_test_bogus),
+ cmocka_unit_test(client_test_list),
+ cmocka_unit_test(client_test_show),
+ cmocka_unit_test(client_test_s),
+ cmocka_unit_test(client_test_sh),
+ cmocka_unit_test(client_test_sho),
+ cmocka_unit_test(client_test_add),
+ cmocka_unit_test(client_test_resume),
+ cmocka_unit_test(client_test_disablequeueing),
+ cmocka_unit_test(client_test_disable_queueing),
+ cmocka_unit_test(client_test_queueing_disable),
+ cmocka_unit_test(client_test_disable),
+ cmocka_unit_test(client_test_setprkey),
+ cmocka_unit_test(client_test_quit),
+ cmocka_unit_test(client_test_exit),
+ cmocka_unit_test(client_test_show_maps),
+ cmocka_unit_test(client_test_sh_maps),
+ cmocka_unit_test(client_test_sho_maps),
+ cmocka_unit_test(client_test_sho_multipaths),
+ cmocka_unit_test(client_test_show_map),
+ cmocka_unit_test(client_test_show_ma),
+ cmocka_unit_test(client_test_maps_show),
+ cmocka_unit_test(client_test_show_list_maps),
+ cmocka_unit_test(client_test_show_maps_list),
+ cmocka_unit_test(client_test_show_maps_list_json),
+ cmocka_unit_test(client_test_show_maps_list_json_raw),
+ cmocka_unit_test(client_test_show_list_show_list),
+ cmocka_unit_test(client_test_show_list_show_list_show),
+ cmocka_unit_test(client_test_q_q_q_q),
+ cmocka_unit_test(client_test_show_map_xy),
+ cmocka_unit_test(client_test_show_map_xy_bogus),
+ cmocka_unit_test(client_test_show_path_xy),
+ cmocka_unit_test(client_param_show_path_xy),
+ cmocka_unit_test(client_param_show_path_xy_2),
+ cmocka_unit_test(client_param_show_path_x_y),
+ cmocka_unit_test(client_param_show_path_2inch),
+ cmocka_unit_test(client_param_show_path_2inch_1),
+ cmocka_unit_test(client_2param_show_map_xy_format_h),
+ cmocka_unit_test(client_2param_show_map_xy_raw_format_h),
+ cmocka_unit_test(client_test_show_map_xy_format_h_raw),
+ cmocka_unit_test(client_param_list_path_sda),
+ cmocka_unit_test(client_param_add_path_sda),
+ cmocka_unit_test(client_test_list_list_path_sda),
+ };
+
+ return cmocka_run_group_tests(tests, setup, teardown);
+}
+
+int main(void)
+{
+ int ret = 0;
+
+ init_test_verbosity(-1);
+ ret += client_tests();
+ return ret;
+}
diff --git a/tests/features.c b/tests/features.c
new file mode 100644
index 0000000..31f978f
--- /dev/null
+++ b/tests/features.c
@@ -0,0 +1,549 @@
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "../libmultipath/propsel.c"
+#include "globals.c"
+
+static void test_af_null_features_ptr(void **state)
+{
+ assert_int_equal(add_feature(NULL, "test"), 1);
+}
+
+static void af_helper(const char *features_start, const char *addition,
+ const char *features_end, int result)
+{
+ char *f = NULL, *orig = NULL;
+
+ if (features_start) {
+ f = strdup(features_start);
+ assert_non_null(f);
+ orig = f;
+ }
+ assert_int_equal(add_feature(&f, addition), result);
+ if (result != 0 || features_end == NULL)
+ assert_ptr_equal(orig, f);
+ else
+ assert_string_equal(f, features_end);
+ free(f);
+}
+
+static void test_af_null_addition1(void **state)
+{
+ af_helper("0", NULL, NULL, 0);
+}
+
+static void test_af_null_addition2(void **state)
+{
+ af_helper("1 queue_if_no_path", NULL, NULL, 0);
+}
+
+static void test_af_empty_addition(void **state)
+{
+ af_helper("2 pg_init_retries 5", "", NULL, 0);
+}
+
+static void test_af_invalid_addition1(void **state)
+{
+ af_helper("2 pg_init_retries 5", " ", NULL, 1);
+}
+
+static void test_af_invalid_addition2(void **state)
+{
+ af_helper("2 pg_init_retries 5", "\tbad", NULL, 1);
+}
+
+static void test_af_invalid_addition3(void **state)
+{
+ af_helper("2 pg_init_retries 5", "bad ", NULL, 1);
+}
+
+static void test_af_invalid_addition4(void **state)
+{
+ af_helper("2 pg_init_retries 5", " bad ", NULL, 1);
+}
+
+static void test_af_null_features1(void **state)
+{
+ af_helper(NULL, "test", "1 test", 0);
+}
+
+static void test_af_null_features2(void **state)
+{
+ af_helper(NULL, "test\t more", "2 test\t more", 0);
+}
+
+static void test_af_null_features3(void **state)
+{
+ af_helper(NULL, "test\neven\tmore", "3 test\neven\tmore", 0);
+}
+
+static void test_af_already_exists1(void **state)
+{
+ af_helper("4 this is a test", "test", NULL, 0);
+}
+
+static void test_af_already_exists2(void **state)
+{
+ af_helper("5 contest testy intestine test retest", "test", NULL, 0);
+}
+
+static void test_af_almost_exists(void **state)
+{
+ af_helper("3 contest testy intestine", "test",
+ "4 contest testy intestine test", 0);
+}
+
+static void test_af_bad_features1(void **state)
+{
+ af_helper("bad", "test", NULL, 1);
+}
+
+static void test_af_bad_features2(void **state)
+{
+ af_helper("1bad", "test", NULL, 1);
+}
+
+static void test_af_add1(void **state)
+{
+ af_helper("0", "test", "1 test", 0);
+}
+
+static void test_af_add2(void **state)
+{
+ af_helper("0", "this is a test", "4 this is a test", 0);
+}
+
+static void test_af_add3(void **state)
+{
+ af_helper("1 features", "more values", "3 features more values", 0);
+}
+
+static void test_af_add4(void **state)
+{
+ af_helper("2 one\ttwo", "three\t four", "4 one\ttwo three\t four", 0);
+}
+
+static int test_add_features(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_af_null_features_ptr),
+ cmocka_unit_test(test_af_null_addition1),
+ cmocka_unit_test(test_af_null_addition2),
+ cmocka_unit_test(test_af_empty_addition),
+ cmocka_unit_test(test_af_invalid_addition1),
+ cmocka_unit_test(test_af_invalid_addition2),
+ cmocka_unit_test(test_af_invalid_addition3),
+ cmocka_unit_test(test_af_invalid_addition4),
+ cmocka_unit_test(test_af_null_features1),
+ cmocka_unit_test(test_af_null_features2),
+ cmocka_unit_test(test_af_null_features3),
+ cmocka_unit_test(test_af_already_exists1),
+ cmocka_unit_test(test_af_already_exists2),
+ cmocka_unit_test(test_af_almost_exists),
+ cmocka_unit_test(test_af_bad_features1),
+ cmocka_unit_test(test_af_bad_features2),
+ cmocka_unit_test(test_af_add1),
+ cmocka_unit_test(test_af_add2),
+ cmocka_unit_test(test_af_add3),
+ cmocka_unit_test(test_af_add4),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+static void test_rf_null_features_ptr(void **state)
+{
+ assert_int_equal(remove_feature(NULL, "test"), 1);
+}
+
+static void test_rf_null_features(void **state)
+{
+ char *f = NULL;
+ assert_int_equal(remove_feature(&f, "test"), 1);
+}
+
+static void rf_helper(const char *features_start, const char *removal,
+ const char *features_end, int result)
+{
+ char *f = strdup(features_start);
+ char *orig = f;
+
+ assert_non_null(f);
+ assert_int_equal(remove_feature(&f, removal), result);
+ if (result != 0 || features_end == NULL)
+ assert_ptr_equal(orig, f);
+ else
+ assert_string_equal(f, features_end);
+ free(f);
+}
+
+static void test_rf_null_removal(void **state)
+{
+ rf_helper("1 feature", NULL, NULL, 0);
+}
+
+static void test_rf_empty_removal(void **state)
+{
+ rf_helper("1 feature", "", NULL, 0);
+}
+
+static void test_rf_invalid_removal1(void **state)
+{
+ rf_helper("1 feature", " ", NULL, 1);
+}
+
+static void test_rf_invalid_removal2(void **state)
+{
+ rf_helper("1 feature", " bad", NULL, 1);
+}
+
+static void test_rf_invalid_removal3(void **state)
+{
+ rf_helper("1 feature", "bad\n", NULL, 1);
+}
+
+static void test_rf_invalid_removal4(void **state)
+{
+ rf_helper("1 feature", "\tbad \n", NULL, 1);
+}
+
+static void test_rf_bad_features1(void **state)
+{
+ rf_helper("invalid feature test string", "test", NULL, 1);
+}
+
+static void test_rf_bad_features2(void **state)
+{
+ rf_helper("2no space test", "test", NULL, 1);
+}
+
+static void test_rf_missing_removal1(void **state)
+{
+ rf_helper("0", "test", NULL, 0);
+}
+
+static void test_rf_missing_removal2(void **state)
+{
+ rf_helper("1 detest", "test", NULL, 0);
+}
+
+static void test_rf_missing_removal3(void **state)
+{
+ rf_helper("4 testing one two three", "test", NULL, 0);
+}
+
+static void test_rf_missing_removal4(void **state)
+{
+ rf_helper("1 contestant", "test", NULL, 0);
+}
+
+static void test_rf_missing_removal5(void **state)
+{
+ rf_helper("3 testament protest detestable", "test", NULL, 0);
+}
+
+static void test_rf_remove_all_features1(void **state)
+{
+ rf_helper("1 test", "test", "0", 0);
+}
+
+static void test_rf_remove_all_features2(void **state)
+{
+ rf_helper("2 another\t test", "another\t test", "0", 0);
+}
+
+static void test_rf_remove1(void **state)
+{
+ rf_helper("2 feature1 feature2", "feature2", "1 feature1", 0);
+}
+
+static void test_rf_remove2(void **state)
+{
+ rf_helper("2 feature1 feature2", "feature1", "1 feature2", 0);
+}
+
+static void test_rf_remove3(void **state)
+{
+ rf_helper("3 test1 test\ttest2", "test", "2 test1 test2", 0);
+}
+
+static void test_rf_remove4(void **state)
+{
+ rf_helper("4 this\t is a test", "is a", "2 this\t test", 0);
+}
+
+static void test_rf_remove5(void **state)
+{
+ rf_helper("3 one more test", "more test", "1 one", 0);
+}
+
+static int test_remove_features(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_rf_null_features_ptr),
+ cmocka_unit_test(test_rf_null_features),
+ cmocka_unit_test(test_rf_null_removal),
+ cmocka_unit_test(test_rf_empty_removal),
+ cmocka_unit_test(test_rf_invalid_removal1),
+ cmocka_unit_test(test_rf_invalid_removal2),
+ cmocka_unit_test(test_rf_invalid_removal3),
+ cmocka_unit_test(test_rf_invalid_removal4),
+ cmocka_unit_test(test_rf_bad_features1),
+ cmocka_unit_test(test_rf_bad_features2),
+ cmocka_unit_test(test_rf_missing_removal1),
+ cmocka_unit_test(test_rf_missing_removal2),
+ cmocka_unit_test(test_rf_missing_removal3),
+ cmocka_unit_test(test_rf_missing_removal4),
+ cmocka_unit_test(test_rf_missing_removal5),
+ cmocka_unit_test(test_rf_remove_all_features1),
+ cmocka_unit_test(test_rf_remove_all_features2),
+ cmocka_unit_test(test_rf_remove1),
+ cmocka_unit_test(test_rf_remove2),
+ cmocka_unit_test(test_rf_remove3),
+ cmocka_unit_test(test_rf_remove4),
+ cmocka_unit_test(test_rf_remove5),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+static void test_cf_null_features(void **state)
+{
+ struct multipath mp = {
+ .alias = "test",
+ };
+ reconcile_features_with_queue_mode(&mp);
+ assert_null(mp.features);
+}
+
+static void cf_helper(const char *features_start, const char *features_end,
+ int queue_mode_start, int queue_mode_end)
+{
+ struct multipath mp = {
+ .alias = "test",
+ .features = strdup(features_start),
+ .queue_mode = queue_mode_start,
+ };
+ char *orig = mp.features;
+
+ assert_non_null(orig);
+ reconcile_features_with_queue_mode(&mp);
+ if (!features_end)
+ assert_ptr_equal(orig, mp.features);
+ else
+ assert_string_equal(mp.features, features_end);
+ free(mp.features);
+ assert_int_equal(mp.queue_mode, queue_mode_end);
+}
+
+static void test_cf_unset_unset1(void **state)
+{
+ cf_helper("0", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF);
+}
+
+static void test_cf_unset_unset2(void **state)
+{
+ cf_helper("1 queue_mode", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF);
+}
+
+static void test_cf_unset_unset3(void **state)
+{
+ cf_helper("queue_mode", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF);
+}
+
+static void test_cf_unset_unset4(void **state)
+{
+ cf_helper("2 queue_model bio", NULL, QUEUE_MODE_UNDEF,
+ QUEUE_MODE_UNDEF);
+}
+
+static void test_cf_unset_unset5(void **state)
+{
+ cf_helper("1 queue_if_no_path", NULL, QUEUE_MODE_UNDEF,
+ QUEUE_MODE_UNDEF);
+}
+
+static void test_cf_invalid_unset1(void **state)
+{
+ cf_helper("2 queue_mode biop", "0", QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF);
+}
+
+static void test_cf_invalid_unset2(void **state)
+{
+ cf_helper("3 queue_mode rqs queue_if_no_path", "1 queue_if_no_path",
+ QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF);
+}
+
+static void test_cf_rq_unset1(void **state)
+{
+ cf_helper("2 queue_mode rq", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_RQ);
+}
+
+static void test_cf_rq_unset2(void **state)
+{
+ cf_helper("2 queue_mode mq", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_RQ);
+}
+
+static void test_cf_bio_unset(void **state)
+{
+ cf_helper("2 queue_mode bio", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_BIO);
+}
+
+static void test_cf_unset_bio1(void **state)
+{
+ cf_helper("1 queue_if_no_path", "3 queue_if_no_path queue_mode bio",
+ QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_unset_bio2(void **state)
+{
+ cf_helper("0", "2 queue_mode bio", QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_unset_bio3(void **state)
+{
+ cf_helper("2 pg_init_retries 50", "4 pg_init_retries 50 queue_mode bio",
+ QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_invalid_bio1(void **state)
+{
+ cf_helper("2 queue_mode bad", "2 queue_mode bio",
+ QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_invalid_bio2(void **state)
+{
+ cf_helper("3 queue_if_no_path queue_mode\tbad", "3 queue_if_no_path queue_mode bio",
+ QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_bio_bio1(void **state)
+{
+ cf_helper("2 queue_mode bio", NULL, QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_bio_bio2(void **state)
+{
+ cf_helper("3 queue_if_no_path queue_mode bio", NULL, QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_bio_bio3(void **state)
+{
+ cf_helper("3 queue_mode\nbio queue_if_no_path", NULL, QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_bio_rq1(void **state)
+{
+ cf_helper("2\nqueue_mode\tbio", "0", QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_bio_rq2(void **state)
+{
+ cf_helper("3 queue_if_no_path\nqueue_mode bio", "1 queue_if_no_path",
+ QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_bio_rq3(void **state)
+{
+ cf_helper("4 queue_mode bio pg_init_retries 20", "2 pg_init_retries 20",
+ QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_unset_rq1(void **state)
+{
+ cf_helper("0", NULL, QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_unset_rq2(void **state)
+{
+ cf_helper("2 pg_init_retries 15", NULL, QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_invalid_rq1(void **state)
+{
+ cf_helper("2 queue_mode bionic", "0", QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_invalid_rq2(void **state)
+{
+ cf_helper("3 queue_mode b\nqueue_if_no_path", "1 queue_if_no_path",
+ QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_rq_rq1(void **state)
+{
+ cf_helper("2 queue_mode rq", NULL, QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_rq_rq2(void **state)
+{
+ cf_helper("3 queue_mode\t \trq\nqueue_if_no_path", NULL, QUEUE_MODE_RQ, QUEUE_MODE_RQ);
+}
+
+static void test_cf_rq_bio1(void **state)
+{
+ cf_helper("2 queue_mode rq", "2 queue_mode bio", QUEUE_MODE_BIO,
+ QUEUE_MODE_BIO);
+}
+
+static void test_cf_rq_bio2(void **state)
+{
+ cf_helper("3 queue_if_no_path\nqueue_mode rq", "3 queue_if_no_path queue_mode bio", QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static void test_cf_rq_bio3(void **state)
+{
+ cf_helper("3 queue_mode rq\nqueue_if_no_path", "3 queue_if_no_path queue_mode bio", QUEUE_MODE_BIO, QUEUE_MODE_BIO);
+}
+
+static int test_reconcile_features(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_cf_null_features),
+ cmocka_unit_test(test_cf_unset_unset1),
+ cmocka_unit_test(test_cf_unset_unset2),
+ cmocka_unit_test(test_cf_unset_unset3),
+ cmocka_unit_test(test_cf_unset_unset4),
+ cmocka_unit_test(test_cf_unset_unset5),
+ cmocka_unit_test(test_cf_invalid_unset1),
+ cmocka_unit_test(test_cf_invalid_unset2),
+ cmocka_unit_test(test_cf_rq_unset1),
+ cmocka_unit_test(test_cf_rq_unset2),
+ cmocka_unit_test(test_cf_bio_unset),
+ cmocka_unit_test(test_cf_unset_bio1),
+ cmocka_unit_test(test_cf_unset_bio2),
+ cmocka_unit_test(test_cf_unset_bio3),
+ cmocka_unit_test(test_cf_invalid_bio1),
+ cmocka_unit_test(test_cf_invalid_bio2),
+ cmocka_unit_test(test_cf_bio_bio1),
+ cmocka_unit_test(test_cf_bio_bio2),
+ cmocka_unit_test(test_cf_bio_bio3),
+ cmocka_unit_test(test_cf_bio_rq1),
+ cmocka_unit_test(test_cf_bio_rq2),
+ cmocka_unit_test(test_cf_bio_rq3),
+ cmocka_unit_test(test_cf_unset_rq1),
+ cmocka_unit_test(test_cf_unset_rq2),
+ cmocka_unit_test(test_cf_invalid_rq1),
+ cmocka_unit_test(test_cf_invalid_rq2),
+ cmocka_unit_test(test_cf_rq_rq1),
+ cmocka_unit_test(test_cf_rq_rq2),
+ cmocka_unit_test(test_cf_rq_bio1),
+ cmocka_unit_test(test_cf_rq_bio2),
+ cmocka_unit_test(test_cf_rq_bio3),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+int main(void)
+{
+ int ret = 0;
+
+ init_test_verbosity(-1);
+ ret += test_add_features();
+ ret += test_remove_features();
+ ret += test_reconcile_features();
+
+ return ret;
+}