#
include ../Makefile.inc
-CFLAGS += $(BIN_CFLAGS) -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+CFLAGS += $(BIN_CFLAGS) -I. -I$(multipathdir) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
LDFLAGS += $(BIN_LDFLAGS)
LIBDEPS += -ldevmapper
return -1;
} else {
fd_dasd = dup(fd);
+ if (fd_dasd < 0)
+ return -1;
}
if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) {
char res3[28]; /* reserved */
uint8_t ldl_version; /* version number, valid for ldl format */
uint64_t formatted_blocks; /* valid when ldl_version >= f2 */
-} __attribute__ ((packed)) volume_label_t;
+} __attribute__ ((packed, aligned(__alignof__(int)))) volume_label_t;
typedef struct extent
.RB [\| \-p \|]
.RB [\| \-f \|]
.RB [\| \-g \|]
-.RB [\| \-s \|]
+.RB [\| \-s | \-n \|]
.RB [\| \-v \|]
.B wholedisk
.
.
.TP
.B \-s
-Sync mode. Don't return until the partitions are created.
+Sync mode (Default). Don't return until the partitions are created.
+.
+.TP
+.B \-n
+Nosync mode. Return before the partitions are created.
.
.TP
.B \-v
#include "crc32.h"
#include "lopart.h"
#include "kpartx.h"
+#include "version.h"
#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
static int
usage(void) {
- printf("usage : kpartx [-a|-d|-l] [-f] [-v] wholedisk\n");
+ printf(VERSION_STRING);
+ printf("Usage:\n");
+ printf(" kpartx [-a|-d|-u|-l] [-r] [-p] [-f] [-g] [-s|-n] [-v] wholedisk\n");
printf("\t-a add partition devmappings\n");
printf("\t-r devmappings will be readonly\n");
printf("\t-d del partition devmappings\n");
printf("\t-f force devmap create\n");
printf("\t-v verbose\n");
printf("\t-n nosync mode. Return before the partitions are created\n");
- printf("\t-s sync mode. Don't return until the partitions are created. Default.\n");
+ printf("\t-s sync mode (Default). Don't return until the partitions are created\n");
return 1;
}
#include <poll.h>
#include <string.h>
#include <errno.h>
+#include <fcntl.h>
#include "mpath_cmd.h"
/*
* connect to a unix domain socket
*/
-int mpath_connect(void)
+int __mpath_connect(int nonblocking)
{
int fd, len;
struct sockaddr_un addr;
+ int flags = 0;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_LOCAL;
addr.sun_path[0] = '\0';
+ strncpy(&addr.sun_path[1], DEFAULT_SOCKET, sizeof(addr.sun_path) - 1);
len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t);
- strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len);
+ if (len > sizeof(struct sockaddr_un))
+ len = sizeof(struct sockaddr_un);
fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (fd == -1)
return -1;
+ if (nonblocking) {
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags != -1)
+ (void)fcntl(fd, F_SETFL, flags|O_NONBLOCK);
+ }
+
if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
+ int err = errno;
+
close(fd);
+ errno = err;
return -1;
}
+ if (nonblocking && flags != -1)
+ (void)fcntl(fd, F_SETFL, flags);
+
return fd;
}
+/*
+ * connect to a unix domain socket
+ */
+int mpath_connect(void)
+{
+ return __mpath_connect(0);
+}
+
int mpath_disconnect(int fd)
{
return close(fd);
#define DEFAULT_REPLY_TIMEOUT 4000
+/*
+ * DESCRIPTION:
+ * Same as mpath_connect() (see below) except for the "nonblocking"
+ * parameter.
+ * If "nonblocking" is set, connects in non-blocking mode. This is
+ * useful to avoid blocking if the listening socket's backlog is
+ * exceeded. In this case, errno will be set to EAGAIN.
+ * In case of success, the returned file descriptor is in in blocking
+ * mode, even if "nonblocking" was true.
+ *
+ * RETURNS:
+ * A file descriptor on success. -1 on failure (with errno set).
+ */
+int __mpath_connect(int nonblocking);
+
/*
* DESCRIPTION:
* Connect to the running multipathd daemon. On systems with the
#include "config.h"
#include "switchgroup.h"
#include "discovery.h"
+#include "configure.h"
#include "dmparser.h"
#include <ctype.h>
#include "propsel.h"
continue;
}
pp->mpp = mpp;
- if (pp->state == PATH_UNCHECKED ||
- pp->state == PATH_WILD) {
+ if (pp->udev == NULL) {
+ pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
+ if (pp->udev == NULL) {
+ pp->state = PATH_DOWN;
+ continue;
+ }
conf = get_multipath_config();
- pathinfo(pp, conf, DI_CHECKER);
+ pathinfo(pp, conf, DI_SYSFS|DI_CHECKER);
put_multipath_config(conf);
+ continue;
}
-
- if (pp->priority == PRIO_UNDEF) {
+ if (pp->state == PATH_UNCHECKED ||
+ pp->state == PATH_WILD) {
conf = get_multipath_config();
- pathinfo(pp, conf, DI_PRIO);
+ pathinfo(pp, conf, DI_CHECKER);
put_multipath_config(conf);
}
}
int mpath_persistent_reserve_in (int fd, int rq_servact,
struct prin_resp *resp, int noisy, int verbose)
{
- struct stat info;
- vector curmp = NULL;
- vector pathvec = NULL;
- char * alias;
- struct multipath * mpp;
- int map_present;
- int major, minor;
- int ret;
- struct config *conf;
+ int ret = mpath_persistent_reserve_init_vecs(verbose);
- conf = get_multipath_config();
- conf->verbosity = verbose;
- put_multipath_config(conf);
+ if (ret != MPATH_PR_SUCCESS)
+ return ret;
+ ret = __mpath_persistent_reserve_in(fd, rq_servact, resp, noisy);
+ mpath_persistent_reserve_free_vecs();
+ return ret;
+}
- if (fstat( fd, &info) != 0){
- condlog(0, "stat error %d", fd);
- return MPATH_PR_FILE_ERROR;
- }
- if(!S_ISBLK(info.st_mode)){
- condlog(0, "Failed to get major:minor. fd = %d", fd);
- return MPATH_PR_FILE_ERROR;
- }
+int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
+ unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose)
+{
+ int ret = mpath_persistent_reserve_init_vecs(verbose);
- major = major(info.st_rdev);
- minor = minor(info.st_rdev);
- condlog(4, "Device %d:%d: ", major, minor);
+ if (ret != MPATH_PR_SUCCESS)
+ return ret;
+ ret = __mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type,
+ paramp, noisy);
+ mpath_persistent_reserve_free_vecs();
+ return ret;
+}
- /* get alias from major:minor*/
- alias = dm_mapname(major, minor);
- if (!alias){
- condlog(0, "%d:%d failed to get device alias.", major, minor);
- return MPATH_PR_DMMP_ERROR;
- }
+static vector curmp;
+static vector pathvec;
- condlog(3, "alias = %s", alias);
- map_present = dm_map_present(alias);
- if (map_present && dm_is_mpath(alias) != 1){
- condlog( 0, "%s: not a multipath device.", alias);
- ret = MPATH_PR_DMMP_ERROR;
- goto out;
- }
+void mpath_persistent_reserve_free_vecs(void)
+{
+ free_multipathvec(curmp, KEEP_PATHS);
+ free_pathvec(pathvec, FREE_PATHS);
+ curmp = pathvec = NULL;
+}
+
+int mpath_persistent_reserve_init_vecs(int verbose)
+{
+ struct config *conf = get_multipath_config();
+
+ conf->verbosity = verbose;
+ put_multipath_config(conf);
+ if (curmp)
+ return MPATH_PR_SUCCESS;
/*
* allocate core vectors to store paths and multipaths
*/
pathvec = vector_alloc ();
if (!curmp || !pathvec){
- condlog (0, "%s: vector allocation failed.", alias);
- ret = MPATH_PR_DMMP_ERROR;
- if (curmp)
- vector_free(curmp);
- if (pathvec)
- vector_free(pathvec);
- goto out;
+ condlog (0, "vector allocation failed.");
+ goto err;
}
- if (path_discovery(pathvec, DI_SYSFS | DI_CHECKER) < 0) {
- ret = MPATH_PR_DMMP_ERROR;
- goto out1;
- }
+ if (dm_get_maps(curmp))
+ goto err;
- /* get info of all paths from the dm device */
- if (get_mpvec (curmp, pathvec, alias)){
- condlog(0, "%s: failed to get device info.", alias);
- ret = MPATH_PR_DMMP_ERROR;
- goto out1;
- }
-
- mpp = find_mp_by_alias(curmp, alias);
- if (!mpp){
- condlog(0, "%s: devmap not registered.", alias);
- ret = MPATH_PR_DMMP_ERROR;
- goto out1;
- }
-
- ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy);
+ return MPATH_PR_SUCCESS;
-out1:
- free_multipathvec(curmp, KEEP_PATHS);
- free_pathvec(pathvec, FREE_PATHS);
-out:
- FREE(alias);
- return ret;
+err:
+ mpath_persistent_reserve_free_vecs();
+ return MPATH_PR_DMMP_ERROR;
}
-int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose)
+static int mpath_get_map(int fd, char **palias, struct multipath **pmpp)
{
-
+ int ret = MPATH_PR_DMMP_ERROR;
struct stat info;
-
- vector curmp = NULL;
- vector pathvec = NULL;
-
- char * alias;
- struct multipath * mpp;
- int map_present;
int major, minor;
- int ret;
- uint64_t prkey;
- struct config *conf;
-
- conf = get_multipath_config();
- conf->verbosity = verbose;
- put_multipath_config(conf);
+ char *alias;
+ struct multipath *mpp;
- if (fstat( fd, &info) != 0){
+ if (fstat(fd, &info) != 0){
condlog(0, "stat error fd=%d", fd);
return MPATH_PR_FILE_ERROR;
}
-
if(!S_ISBLK(info.st_mode)){
condlog(3, "Failed to get major:minor. fd=%d", fd);
return MPATH_PR_FILE_ERROR;
minor = minor(info.st_rdev);
condlog(4, "Device %d:%d", major, minor);
- /* get WWN of the device from major:minor*/
+ /* get alias from major:minor*/
alias = dm_mapname(major, minor);
if (!alias){
+ condlog(0, "%d:%d failed to get device alias.", major, minor);
return MPATH_PR_DMMP_ERROR;
}
condlog(3, "alias = %s", alias);
- map_present = dm_map_present(alias);
- if (map_present && dm_is_mpath(alias) != 1){
+ if (dm_map_present(alias) && dm_is_mpath(alias) != 1){
condlog(3, "%s: not a multipath device.", alias);
- ret = MPATH_PR_DMMP_ERROR;
goto out;
}
- /*
- * allocate core vectors to store paths and multipaths
- */
- curmp = vector_alloc ();
- pathvec = vector_alloc ();
-
- if (!curmp || !pathvec){
- condlog (0, "%s: vector allocation failed.", alias);
- ret = MPATH_PR_DMMP_ERROR;
- if (curmp)
- vector_free(curmp);
- if (pathvec)
- vector_free(pathvec);
- goto out;
- }
-
- if (path_discovery(pathvec, DI_SYSFS | DI_CHECKER) < 0) {
- ret = MPATH_PR_DMMP_ERROR;
- goto out1;
- }
-
/* get info of all paths from the dm device */
if (get_mpvec(curmp, pathvec, alias)){
condlog(0, "%s: failed to get device info.", alias);
- ret = MPATH_PR_DMMP_ERROR;
- goto out1;
+ goto out;
}
mpp = find_mp_by_alias(curmp, alias);
if (!mpp) {
condlog(0, "%s: devmap not registered.", alias);
- ret = MPATH_PR_DMMP_ERROR;
- goto out1;
+ goto out;
+ }
+
+ ret = MPATH_PR_SUCCESS;
+ if (pmpp)
+ *pmpp = mpp;
+ if (palias) {
+ *palias = alias;
+ alias = NULL;
}
+out:
+ FREE(alias);
+ return ret;
+}
+
+int __mpath_persistent_reserve_in (int fd, int rq_servact,
+ struct prin_resp *resp, int noisy)
+{
+ struct multipath *mpp;
+ int ret;
+
+ ret = mpath_get_map(fd, NULL, &mpp);
+ if (ret != MPATH_PR_SUCCESS)
+ return ret;
+
+ ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy);
+
+ return ret;
+}
+
+int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
+ unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy)
+{
+ struct multipath *mpp;
+ char *alias;
+ int ret;
+ uint64_t prkey;
+ struct config *conf;
+
+ ret = mpath_get_map(fd, &alias, &mpp);
+ if (ret != MPATH_PR_SUCCESS)
+ return ret;
conf = get_multipath_config();
select_reservation_key(conf, mpp);
update_prkey(alias, 0);
}
out1:
- free_multipathvec(curmp, KEEP_PATHS);
- free_pathvec(pathvec, FREE_PATHS);
-
-out:
FREE(alias);
return ret;
}
struct multipath *mpp;
char params[PARAMS_SIZE], status[PARAMS_SIZE];
- if (dm_get_maps (curmp)){
- return 1;
- }
-
vector_foreach_slot (curmp, mpp, i){
/*
* discard out of scope maps
*/
- if (mpp->alias && refwwid &&
- strncmp (mpp->alias, refwwid, WWID_SIZE - 1)){
- free_multipath (mpp, KEEP_PATHS);
- vector_del_slot (curmp, i);
- i--;
+ if (!mpp->alias) {
+ condlog(0, "%s: map with empty alias!", __func__);
continue;
}
+ if (mpp->pg != NULL)
+ /* Already seen this one */
+ continue;
+
+ if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
+ continue;
+
dm_get_map(mpp->alias, &mpp->size, params);
condlog(3, "params = %s", params);
dm_get_status(mpp->alias, status);
* about them
*/
updatepaths(mpp);
- mpp->bestpg = select_path_group (mpp);
disassemble_status (status, mpp);
}
if (k < count)
continue;
}
- strncpy(thread[count].param.dev, pp->dev,
- FILE_NAME_SIZE - 1);
+ strlcpy(thread[count].param.dev, pp->dev,
+ FILE_NAME_SIZE);
if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){
/*
int rc;
memset(&thread, 0, sizeof(thread));
- strncpy(param.dev, dev, FILE_NAME_SIZE - 1);
+ strlcpy(param.dev, dev, FILE_NAME_SIZE);
/* Initialize and set thread joinable attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
continue;
}
- strncpy(thread[count].param.dev, pp->dev,
- FILE_NAME_SIZE - 1);
+ strlcpy(thread[count].param.dev, pp->dev,
+ FILE_NAME_SIZE);
condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn,
(void *) (&thread[count].param));
extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp,
int noisy, int verbose);
+/*
+ * DESCRIPTION :
+ * This function is like mpath_persistent_reserve_in(), except that it doesn't call
+ * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs()
+ * before and after the actual PR call.
+ */
+extern int __mpath_persistent_reserve_in(int fd, int rq_servact,
+ struct prin_resp *resp, int noisy);
+
/*
* DESCRIPTION :
* This function sends PROUT command to the DM device and get the response.
extern int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy,
int verbose);
+/*
+ * DESCRIPTION :
+ * This function is like mpath_persistent_reserve_out(), except that it doesn't call
+ * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs()
+ * before and after the actual PR call.
+ */
+extern int __mpath_persistent_reserve_out( int fd, int rq_servact, int rq_scope,
+ unsigned int rq_type, struct prout_param_descriptor *paramp,
+ int noisy);
+
+/*
+ * DESCRIPTION :
+ * This function allocates data structures and performs basic initialization and
+ * device discovery for later calls of __mpath_persistent_reserve_in() or
+ * __mpath_persistent_reserve_out().
+ * @verbose: Set verbosity level. Input argument. value:0 to 3. 0->disabled, 3->Max verbose
+ *
+ * RESTRICTIONS:
+ *
+ * RETURNS: MPATH_PR_SUCCESS if successful else returns any of the status specified
+ * above in RETURN_STATUS.
+ */
+int mpath_persistent_reserve_init_vecs(int verbose);
+
+/*
+ * DESCRIPTION :
+ * This function frees data structures allocated by
+ * mpath_persistent_reserve_init_vecs().
+ */
+void mpath_persistent_reserve_free_vecs(void);
+
#ifdef __cplusplus
}
{
int r;
- r = filter_property(conf, pp->udev, 3);
+ r = filter_property(conf, pp->udev, 3, pp->uid_attribute);
if (r > 0)
return r;
r = filter_devnode(conf->blist_devnode, conf->elist_devnode, pp->dev);
}
int
-filter_property(struct config *conf, struct udev_device *udev, int lvl)
+filter_property(struct config *conf, struct udev_device *udev, int lvl,
+ const char *uid_attribute)
{
const char *devname = udev_device_get_sysname(udev);
struct udev_list_entry *list_entry;
/*
* This is the inverse of the 'normal' matching;
* the environment variable _has_ to match.
+ * But only if the uid_attribute used for determining the WWID
+ * of the path is is present in the environment
+ * (uid_attr_seen). If this is not the case, udev probably
+ * just failed to access the device, which should not cause the
+ * device to be blacklisted (it won't be used by multipath
+ * anyway without WWID).
+ * Likewise, if no uid attribute is defined, udev-based WWID
+ * determination is effectively off, and devices shouldn't be
+ * blacklisted by missing properties (check_missing_prop).
*/
+
+ bool check_missing_prop = uid_attribute != NULL &&
+ *uid_attribute != '\0';
+ bool uid_attr_seen = false;
+
r = MATCH_PROPERTY_BLIST_MISSING;
udev_list_entry_foreach(list_entry,
udev_device_get_properties_list_entry(udev)) {
env = udev_list_entry_get_name(list_entry);
if (!env)
continue;
+
+ if (check_missing_prop && !strcmp(env, uid_attribute))
+ uid_attr_seen = true;
+
if (_blacklist_exceptions(conf->elist_property, env)) {
r = MATCH_PROPERTY_BLIST_EXCEPT;
break;
}
env = NULL;
}
+ if (r == MATCH_PROPERTY_BLIST_MISSING &&
+ (!check_missing_prop || !uid_attr_seen))
+ r = MATCH_NOTHING;
}
log_filter(devname, NULL, NULL, NULL, env, NULL, r, lvl);
int filter_wwid (vector, vector, char *, char *);
int filter_device (vector, vector, char *, char *, char *);
int filter_path (struct config *, struct path *);
-int filter_property(struct config *, struct udev_device *, int);
+int filter_property(struct config *, struct udev_device *, int, const char*);
int filter_protocol(vector, vector, struct path *);
int store_ble (vector, char *, int);
int set_ble_device (vector, char *, char *, int);
if (conf->uid_attribute)
FREE(conf->uid_attribute);
- if (conf->uid_attrs)
- FREE(conf->uid_attrs);
+ vector_reset(&conf->uid_attrs);
if (conf->getuid)
FREE(conf->getuid);
conf->remove_retries = 0;
conf->ghost_delay = DEFAULT_GHOST_DELAY;
conf->all_tg_pt = DEFAULT_ALL_TG_PT;
-
/*
* preload default hwtable
*/
free_config(conf);
return NULL;
}
+
+char *get_uid_attribute_by_attrs(struct config *conf,
+ const char *path_dev)
+{
+ vector uid_attrs = &conf->uid_attrs;
+ int j;
+ char *att, *col;
+
+ vector_foreach_slot(uid_attrs, att, j) {
+ col = strrchr(att, ':');
+ if (!col)
+ continue;
+ if (!strncmp(path_dev, att, col - att))
+ return col + 1;
+ }
+ return NULL;
+}
+
+int parse_uid_attrs(char *uid_attrs, struct config *conf)
+{
+ vector attrs = &conf->uid_attrs;
+ char *uid_attr_record, *tmp;
+ int ret = 0, count;
+
+ if (!uid_attrs)
+ return 1;
+
+ count = get_word(uid_attrs, &uid_attr_record);
+ while (uid_attr_record) {
+ tmp = strchr(uid_attr_record, ':');
+ if (!tmp) {
+ condlog(2, "invalid record in uid_attrs: %s",
+ uid_attr_record);
+ free(uid_attr_record);
+ ret = 1;
+ } else if (!vector_alloc_slot(attrs)) {
+ free(uid_attr_record);
+ ret = 1;
+ } else
+ vector_set_slot(attrs, uid_attr_record);
+ if (!count)
+ break;
+ uid_attrs += count;
+ count = get_word(uid_attrs, &uid_attr_record);
+ }
+ return ret;
+}
char * multipath_dir;
char * selector;
- char * uid_attrs;
+ struct _vector uid_attrs;
char * uid_attribute;
char * getuid;
char * features;
extern struct config *get_multipath_config(void);
extern void put_multipath_config(void *);
+int parse_uid_attrs(char *uid_attrs, struct config *conf);
+char *get_uid_attribute_by_attrs(struct config *conf,
+ const char *path_dev);
+
#endif
goto out;
agp->pgp = pgp;
- strncpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE - 1);
+ strlcpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE);
store_adaptergroup(adapters, agp);
/* create a new host port group
if (cmpp) {
condlog(2, "%s: rename %s to %s", mpp->wwid,
cmpp->alias, mpp->alias);
- strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE - 1);
+ strlcpy(mpp->alias_old, cmpp->alias, WWID_SIZE);
mpp->action = ACT_RENAME;
if (force_reload) {
mpp->force_udev_reload = 1;
if (!cmpp) {
condlog(2, "%s: remove (wwid changed)", mpp->alias);
dm_flush_map(mpp->alias);
- strncpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE - 1);
+ strlcpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE);
drop_multipath(curmp, cmpp_by_name->wwid, KEEP_PATHS);
mpp->action = ACT_CREATE;
condlog(3, "%s: set ACT_CREATE (map wwid change)",
if (!deadmap(mpp))
continue;
- strncpy(alias, mpp->alias, WWID_SIZE - 1);
+ strlcpy(alias, mpp->alias, WWID_SIZE);
vector_del_slot(newmp, i);
i--;
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
if (pp->udev && pp->uid_attribute &&
- filter_property(conf, pp->udev, 3) > 0)
+ filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
invalid = 1;
pthread_cleanup_pop(1);
if (invalid)
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
if (pp->udev && pp->uid_attribute &&
- filter_property(conf, pp->udev, 3) > 0)
+ filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
invalid = 1;
pthread_cleanup_pop(1);
if (invalid)
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
if (pp->udev && pp->uid_attribute &&
- filter_property(conf, pp->udev, 3) > 0)
+ filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
invalid = 1;
pthread_cleanup_pop(1);
if (invalid)
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
- if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) {
+ if (((dm_get_uuid(dev, tmpwwid, WWID_SIZE)) == 0)
+ && (strlen(tmpwwid))) {
refwwid = tmpwwid;
goto check;
}
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <string.h>
#include "log_pthread.h"
#include <sys/types.h>
#include "memory.h"
const char * const default_partition_delim = DEFAULT_PARTITION_DELIM;
-
-char *
-set_default (char * str)
-{
- int len;
- char * p;
-
- len = strlen(str);
- p = MALLOC(len + 1);
-
- if (!p)
- return NULL;
-
- strncat(p, str, len);
-
- return p;
-}
+#ifndef _DEFAULTS_H
+#define _DEFAULTS_H
/*
* If you add or modify a value also update multipath/multipath.conf.5
* and the TEMPLATE in libmultipath/hwtable.c
*/
#define DEFAULT_UID_ATTRIBUTE "ID_SERIAL"
+#define DEFAULT_NVME_UID_ATTRIBUTE "ID_WWN"
#define DEFAULT_UDEVDIR "/dev"
#define DEFAULT_MULTIPATHDIR "/" LIB_STRING "/multipath"
#define DEFAULT_SELECTOR "service-time 0"
#define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d"
#define MULTIPATH_SHM_BASE "/dev/shm/multipath/"
-char * set_default (char * str);
+
+static inline char *set_default(char *str)
+{
+ return strdup(str);
+}
extern const char *const default_partition_delim;
+#endif /* _DEFAULTS_H */
#include <unistd.h>
#include <errno.h>
#include <sys/sysmacros.h>
+#include <linux/dm-ioctl.h>
+#include "util.h"
#include "checkers.h"
#include "vector.h"
#include "structs.h"
}
static int
-dm_get_prefixed_uuid(const char *name, char *uuid)
+dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
{
struct dm_task *dmt;
const char *uuidtmp;
uuidtmp = dm_task_get_uuid(dmt);
if (uuidtmp)
- strcpy(uuid, uuidtmp);
+ strlcpy(uuid, uuidtmp, uuid_len);
else
uuid[0] = '\0';
return r;
}
-int dm_get_uuid(const char *name, char *uuid)
+int dm_get_uuid(const char *name, char *uuid, int uuid_len)
{
- if (dm_get_prefixed_uuid(name, uuid))
+ char tmp[DM_UUID_LEN];
+
+ if (dm_get_prefixed_uuid(name, tmp, sizeof(tmp)))
return 1;
- if (!strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN))
- memmove(uuid, uuid + UUID_PREFIX_LEN,
- strlen(uuid + UUID_PREFIX_LEN) + 1);
+ if (!strncmp(tmp, UUID_PREFIX, UUID_PREFIX_LEN))
+ strlcpy(uuid, tmp + UUID_PREFIX_LEN, uuid_len);
+ else
+ uuid[0] = '\0';
+
return 0;
}
is_mpath_part(const char *part_name, const char *map_name)
{
char *p;
- char part_uuid[WWID_SIZE], map_uuid[WWID_SIZE];
+ char part_uuid[DM_UUID_LEN], map_uuid[DM_UUID_LEN];
- if (dm_get_prefixed_uuid(part_name, part_uuid))
+ if (dm_get_prefixed_uuid(part_name, part_uuid, sizeof(part_uuid)))
return 0;
- if (dm_get_prefixed_uuid(map_name, map_uuid))
+ if (dm_get_prefixed_uuid(map_name, map_uuid, sizeof(map_uuid)))
return 0;
if (strncmp(part_uuid, "part", 4) != 0)
if (dm_get_map(name, &mpp->size, NULL))
goto out;
- dm_get_uuid(name, mpp->wwid);
+ dm_get_uuid(name, mpp->wwid, WWID_SIZE);
dm_get_info(name, &mpp->dmi);
return mpp;
char * dm_mapname(int major, int minor);
int dm_remove_partmaps (const char * mapname, int need_sync,
int deferred_remove);
-int dm_get_uuid(const char *name, char *uuid);
+int dm_get_uuid(const char *name, char *uuid, int uuid_len);
int dm_get_info (const char * mapname, struct dm_info ** dmi);
int dm_rename (const char * old, char * new, char * delim, int skip_kpartx);
int dm_reassign(const char * mapname);
declare_mp_handler(selector, set_str)
declare_mp_snprint(selector, print_str)
-declare_def_handler(uid_attrs, set_str)
-declare_def_snprint(uid_attrs, print_str)
+static int snprint_uid_attrs(struct config *conf, char *buff, int len,
+ const void *dummy)
+{
+ char *p = buff;
+ int n, j;
+ const char *att;
+
+ vector_foreach_slot(&conf->uid_attrs, att, j) {
+ n = snprintf(p, len, "%s%s", j == 0 ? "" : " ", att);
+ if (n >= len)
+ return (p - buff) + n;
+ p += n;
+ len -= n;
+ }
+ return p - buff;
+}
+
+static int uid_attrs_handler(struct config *conf, vector strvec)
+{
+ char *val;
+
+ vector_reset(&conf->uid_attrs);
+ val = set_value(strvec);
+ if (!val)
+ return 1;
+ if (parse_uid_attrs(val, conf))
+ condlog(1, "error parsing uid_attrs: \"%s\"", val);
+ condlog(3, "parsed %d uid_attrs", VECTOR_SIZE(&conf->uid_attrs));
+ FREE(val);
+ return 0;
+}
+
declare_def_handler(uid_attribute, set_str)
declare_def_snprint_defstr(uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE)
declare_ovr_handler(uid_attribute, set_str)
install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);
install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy);
- install_keyword("uid_attrs", &def_uid_attrs_handler, &snprint_def_uid_attrs);
+ install_keyword("uid_attrs", &uid_attrs_handler, &snprint_uid_attrs);
install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);
install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid);
install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name);
pp = find_path_by_devt(pathvec, devt);
if (!pp)
return store_pathinfo(pathvec, conf,
- udevice, flag, NULL);
+ udevice, flag | DI_BLACKLIST,
+ NULL);
}
return pathinfo(pp, conf, flag);
}
return 1;
}
-int
-sysfs_get_tgt_nodename (struct path *pp, char * node)
+static int
+sysfs_get_tgt_nodename(struct path *pp, char *node)
{
const char *tgtname, *value;
struct udev_device *parent, *tgtdev;
if (tgtid >= 0) {
pp->sg_id.proto_id = SCSI_PROTOCOL_SAS;
pp->sg_id.transport_id = tgtid;
- strncpy(node, value, NODE_NAME_SIZE);
+ strlcpy(node, value, NODE_NAME_SIZE);
return 0;
}
}
if (value && !strcmp(value, "usb")) {
pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC;
tgtname = udev_device_get_sysname(tgtdev);
- strncpy(node, tgtname, strlen(tgtname));
+ strlcpy(node, tgtname, NODE_NAME_SIZE);
condlog(3, "%s: skip USB device %s", pp->dev, node);
return 1;
}
if (value) {
pp->sg_id.proto_id = SCSI_PROTOCOL_FCP;
pp->sg_id.transport_id = tgtid;
- strncpy(node, value, NODE_NAME_SIZE);
+ strlcpy(node, value, NODE_NAME_SIZE);
udev_device_unref(tgtdev);
return 0;
} else
if (value) {
pp->sg_id.proto_id = SCSI_PROTOCOL_ISCSI;
pp->sg_id.transport_id = tgtid;
- strncpy(node, value, NODE_NAME_SIZE);
+ strlcpy(node, value, NODE_NAME_SIZE);
udev_device_unref(tgtdev);
return 0;
}
static int
parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len)
{
- char *p = NULL;
int len = get_unaligned_be16(&in[2]);
+ /*
+ * Strip leading and trailing whitespace
+ */
+ while (len > 0 && in[len + 3] == ' ')
+ --len;
+ while (len > 0 && in[4] == ' ') {
+ ++in;
+ --len;
+ }
+
if (len >= out_len) {
condlog(2, "vpd pg80 overflow, %d/%d bytes required",
- len, (int)out_len);
- len = out_len;
+ len + 1, (int)out_len);
+ len = out_len - 1;
}
if (len > 0) {
memcpy(out, in + 4, len);
out[len] = '\0';
}
- /*
- * Strip trailing whitspaces
- */
- p = out + len - 1;
- while (p > out && *p == ' ') {
- *p = '\0';
- p--;
- len --;
- }
return len;
}
if (vpd_type == 0x2 || vpd_type == 0x3) {
int i;
+ assert(out_len >= 2);
len = sprintf(out, "%d", vpd_type);
- for (i = 0; i < vpd_len; i++) {
+ if (2 * vpd_len >= out_len - len) {
+ condlog(1, "%s: WWID overflow, type %d, %d/%lu bytes required",
+ __func__, vpd_type,
+ 2 * vpd_len + len + 1, out_len);
+ vpd_len = (out_len - len - 1) / 2;
+ }
+ for (i = 0; i < vpd_len; i++)
len += sprintf(out + len,
"%02x", vpd[i]);
- if (len >= out_len)
- break;
- }
} else if (vpd_type == 0x8) {
- if (!memcmp("eui.", vpd, 4)) {
+ if (!memcmp("eui.", vpd, 4))
out[0] = '2';
- len = 1;
- vpd += 4;
- vpd_len -= 4;
- for (i = 0; i < vpd_len; i++) {
- len += sprintf(out + len, "%c",
- tolower(vpd[i]));
- if (len >= out_len)
- break;
- }
- len = vpd_len + 1;
- out[len] = '\0';
- } else if (!memcmp("naa.", vpd, 4)) {
+ else if (!memcmp("naa.", vpd, 4))
out[0] = '3';
- len = 1;
- vpd += 4;
- vpd_len -= 4;
- for (i = 0; i < vpd_len; i++) {
- len += sprintf(out + len, "%c",
- tolower(vpd[i]));
- if (len >= out_len)
- break;
- }
- len = vpd_len + 1;
- out[len] = '\0';
- } else {
+ else
out[0] = '8';
- len = 1;
- vpd += 4;
- vpd_len -= 4;
- if (vpd_len > out_len + 2)
- vpd_len = out_len - 2;
- memcpy(out, vpd, vpd_len);
- len = vpd_len + 1;
- out[len] = '\0';
+
+ vpd += 4;
+ len = vpd_len - 4;
+ while (len > 2 && vpd[len - 2] == '\0')
+ --len;
+ if (len > out_len - 1) {
+ condlog(1, "%s: WWID overflow, type 8/%c, %d/%lu bytes required",
+ __func__, out[0], len + 1, out_len);
+ len = out_len - 1;
}
+
+ if (out[0] == '8')
+ for (i = 0; i < len; ++i)
+ out[1 + i] = vpd[i];
+ else
+ for (i = 0; i < len; ++i)
+ out[1 + i] = tolower(vpd[i]);
+
+ /* designator should be 0-terminated, but let's make sure */
+ out[len] = '\0';
+
} else if (vpd_type == 0x1) {
const unsigned char *p;
int p_len;
p = vpd;
while ((p = memchr(vpd, ' ', vpd_len))) {
p_len = p - vpd;
- if (len + p_len > out_len - 1)
- p_len = out_len - len - 2;
+ if (len + p_len > out_len - 1) {
+ condlog(1, "%s: WWID overflow, type 1, %d/%lu bytes required",
+ __func__, len + p_len, out_len);
+ p_len = out_len - len - 1;
+ }
memcpy(out + len, vpd, p_len);
len += p_len;
if (len >= out_len - 1) {
}
out[len] = '_';
len ++;
+ if (len >= out_len - 1) {
+ out[len] = '\0';
+ break;
+ }
vpd = p;
vpd_len -= p_len;
while (vpd && *vpd == ' ') {
vpd_len --;
}
}
+ p_len = vpd_len;
+ if (p_len > 0 && len < out_len - 1) {
+ if (len + p_len > out_len - 1) {
+ condlog(1, "%s: WWID overflow, type 1, %d/%lu bytes required",
+ __func__, len + p_len + 1, out_len);
+ p_len = out_len - len - 1;
+ }
+ memcpy(out + len, vpd, p_len);
+ len += p_len;
+ out[len] = '\0';
+ }
if (len > 1 && out[len - 1] == '_') {
out[len - 1] = '\0';
len--;
old_prio = pp->priority;
pp->priority = prio_getprio(p, pp, checker_timeout);
if (pp->priority < 0) {
- condlog(3, "%s: %s prio error", pp->dev, prio_name(p));
- pp->priority = PRIO_UNDEF;
+ /* this changes pp->offline, but why not */
+ int state = path_offline(pp);
+
+ if (state == PATH_DOWN || state == PATH_PENDING) {
+ pp->priority = old_prio;
+ condlog(3, "%s: %s prio error in state %d, keeping prio = %d",
+ pp->dev, prio_name(p), state, pp->priority);
+ } else {
+ condlog(3, "%s: %s prio error in state %d",
+ pp->dev, prio_name(p), state);
+ pp->priority = PRIO_UNDEF;
+ }
return 1;
}
condlog((old_prio == pp->priority ? 4 : 3), "%s: %s prio = %u",
{
ssize_t len = -1;
- if (pp->bus == SYSFS_BUS_SCSI &&
- !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) {
+ if (pp->bus == SYSFS_BUS_SCSI) {
len = get_vpd_uid(pp);
*origin = "sysfs";
if (len < 0 && path_state == PATH_UP) {
return len;
}
-static int has_uid_fallback(struct path *pp)
+static bool has_uid_fallback(struct path *pp)
{
+ /*
+ * Falling back to direct WWID determination is dangerous
+ * if uid_attribute is set to something non-standard.
+ * Allow it only if it's either the default, or if udev
+ * has been disabled by setting 'uid_attribute ""'.
+ */
+ if (!pp->uid_attribute)
+ return false;
return ((pp->bus == SYSFS_BUS_SCSI &&
- !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) ||
- pp->bus == SYSFS_BUS_NVME);
+ (!strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE) ||
+ !strcmp(pp->uid_attribute, ""))) ||
+ (pp->bus == SYSFS_BUS_NVME &&
+ (!strcmp(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE) ||
+ !strcmp(pp->uid_attribute, ""))));
}
int
len = strlen(pp->wwid);
origin = "callout";
} else {
+ bool udev_available = udev && pp->uid_attribute
+ && *pp->uid_attribute;
- if (udev && pp->uid_attribute) {
+ if (udev_available) {
len = get_udev_uid(pp, pp->uid_attribute, udev);
- origin = "udev";
if (len <= 0)
condlog(1,
"%s: failed to get udev uid: %s",
pp->dev, strerror(-len));
-
- } else if (pp->bus == SYSFS_BUS_SCSI) {
- len = get_vpd_uid(pp);
- origin = "sysfs";
+ else
+ origin = "udev";
}
- if (len <= 0 && allow_fallback && has_uid_fallback(pp)) {
+ if ((!udev_available || (len <= 0 && allow_fallback))
+ && has_uid_fallback(pp)) {
used_fallback = 1;
len = uid_fallback(pp, path_state, &origin);
}
return PATHINFO_SKIPPED;
}
if (is_claimed_by_foreign(pp->udev) ||
- filter_property(conf, pp->udev, 4) > 0)
+ filter_property(conf, pp->udev, 4, pp->uid_attribute) > 0)
return PATHINFO_SKIPPED;
}
} else {
condlog(3, "%s: path inaccessible", pp->dev);
pp->chkrstate = pp->state = path_state;
- if (path_state == PATH_PENDING ||
- path_state == PATH_DOWN)
- pp->priority = 0;
}
}
#define WORD_SIZE 64
static int
-merge_words (char ** dst, char * word, int space)
+merge_words(char **dst, char *word)
{
char * p = *dst;
- int len;
+ int len, dstlen;
- len = strlen(*dst) + strlen(word) + space;
- *dst = REALLOC(*dst, len + 1);
+ dstlen = strlen(*dst);
+ len = dstlen + strlen(word) + 2;
+ *dst = REALLOC(*dst, len);
if (!*dst) {
free(p);
return 1;
}
- p = *dst;
-
- while (*p != '\0')
- p++;
-
- while (space) {
- *p = ' ';
- p++;
- space--;
- }
- strncpy(p, word, strlen(word) + 1);
+ p = *dst + dstlen;
+ *p = ' ';
+ ++p;
+ strncpy(p, word, len - dstlen - 1);
return 0;
}
if (!word)
return 1;
- if (merge_words(&mpp->features, word, 1)) {
+ if (merge_words(&mpp->features, word)) {
FREE(word);
return 1;
}
if (!word)
return 1;
- if (merge_words(&mpp->hwhandler, word, 1)) {
+ if (merge_words(&mpp->hwhandler, word)) {
FREE(word);
return 1;
}
num_pg_args = atoi(word);
- if (merge_words(&mpp->selector, word, 1)) {
+ if (merge_words(&mpp->selector, word))
goto out1;
- }
FREE(word);
} else {
p += get_word(p, NULL);
if (!pp)
goto out1;
- strncpy(pp->dev_t, word, BLK_DEV_SIZE - 1);
- strncpy(pp->dev, devname, FILE_NAME_SIZE - 1);
+ strlcpy(pp->dev_t, word, BLK_DEV_SIZE);
+ strlcpy(pp->dev, devname, FILE_NAME_SIZE);
if (strlen(mpp->wwid)) {
- strncpy(pp->wwid, mpp->wwid,
- WWID_SIZE - 1);
+ strlcpy(pp->wwid, mpp->wwid,
+ WWID_SIZE);
}
/* Only call this in multipath client mode */
if (!is_daemon && store_path(pathvec, pp))
} else {
if (!strlen(pp->wwid) &&
strlen(mpp->wwid))
- strncpy(pp->wwid, mpp->wwid,
- WWID_SIZE - 1);
+ strlcpy(pp->wwid, mpp->wwid,
+ WWID_SIZE);
}
FREE(word);
* in the get_dm_mpvec() code path
*/
if (!strlen(mpp->wwid))
- strncpy(mpp->wwid, pp->wwid,
- WWID_SIZE - 1);
+ strlcpy(mpp->wwid, pp->wwid, WWID_SIZE);
/*
* Update wwid for paths which may not have been
* active at the time the getuid callout was run
*/
else if (!strlen(pp->wwid))
- strncpy(pp->wwid, mpp->wwid,
- WWID_SIZE - 1);
+ strlcpy(pp->wwid, mpp->wwid, WWID_SIZE);
/*
* Do not allow in-use patch to change wwid
*/
else if (strcmp(pp->wwid, mpp->wwid) != 0) {
condlog(0, "%s: path wwid appears to have changed. Using map wwid.\n", pp->dev_t);
- strncpy(pp->wwid, mpp->wwid, WWID_SIZE);
+ strlcpy(pp->wwid, mpp->wwid, WWID_SIZE);
}
pgp->id ^= (long)pp;
*
* COMPANY_NAME
*
- * Maintainer: XXX <email>
+ * Maintainer: NAME <email>
*/
{
/* Product Name */
/* Generic NVMe */
.vendor = "NVME",
.product = ".*",
- .uid_attribute = "ID_WWN",
+ .uid_attribute = DEFAULT_NVME_UID_ATTRIBUTE,
.checker_name = NONE,
.retain_hwhandler = RETAIN_HWHANDLER_OFF,
},
/*
* DE Series
*
- * Maintainer: ng-eseries-upstream-maintainers@netapp.com
+ * Maintainer: NetApp RDAC team <ng-eseries-upstream-maintainers@netapp.com>
*/
.vendor = "LENOVO",
.product = "DE_Series",
if (alloc)
memcpy(alloc, str, size);
else
- condlog(0, "can't allocate memeory for option '%s'",
- (char *)VECTOR_SLOT(strvec, 0));
+ goto oom;
return alloc;
}
/* Even empty quotes counts as a value (An empty string) */
alloc = (char *) MALLOC(sizeof (char));
- if (!alloc) {
- condlog(0, "can't allocate memeory for option '%s'",
- (char *)VECTOR_SLOT(strvec, 0));
- return NULL;
- }
+ if (!alloc)
+ goto oom;
for (i = 2; i < VECTOR_SIZE(strvec); i++) {
str = VECTOR_SLOT(strvec, i);
if (!str) {
alloc = REALLOC(alloc, sizeof (char) * len);
if (!alloc) {
FREE(tmp);
- condlog(0, "can't allocate memeory for option '%s'",
- (char *)VECTOR_SLOT(strvec, 0));
- return NULL;
+ goto oom;
}
if (*alloc != '\0')
strncat(alloc, " ", 1);
- strncat(alloc, str, strlen(str));
+ strncat(alloc, str, len - strlen(alloc) - 1);
}
return alloc;
+oom:
+ condlog(0, "can't allocate memory for option '%s'",
+ (char *)VECTOR_SLOT(strvec, 0));
+ return NULL;
}
/* non-recursive configuration stream handler */
int group_by_prio(struct multipath *mp)
{
int i;
- unsigned int prio;
+ int prio;
struct path * pp;
struct pathgroup * pgp;
vector pathvec = NULL;
#include <sys/stat.h>
#include "debug.h"
+#include "util.h"
#include "prio.h"
static LIST_HEAD(prioritizers);
strncpy(dst->name, src->name, PRIO_NAME_LEN);
if (args)
- strncpy(dst->args, args, PRIO_ARGS_LEN - 1);
+ strlcpy(dst->args, args, PRIO_ARGS_LEN);
dst->getprio = src->getprio;
dst->handle = NULL;
ana_log, ana_log_len);
pthread_cleanup_pop(1);
if (rc >= 0)
- condlog(3, "%s: ana state = %02x [%s]", pp->dev, rc,
+ condlog(4, "%s: ana state = %02x [%s]", pp->dev, rc,
aas_print_string(rc));
return rc;
}
{
const char *origin;
- pp->uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, pp->dev);
+ pp->uid_attribute = get_uid_attribute_by_attrs(conf, pp->dev);
if (pp->uid_attribute) {
- origin = "(setting: multipath.conf defaults section)";
+ origin = "(setting: multipath.conf defaults section / uid_attrs)";
goto out;
}
#include <string.h>
#include <unistd.h>
+#include "util.h"
#include "checkers.h"
#include "vector.h"
#include "defaults.h"
vector_foreach_slot (vecs->mpvec, mp, i)
if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) {
- strncpy(mpp->alias_old, mp->alias, WWID_SIZE - 1);
+ strlcpy(mpp->alias_old, mp->alias, WWID_SIZE);
return;
}
}
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
- uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, uev->kernel);
+ uid_attribute = get_uid_attribute_by_attrs(conf, uev->kernel);
pthread_cleanup_pop(1);
val = uevent_get_env_var(uev, uid_attribute);
if (val)
uev->wwid = val;
- FREE(uid_attribute);
}
bool
bool need_merge = false;
conf = get_multipath_config();
- if (conf->uid_attrs)
+ if (VECTOR_SIZE(&conf->uid_attrs) > 0)
need_merge = true;
put_multipath_config(conf);
return 1;
}
p++;
- strncpy(devname, p, devname_len);
+ strlcpy(devname, p, devname_len);
return 0;
}
}
return makedev(maj, min);
}
-char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev)
-{
- char *uid_attribute;
- char *uid_attr_record;
- char *dev;
- char *attr;
- char *tmp;
- int count;
-
- if(!uid_attrs || !path_dev)
- return NULL;
-
- count = get_word(uid_attrs, &uid_attr_record);
- while (uid_attr_record) {
- tmp = strrchr(uid_attr_record, ':');
- if (!tmp) {
- free(uid_attr_record);
- if (!count)
- break;
- uid_attrs += count;
- count = get_word(uid_attrs, &uid_attr_record);
- continue;
- }
- dev = uid_attr_record;
- attr = tmp + 1;
- *tmp = '\0';
-
- if(!strncmp(path_dev, dev, strlen(dev))) {
- uid_attribute = STRDUP(attr);
- free(uid_attr_record);
- return uid_attribute;
- }
-
- free(uid_attr_record);
- if (!count)
- break;
- uid_attrs += count;
- count = get_word(uid_attrs, &uid_attr_record);
- }
- return NULL;
-}
-
void
setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached)
{
int devt2devname (char *, int, char *);
dev_t parse_devt(const char *dev_t);
char *convert_dev(char *dev, int is_path_device);
-char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev);
void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
int systemd_service_enabled(const char *dev);
int get_linux_version_code(void);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_LOCAL;
addr.sun_path[0] = '\0';
- len = strlen(name) + 1 + sizeof(sa_family_t);
- strncpy(&addr.sun_path[1], name, len);
+ len = strlen(name) + 1;
+ if (len >= sizeof(addr.sun_path))
+ len = sizeof(addr.sun_path) - 1;
+ memcpy(&addr.sun_path[1], name, len);
+ len += sizeof(sa_family_t);
if (bind(fd, (struct sockaddr *)&addr, len) == -1) {
condlog(3, "Couldn't bind to ux_socket, error %d", errno);
close(fd);
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000801
-#define DATE_CODE 0x041213
+#define VERSION_CODE 0x000802
+#define DATE_CODE 0x070313
#define PROG "multipath-tools"
char tmp_wwid[WWID_SIZE];
struct multipath *mp = find_mp_by_wwid(mpvec, pp1->wwid);
- if (mp != NULL && dm_get_uuid(mp->alias, tmp_wwid) == 0 &&
+ if (mp != NULL &&
+ dm_get_uuid(mp->alias, tmp_wwid, WWID_SIZE) == 0 &&
!strncmp(tmp_wwid, pp1->wwid, WWID_SIZE)) {
condlog(3, "wwid %s is already multipathed, keeping it",
pp1->wwid);
#include <pthread.h>
#include <ctype.h>
#include <string.h>
+#include <errno.h>
+#include "version.h"
static const char * pr_type_strs[] = {
"obsolete [0]",
struct udev *udev;
-int main (int argc, char * argv[])
+static int verbose, loglevel, noisy;
+
+static int handle_args(int argc, char * argv[], int line);
+
+static int do_batch_file(const char *batch_fn)
+{
+ char command[] = "mpathpersist";
+ const int ARGV_CHUNK = 2;
+ const char delims[] = " \t\n";
+ size_t len = 0;
+ char *line = NULL;
+ ssize_t n;
+ int nline = 0;
+ int argl = ARGV_CHUNK;
+ FILE *fl;
+ char **argv = calloc(argl, sizeof(*argv));
+ int ret = MPATH_PR_SUCCESS;
+
+ if (argv == NULL)
+ return MPATH_PR_OTHER;
+
+ fl = fopen(batch_fn, "r");
+ if (fl == NULL) {
+ fprintf(stderr, "unable to open %s: %s\n",
+ batch_fn, strerror(errno));
+ free(argv);
+ return MPATH_PR_SYNTAX_ERROR;
+ } else {
+ if (verbose >= 2)
+ fprintf(stderr, "running batch file %s\n",
+ batch_fn);
+ }
+
+ while ((n = getline(&line, &len, fl)) != -1) {
+ char *_token, *token;
+ int argc = 0;
+ int rv;
+
+ nline++;
+ argv[argc++] = command;
+
+ if (line[n-1] == '\n')
+ line[n-1] = '\0';
+ if (verbose >= 3)
+ fprintf(stderr, "processing line %d: %s\n",
+ nline, line);
+
+ for (token = strtok_r(line, delims, &_token);
+ token != NULL && *token != '#';
+ token = strtok_r(NULL, delims, &_token)) {
+
+ if (argc >= argl) {
+ int argn = argl + ARGV_CHUNK;
+ char **tmp;
+
+ tmp = realloc(argv, argn * sizeof(*argv));
+ if (tmp == NULL)
+ break;
+ argv = tmp;
+ argl = argn;
+ }
+
+ if (argc == 1 && !strcmp(token, command))
+ continue;
+
+ argv[argc++] = token;
+ }
+
+ if (argc <= 1)
+ continue;
+
+ if (verbose >= 2) {
+ int i;
+
+ fprintf(stderr, "## file %s line %d:", batch_fn, nline);
+ for (i = 0; i < argc; i++)
+ fprintf(stderr, " %s", argv[i]);
+ fprintf(stderr, "\n");
+ }
+
+ optind = 0;
+ rv = handle_args(argc, argv, nline);
+ if (rv != MPATH_PR_SUCCESS)
+ ret = rv;
+ }
+
+ fclose(fl);
+ free(argv);
+ free(line);
+ return ret;
+}
+
+static int handle_args(int argc, char * argv[], int nline)
{
- int fd, c, res;
+ int fd, c;
const char *device_name = NULL;
int num_prin_sa = 0;
int num_prout_sa = 0;
int prin = 1;
int prin_sa = -1;
int prout_sa = -1;
- int verbose = 0;
- int loglevel = 0;
- int noisy = 0;
int num_transport =0;
+ char *batch_fn = NULL;
void *resp = NULL;
struct transportid * tmp;
- struct config *conf;
-
- if (optind == argc)
- {
-
- fprintf (stderr, "No parameter used\n");
- usage ();
- exit (1);
- }
-
- if (getuid () != 0)
- {
- fprintf (stderr, "need to be root\n");
- exit (1);
- }
-
- udev = udev_new();
- conf = mpath_lib_init();
- if(!conf) {
- udev_unref(udev);
- exit(1);
- }
memset(transportids, 0, MPATH_MX_TIDS * sizeof(struct transportid));
- multipath_conf = conf;
while (1)
{
int option_index = 0;
- c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:",
+ c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:f:",
long_options, &option_index);
if (c == -1)
break;
switch (c)
{
+ case 'f':
+ if (nline != 0) {
+ fprintf(stderr,
+ "ERROR: -f option not allowed in batch file\n");
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out;
+ }
+ if (batch_fn != NULL) {
+ fprintf(stderr,
+ "ERROR: -f option can be used at most once\n");
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out;
+ }
+ batch_fn = strdup(optarg);
+ break;
case 'v':
- if (1 != sscanf (optarg, "%d", &loglevel))
+ if (nline == 0 && 1 != sscanf (optarg, "%d", &loglevel))
{
fprintf (stderr, "bad argument to '--verbose'\n");
return MPATH_PR_SYNTAX_ERROR;
default:
fprintf(stderr, "unrecognised switch " "code 0x%x ??\n", c);
- usage ();
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
{
for (; optind < argc; ++optind)
fprintf (stderr, "Unexpected extra argument: %s\n", argv[optind]);
- usage ();
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
}
- /* set verbosity */
- noisy = (loglevel >= 3) ? 1 : hex;
- verbose = (loglevel >= 3)? 3: loglevel;
+ if (nline == 0) {
+ /* set verbosity */
+ noisy = (loglevel >= 3) ? 1 : hex;
+ verbose = (loglevel >= 3)? 3: loglevel;
+ ret = mpath_persistent_reserve_init_vecs(verbose);
+ if (ret != MPATH_PR_SUCCESS)
+ goto out;
+ }
- if ((prout_flag + prin_flag) == 0)
+ if ((prout_flag + prin_flag) == 0 && batch_fn == NULL)
{
fprintf (stderr, "choose either '--in' or '--out' \n");
- usage ();
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
if ((prout_flag + prin_flag) > 1)
{
fprintf (stderr, "choose either '--in' or '--out' \n");
- usage ();
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
{
fprintf (stderr,
" No service action given for Persistent Reserve IN\n");
- usage();
ret = MPATH_PR_SYNTAX_ERROR;
}
else if (num_prin_sa > 1)
{
fprintf (stderr, " Too many service actions given; choose "
"one only\n");
- usage();
ret = MPATH_PR_SYNTAX_ERROR;
}
}
else
{
- usage ();
- ret = MPATH_PR_SYNTAX_ERROR;
+ if (batch_fn == NULL)
+ ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
{
fprintf (stderr, " --relative-target-port"
" only useful with --register-move\n");
- usage ();
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
if (device_name == NULL)
{
fprintf (stderr, "No device name given \n");
- usage ();
ret = MPATH_PR_SYNTAX_ERROR;
goto out;
}
goto out;
}
- ret = mpath_persistent_reserve_in (fd, prin_sa, resp, noisy, verbose);
+ ret = __mpath_persistent_reserve_in (fd, prin_sa, resp, noisy);
if (ret != MPATH_PR_SUCCESS )
{
fprintf (stderr, "Persistent Reserve IN command failed\n");
}
/* PROUT commands other than 'register and move' */
- ret = mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type,
- paramp, noisy, verbose);
+ ret = __mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type,
+ paramp, noisy);
for (j = 0 ; j < num_transport; j++)
{
tmp = paramp->trnptid_list[j];
printf("PR out: command failed\n");
}
- res = close (fd);
- if (res < 0)
+ close (fd);
+
+out :
+ if (ret == MPATH_PR_SYNTAX_ERROR) {
+ free(batch_fn);
+ if (nline == 0)
+ usage();
+ else
+ fprintf(stderr, "syntax error on line %d in batch file\n",
+ nline);
+ } else if (batch_fn != NULL) {
+ int rv = do_batch_file(batch_fn);
+
+ free(batch_fn);
+ ret = ret == 0 ? rv : ret;
+ }
+ if (nline == 0)
+ mpath_persistent_reserve_free_vecs();
+ return (ret >= 0) ? ret : MPATH_PR_OTHER;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (optind == argc)
+ {
+
+ fprintf (stderr, "No parameter used\n");
+ usage ();
+ exit (1);
+ }
+
+ if (getuid () != 0)
{
- mpath_lib_exit(conf);
+ fprintf (stderr, "need to be root\n");
+ exit (1);
+ }
+
+ udev = udev_new();
+ multipath_conf = mpath_lib_init();
+ if(!multipath_conf) {
udev_unref(udev);
- return MPATH_PR_FILE_ERROR;
+ exit(1);
}
-out :
- mpath_lib_exit(conf);
+ ret = handle_args(argc, argv, 0);
+
+ mpath_lib_exit(multipath_conf);
udev_unref(udev);
+
return (ret >= 0) ? ret : MPATH_PR_OTHER;
}
static void usage(void)
{
+ fprintf(stderr, VERSION_STRING);
fprintf(stderr,
"Usage: mpathpersist [OPTIONS] [DEVICE]\n"
" Options:\n"
" 4 Informational messages with trace enabled\n"
" --clear|-C PR Out: Clear\n"
" --device=DEVICE|-d DEVICE query or change DEVICE\n"
+ " --batch-file|-f FILE run commands from FILE\n"
" --help|-h output this usage message\n"
" --hex|-H output response in hex\n"
" --in|-i request PR In command \n"
" --param-aptpl|-Z PR Out parameter 'APTPL'\n"
" --read-keys|-k PR In: Read Keys\n"
" --param-rk=RK|-K RK PR Out parameter reservation key\n"
- " --param-sark=SARK|-S SARK PR Out parameter service "
- "action\n"
- " reservation key (SARK is in "
- "hex)\n"
+ " --param-sark=SARK|-S SARK PR Out parameter service action\n"
+ " reservation key (SARK is in hex)\n"
" --preempt|-P PR Out: Preempt\n"
" --preempt-abort|-A PR Out: Preempt and Abort\n"
" --prout-type=TYPE|-T TYPE PR Out command type\n"
" --release|-L PR Out: Release\n"
" --report-capabilities|-c PR In: Report Capabilities\n"
" --reserve|-R PR Out: Reserve\n"
- " --transport-id=TIDS|-X TIDS TransportIDs can be mentioned \n"
- " in several forms\n"
+ " --transport-id=TIDS|-X TIDS TransportIDs can be mentioned\n"
+ " in several forms\n"
+ " --alloc-length=LEN|-l LEN PR In: maximum allocation length\n"
" Examples:\n"
" mpathpersist --out --register --param-sark=123abc --prout-type=5 /dev/mapper/mpath9\n"
- " mpathpersist -i -k /dev/mapper/mpath9\n" );
+ " mpathpersist -i -k /dev/mapper/mpath9\n"
+ " mpathpersist --out --reserve --param-sark=123abc --prout-type=8 -d /dev/mapper/mpath9\n"
+ " mpathpersist -i -s -d /dev/mapper/mpath9\n");
}
void
{"verbose", 1, NULL, 'v'},
{"clear", 0, NULL, 'C'},
{"device", 1, NULL, 'd'},
+ {"batch-file", 1, NULL, 'f' },
{"help", 0, NULL, 'h'},
{"hex", 0, NULL, 'H'},
{"in", 0, NULL, 'i'},
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MPATHPERSIST 8 2016-10-30 "Linux"
+.TH MPATHPERSIST 8 2019-05-27 "Linux"
.
.
.\" ----------------------------------------------------------------------------
Query or change DEVICE.
.
.TP
+.BI \--batch-file=\fIDEVICE\fB|\-f " FILE"
+Read commands from \fIFILE\fR. See section \(dqBATCH FILES\(dq below. This
+option can be given at most once.
+.
+.TP
.B \--help|\-h
Output this usage message.
.
.SH EXAMPLE
.\" ----------------------------------------------------------------------------
.
-.TP
-Register the Service Action Reservation Key for the /dev/mapper/mpath9 device:
-\fBmpathpersist --out --register --param-sark=\fI123abc \fB--prout-type=\fI5 /dev/mapper/mpath9\fR
-.TP
-Read the Service Action Reservation Key for the /dev/mapper/mpath9 device:
+.PP
+Register the key \(dq123abc\(dq for the /dev/mapper/mpath9 device:
+.RS
+\fBmpathpersist --out --register --param-sark=\fI123abc /dev/mapper/mpath9\fR
+.RE
+.PP
+Read registered reservation keys for the /dev/mapper/mpath9 device:
+.RS
\fBmpathpersist -i -k \fI/dev/mapper/mpath9\fR
-.TP
-Reserve the Service Action Reservation Key for the /dev/mapper/mpath9 device:
-\fBmpathpersist --out --reserve --param-sark=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
-.TP
+.RE
+.PP
+Create a reservation for the /dev/mapper/mpath9 device with the given
+reservation key:
+.RS
+\fBmpathpersist --out --reserve --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
Read the reservation status of the /dev/mapper/mpath9 device:
+.RS
\fBmpathpersist -i -s -d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Release the previously created reservation (note that the prout-type needs to
+be the same as above):
+.RS
+\fBmpathpersist --out --release --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Remove the current key registered for this host (i.e. reset it to 0):
+.RS
+\fBmpathpersist --out --register-ignore -K \fI123abc\fB -S \fI0\fB \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Remove current reservation, and unregister all registered keys from all I_T nexuses:
+.RS
+\fBmpathpersist -oCK \fI123abc \fI/dev/mapper/mpath9\fR
+.RE
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH BATCH FILES
+.\" ----------------------------------------------------------------------------
+.
+.PP
+The option \fI--batch-file\fR (\fI-f\fR) sets an input file to be processed
+by \fBmpathpersist\fR. Grouping commands in batch files can provide a speed
+improvement in particular on large installments, because \fBmpathpersist\fR
+needs to scan existing paths and maps only once during startup.
+.
+.PP
+The input file is a text file that is parsed
+line by line. Every line of the file is interpreted as a command line
+(i.e. list of options and parameters) for \fBmpathpersist\fR. Options
+and parameters are separated by one or more whitespace characters (space or TAB).
+Lines can, but do not have to, begin with the word \(dqmpathpersist\(dq.
+The \(dq#\(dq character, either at the beginning of the line or following
+some whitespace, denotes the start of a comment that lasts until the end of the
+line. Empty lines are allowed. Continuation of mpathpersist commands over
+multiple lines is not supported.
+.
+.PP
+All options listed in this man page, except \fI-f\fR and
+\fI-v\fR, are allowed in batch files. Both short and long option formats may be used.
+Using the \fI-f\fR option inside the batch file is an error. The \fI-v\fR
+option is ignored in batch files.
+.
+.PP
+The multipath map on which to act must be specified on every input line, e.g. using the \fI-d\fR option.
+Commands acting on different multipath maps may be combined in a
+batch file, and multiple commands may act on the same multipath
+map. Commands are executed one by one, so
+that commands further down in the file see status changes caused by previous
+commands.
+If \fBmpathpersist\fR encounters an error while processing a line in the
+batch file, batch file processing is \fBnot\fR aborted; subsequent commands
+are executed nonetheless. The exit status of \fBmpathpersist\fR is the status
+of the first failed command, or 0 if all commands succeeded.
+.
+.PP
+If other options and parameters are used along with
+\fI-f\fR on the \fBmpathpersist\fR command line, the command line will be executed first, followed
+by the commands from the the batch file.
+.
+.PP
+Below is an example of a valid batch input file.
+.
+.PP
+.RS
+.EX
+# This is an mpathpersist input file.
+# Short and long forms of the same command
+-i -k /dev/dm-1 # short form, this comment is ignored
+mpathpersist --in --read-keys --device=/dev/dm-1
+
+# Mixing of long and short options, variable white space
+ --out --register -S abcde /dev/dm-1
+
+# Mixing of commands for different maps
+-ir /dev/dm-0
+-ir /dev/dm-1
+
+mpathpersist --out --param-rk abcde --reserve --prout-type 5 /dev/dm-1
+# This should now show a reservation
+-ir /dev/dm-1
+-oCK abcde /dev/dm-1
+--in --read-reservation /dev/dm-1
+.EE
+.RE
.
.
.\" ----------------------------------------------------------------------------
{
fprintf (stderr, VERSION_STRING);
fprintf (stderr, "Usage:\n");
- fprintf (stderr, " %s [-a|-c|-w|-W] [-d] [-r] [-i] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname);
- fprintf (stderr, " %s -l|-ll|-f [-v lvl] [-b fil] [-R num] [dev]\n", progname);
- fprintf (stderr, " %s -F [-v lvl] [-R num]\n", progname);
- fprintf (stderr, " %s [-t|-T]\n", progname);
- fprintf (stderr, " %s -h\n", progname);
+ fprintf (stderr, " %s [-v level] [-B|-d|-i|-q|-r] [-b file] [-p policy] [device]\n", progname);
+ fprintf (stderr, " %s [-v level] [-R retries] -f device\n", progname);
+ fprintf (stderr, " %s [-v level] [-R retries] -F\n", progname);
+ fprintf (stderr, " %s [-v level] [-l|-ll] [device]\n", progname);
+ fprintf (stderr, " %s [-v level] [-a|-w] device\n", progname);
+ fprintf (stderr, " %s [-v level] -W\n", progname);
+ fprintf (stderr, " %s [-v level] [-i] [-c|-C] device\n", progname);
+ fprintf (stderr, " %s [-v level] [-i] [-u|-U]\n", progname);
+ fprintf (stderr, " %s [-h|-t|-T]\n", progname);
fprintf (stderr,
"\n"
"Where:\n"
" -b fil bindings file location\n"
" -w remove a device from the wwids file\n"
" -W reset the wwids file include only the current devices\n"
- " -p pol force all maps to specified path grouping policy :\n"
+ " -R num number of times to retry removes of in-use devices\n"
+ " -u check if the device specified in the program environment should be a\n"
+ " path in a multipath device\n"
+ " -U check if the device specified in the program environment is a\n"
+ " multipath device with usable paths, see -C flag\n"
+ " -p pol force all maps to specified path grouping policy:\n"
" . failover one path per priority group\n"
" . multibus all paths in one priority group\n"
" . group_by_serial one priority group per serial\n"
" . group_by_prio one priority group per priority lvl\n"
" . group_by_node_name one priority group per target node\n"
- " -v lvl verbosity level\n"
+ " -v lvl verbosity level:\n"
" . 0 no output\n"
" . 1 print created devmap names only\n"
" . 2 default verbosity\n"
" . 3 print debug information\n"
- " -R num number of times to retry removes of in-use devices\n"
- " dev action limited to:\n"
- " . multipath named 'dev' (ex: mpath0) or\n"
- " . multipath whose wwid is 'dev' (ex: 60051..)\n"
- " . multipath including the path named 'dev' (ex: /dev/sda)\n"
- " . multipath including the path with maj:min 'dev' (ex: 8:0)\n"
+ " device action limited to:\n"
+ " . multipath named 'device' (ex: mpath0)\n"
+ " . multipath whose wwid is 'device' (ex: 60051...)\n"
+ " . multipath including the path named 'device' (ex: /dev/sda or\n"
+ " /dev/dm-0)\n"
+ " . multipath including the path with maj:min 'device' (ex: 8:0)\n"
);
}
return r;
}
+static int test_multipathd_socket(void)
+{
+ int fd;
+ /*
+ * "multipath -u" may be run before the daemon is started. In this
+ * case, systemd might own the socket but might delay multipathd
+ * startup until some other unit (udev settle!) has finished
+ * starting. With many LUNs, the listen backlog may be exceeded, which
+ * would cause connect() to block. This causes udev workers calling
+ * "multipath -u" to hang, and thus creates a deadlock, until "udev
+ * settle" times out. To avoid this, call connect() in non-blocking
+ * mode here, and take EAGAIN as indication for a filled-up systemd
+ * backlog.
+ */
+
+ fd = __mpath_connect(1);
+ if (fd == -1) {
+ if (errno == EAGAIN)
+ condlog(3, "daemon backlog exceeded");
+ else
+ return 0;
+ } else
+ close(fd);
+ return 1;
+}
+
int
main (int argc, char *argv[])
{
}
if (cmd == CMD_VALID_PATH &&
dev_type == DEV_UEVENT) {
- int fd;
-
- fd = mpath_connect();
- if (fd == -1) {
+ if (!test_multipathd_socket()) {
condlog(3, "%s: daemon is not running", dev);
if (!systemd_service_enabled(dev)) {
r = print_cmd_valid(RTVL_NO, NULL, conf);
goto out;
}
- } else
- mpath_disconnect(fd);
+ }
}
if (cmd == CMD_REMOVE_WWID && !dev) {
.
.TP
.B uid_attrs
-The udev attribute providing a unique path identifier for corresponding
-type of path devices. If this field is configured and matched with type
-of device, it would override any other methods providing for device
-unique identifier in config file, and it would activate merging uevents
-according to the identifier to promote effiecncy in processing uevents.
-It has no default value, if you want to identify path by udev attribute
-and to activate merging uevents for SCSI and DASD devices, you can set
-its value as: \fIuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR.
+.
+Setting this option activates \fBmerging uevents\fR by WWID, which may improve
+uevent processing effiency. Moreover, it's an alternative method to configure
+the udev properties to use for determining unique path identifiers (WWIDs).
.RS
-.TP
-The default is: \fB<unset>\fR
+.PP
+The value of this option is a space separated list of records like
+\(dq\fItype:ATTR\fR\(dq, where \fItype\fR is matched against the beginning
+of the device node name (e.g. \fIsd:ATTR\fR matches \fIsda\fR), and
+\fIATTR\fR is the name of the udev property to use for matching devices.
+.PP
+If this option is configured and matches the device
+node name of a device, it overrides any other configured methods for
+determining the WWID for this device.
+.PP
+The default is: \fB<unset>\fR. To enable uevent merging, set it e.g. to
+\(dqsd:ID_SERIAL dasd:ID_UID nvme:ID_WWN\(dq.
.RE
.
.
.TP
.B uid_attribute
-The udev attribute providing a unique path identifier.
+The udev attribute providing a unique path identifier (WWID). If
+\fIuid_attribute\fR is set to the empty string, WWID determination is done
+using the \fIsysfs\fR method rather then using udev (not recommended in
+production; see \fBWWID generation\fR below).
.RS
.TP
The default is: \fBID_SERIAL\fR, for SCSI devices
.
.RS
.PP
+.B Note:
+The behavior of this option has changed in \fBmultipath-tools\fR 0.8.2
+compared to previous versions.
+Blacklisting by missing properties is only applied to devices which do have the
+property specified by \fIuid_attribute\fR (e.g. \fIID_SERIAL\fR)
+set. Previously, it was applied to every device, possibly causing devices to be
+blacklisted because of temporary I/O error conditions.
+.PP
The default \fIblacklist exception\fR is: \fB(SCSI_IDENT_|ID_WWN)\fR, causing
well-behaved SCSI devices and devices that provide a WWN (World Wide Number)
to be included, and all others to be excluded.
The WWID is generated by four methods (in the order of preference):
.TP 17
.B uid_attrs
-The WWID is derived from udev attributes by matching the device node name. See
-description of \fIuid_attrs\fR in the defaults section above.
+The WWID is derived from udev attributes by matching the device node name; cf
+\fIuid_attrs\fR above.
.TP
.B getuid_callout
Use the specified external program; cf \fIgetuid_callout\fR above.
certain circumstances udev might not be able to generate the requested
variable.
.TP
-.B vpd_pg83
-If none of the \fIgetuid_callout\fR or \fIuid_attribute\fR parameters
-are present multipath will try to use the sysfs attribute
-\fIvpd_pg83\fR to generate the WWID.
+.B sysfs
+Try to determine the WWID from sysfs attributes.
+For SCSI devices, this means reading the Vital Product Data (VPD) page
+\(dqDevice Identification\(dq (0x83).
+.PP
+The default settings (using udev and \fBuid_attribute\fR configured from
+the built-in hardware table) should work fine
+in most scenarios. Users who want to enable uevent merging must set
+\fBuid_attrs\fR.
.
.
.\" ----------------------------------------------------------------------------
if (r) {
*reply = genhelp_handler(cmd, r);
+ if (*reply == NULL)
+ return EINVAL;
*len = strlen(*reply) + 1;
return 0;
}
h = find_handler(fingerprint(cmdvec));
if (!h || !h->fn) {
+ free_keys(cmdvec);
*reply = genhelp_handler(cmd, EINVAL);
+ if (*reply == NULL)
+ return EINVAL;
*len = strlen(*reply) + 1;
- free_keys(cmdvec);
return 0;
}
if (m >= MAX_REPLY_LEN) { \
condlog(1, "Warning: max reply length exceeded"); \
free(tmp); \
- r = NULL; \
+ (r) = NULL; \
+ } else { \
+ (r) = REALLOC((r), (m) * 2); \
+ if ((r)) { \
+ memset((r) + (m), 0, (m)); \
+ (m) *= 2; \
+ } \
+ else \
+ free(tmp); \
} \
- (r) = REALLOC((r), (m) * 2); \
- if ((r)) { \
- memset((r) + (m), 0, (m)); \
- (m) *= 2; \
- } \
- else \
- free(tmp); \
} \
} while (0)
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
r = store_pathinfo(vecs->pathvec, conf,
- udevice, DI_ALL, &pp);
+ udevice, DI_ALL | DI_BLACKLIST, &pp);
pthread_cleanup_pop(1);
udev_device_unref(udevice);
if (!pp) {
pthread_mutex_unlock(&config_lock);
}
+/*
+ * If the current status is @oldstate, wait for at most @ms milliseconds
+ * for the state to change, and return the new state, which may still be
+ * @oldstate.
+ */
+enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
+ unsigned long ms)
+{
+ enum daemon_status st;
+ struct timespec tmo;
+
+ if (oldstate == DAEMON_SHUTDOWN)
+ return DAEMON_SHUTDOWN;
+
+ pthread_mutex_lock(&config_lock);
+ pthread_cleanup_push(config_cleanup, NULL);
+ st = running_state;
+ if (st == oldstate && clock_gettime(CLOCK_MONOTONIC, &tmo) == 0) {
+ tmo.tv_nsec += ms * 1000 * 1000;
+ normalize_timespec(&tmo);
+ (void)pthread_cond_timedwait(&config_cond, &config_lock, &tmo);
+ st = running_state;
+ }
+ pthread_cleanup_pop(1);
+ return st;
+}
+
/* must be called with config_lock held */
static void __post_config_state(enum daemon_status state)
{
if (strlen(mpp->wwid))
return;
- dm_get_uuid(mpp->alias, mpp->wwid);
+ dm_get_uuid(mpp->alias, mpp->wwid, WWID_SIZE);
}
static void set_no_path_retry(struct multipath *mpp)
void exit_daemon(void);
const char * daemon_status(void);
+enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
+ unsigned long ms);
int need_to_delay_reconfig (struct vectors *);
int reconfigure (struct vectors *);
int ev_add_path (struct path *, struct vectors *, int);
.RB [\| \-v\ \c
.IR verbosity \|]
.RB [\| \-B \|]
+.RB [\| \-w \|]
.
.
.\" ----------------------------------------------------------------------------
multipathd. See
.BR multipath.conf(5).
.
+.TP
+.B \-w
+Since kernel 4.14 a new device-mapper event polling interface is used for updating
+multipath devices on dmevents. Use this flag to force it to use the old event
+waiting method, based on creating a seperate thread for each device.
+.
.
.
.\" ----------------------------------------------------------------------------
continue;
}
+ /*
+ * Client connection. We shouldn't answer while we're
+ * configuring - nothing may be configured yet.
+ * But we can't wait forever either, because this thread
+ * must handle signals. So wait a short while only.
+ */
+ if (wait_for_state_change_if(DAEMON_CONFIGURE, 10)
+ == DAEMON_CONFIGURE) {
+ handle_signals(false);
+ continue;
+ }
+
/* see if a client wants to speak to us */
for (i = 1; i < num_clients + 1; i++) {
if (polls[i].revents & POLLIN) {
#include <signal.h>
#include <urcu.h>
+#include "util.h"
#include "vector.h"
#include "memory.h"
#include "checkers.h"
if (!wp)
goto out;
- strncpy(wp->mapname, mpp->alias, WWID_SIZE - 1);
+ strlcpy(wp->mapname, mpp->alias, WWID_SIZE);
wp->vecs = vecs;
if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) {
CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka
-TESTS := uevent parser util dmevents hwtable blacklist unaligned
+TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd
.SILENT: $(TESTS:%=%.o)
.PRECIOUS: $(TESTS:%=%-test)
hwtable-test_LIBDEPS := -ludev -lpthread -ldl
blacklist-test_OBJDEPS := ../libmultipath/blacklist.o
blacklist-test_LIBDEPS := -ludev
+vpd-test_OBJDEPS := ../libmultipath/discovery.o
+vpd-test_LIBDEPS := -ludev -lpthread -ldl
lib/libchecktur.so:
mkdir lib
static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
conf.blist_property = blist_property_wwn;
expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n");
- assert_int_equal(filter_property(&conf, &udev, 3),
+ assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
MATCH_PROPERTY_BLIST);
}
static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
conf.elist_property = blist_property_wwn;
expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n");
- assert_int_equal(filter_property(&conf, &udev, 3),
+ assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
MATCH_PROPERTY_BLIST_EXCEPT);
}
static void test_property_missing(void **state)
{
- static struct udev_device udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", NULL } };
+ static struct udev_device udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", "ID_SERIAL", NULL } };
conf.blist_property = blist_property_wwn;
expect_condlog(3, "sdb: blacklisted, udev property missing\n");
- assert_int_equal(filter_property(&conf, &udev, 3),
+ assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
MATCH_PROPERTY_BLIST_MISSING);
+ assert_int_equal(filter_property(&conf, &udev, 3, "ID_BLAH"),
+ MATCH_NOTHING);
+ assert_int_equal(filter_property(&conf, &udev, 3, ""),
+ MATCH_NOTHING);
+ assert_int_equal(filter_property(&conf, &udev, 3, NULL),
+ MATCH_NOTHING);
}
struct udev_device test_udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
assert_int_equal(filter_path(&conf, &test_pp), MATCH_WWID_BLIST);
}
-struct udev_device miss_udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", NULL } };
+struct udev_device miss_udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", "ID_SERIAL", NULL } };
struct path miss1_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI,
.udev = &miss_udev,
+ .uid_attribute = "ID_SERIAL",
.sg_id.proto_id = SCSI_PROTOCOL_ISCSI,
.vendor_id = "foo", .product_id = "baz",
.wwid = "plugh" };
struct path miss2_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI,
.udev = &test_udev,
+ .uid_attribute = "ID_SERIAL",
+ .sg_id.proto_id = SCSI_PROTOCOL_ISCSI,
+ .vendor_id = "foo", .product_id = "baz",
+ .wwid = "plugh" };
+
+struct path miss3_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI,
+ .udev = &miss_udev,
+ .uid_attribute = "ID_EGGS",
.sg_id.proto_id = SCSI_PROTOCOL_ISCSI,
.vendor_id = "foo", .product_id = "baz",
.wwid = "plugh" };
MATCH_NOTHING);
}
+/* Here we use a different uid_attribute which is also missing, thus
+ the path is not blacklisted */
+static void test_filter_path_missing3(void **state)
+{
+ conf.blist_property = blist_property_wwn;
+ conf.blist_devnode = blist_devnode_sdb;
+ conf.blist_device = blist_device_foo_bar;
+ conf.blist_protocol = blist_protocol_fcp;
+ conf.blist_wwid = blist_wwid_xyzzy;
+ assert_int_equal(filter_path(&conf, &miss3_pp),
+ MATCH_NOTHING);
+}
+
static void test_filter_path_whitelist(void **state)
{
conf.elist_property = blist_property_wwn;
test_and_reset(test_filter_path_wwid),
test_and_reset(test_filter_path_missing1),
test_and_reset(test_filter_path_missing2),
+ test_and_reset(test_filter_path_missing3),
test_and_reset(test_filter_path_whitelist),
test_and_reset(test_filter_path_whitelist_property),
test_and_reset(test_filter_path_whitelist_devnode),
/* Required globals */
struct udev *udev;
-int logsink = 0;
+int logsink = -1;
struct config conf = {
- .uid_attrs = "sd:ID_BOGUS",
.verbosity = 4,
};
static struct config *_conf;
struct udev *udev;
-int logsink;
+int logsink = -1;
struct config *get_multipath_config(void)
{
vector hwtable;
struct config *conf;
- condlog(1, "--- %s: replicating %s configuration", __func__,
+ condlog(3, "--- %s: replicating %s configuration", __func__,
local ? "local" : "full");
conf = get_multipath_config();
mp = mock_multipath(pp);
assert_ptr_not_equal(mp, NULL);
TEST_PROP(checker_name(&pp->checker), NONE);
- TEST_PROP(pp->uid_attribute, "ID_WWN");
+ TEST_PROP(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE);
assert_int_equal(mp->pgpolicy, DEFAULT_PGPOLICY);
assert_int_equal(mp->no_path_retry, DEFAULT_NO_PATH_RETRY);
assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF);
static int setup_uev(void **state)
{
+ static char test_uid_attrs[] =
+ "dasd:ID_SPAM sd:ID_BOGUS nvme:ID_EGGS ";
+
struct uevent *uev = alloc_uevent();
+ struct config *conf;
if (uev == NULL)
return -1;
*state = uev;
uev->kernel = "sdo";
uev->envp[0] = "MAJOR=" str(MAJOR);
+ uev->envp[1] = "ID_SPAM=nonsense";
uev->envp[1] = "ID_BOGUS=" WWID;
uev->envp[2] = "MINOR=" str(MINOR);
uev->envp[3] = "DM_NAME=" DM_NAME;
uev->envp[4] = "DISK_RO=" str(DISK_RO);
uev->envp[5] = NULL;
+
+ conf = get_multipath_config();
+ parse_uid_attrs(test_uid_attrs, conf);
+ put_multipath_config(conf);
return 0;
}
assert_int_equal(uevent_get_disk_ro(uev), DISK_RO);
}
+static void test_uid_attrs(void **state)
+{
+ /* see test_uid_attrs above */
+ struct config *conf = get_multipath_config();
+ vector attrs = &conf->uid_attrs;
+
+ assert_int_equal(VECTOR_SIZE(attrs), 3);
+ assert_null(get_uid_attribute_by_attrs(conf, "hda"));
+ assert_string_equal("ID_BOGUS",
+ get_uid_attribute_by_attrs(conf, "sdaw"));
+ assert_string_equal("ID_SPAM",
+ get_uid_attribute_by_attrs(conf, "dasdu"));
+ assert_string_equal("ID_EGGS",
+ get_uid_attribute_by_attrs(conf, "nvme2n4"));
+ put_multipath_config(conf);
+}
+
static void test_wwid(void **state)
{
struct uevent *uev = *state;
cmocka_unit_test(test_minor_good),
cmocka_unit_test(test_ro_good),
cmocka_unit_test(test_dm_name_good),
+ cmocka_unit_test(test_uid_attrs),
cmocka_unit_test(test_wwid),
cmocka_unit_test(test_major_bad_0),
cmocka_unit_test(test_major_bad_1),
return cmocka_run_group_tests(tests, NULL, NULL);
}
+static const char src_str[] = "Hello";
+
+/* strlcpy with length 0 */
+static void test_strlcpy_0(void **state)
+{
+ char tst[] = "word";
+ int rc;
+
+ rc = strlcpy(tst, src_str, 0);
+ assert_int_equal(rc, strlen(src_str));
+ assert_string_equal(tst, "word");
+}
+
+/* strlcpy with length 1 */
+static void test_strlcpy_1(void **state)
+{
+ char tst[] = "word";
+ int rc;
+
+ rc = strlcpy(tst, src_str, 1);
+ assert_int_equal(rc, strlen(src_str));
+ assert_int_equal(tst[0], '\0');
+ assert_string_equal(tst + 1, "ord");
+}
+
+/* strlcpy with length 2 */
+static void test_strlcpy_2(void **state)
+{
+ char tst[] = "word";
+ int rc;
+
+ rc = strlcpy(tst, src_str, 2);
+ assert_int_equal(rc, strlen(src_str));
+ assert_int_equal(tst[0], src_str[0]);
+ assert_int_equal(tst[1], '\0');
+ assert_string_equal(tst + 2, "rd");
+}
+
+/* strlcpy with dst length < src length */
+static void test_strlcpy_3(void **state)
+{
+ char tst[] = "word";
+ int rc;
+
+ rc = strlcpy(tst, src_str, sizeof(tst));
+ assert_int_equal(rc, strlen(src_str));
+ assert_int_equal(sizeof(tst) - 1, strlen(tst));
+ assert_true(strncmp(tst, src_str, sizeof(tst) - 1) == 0);
+}
+
+/* strlcpy with dst length > src length */
+static void test_strlcpy_4(void **state)
+{
+ static const char old[] = "0123456789";
+ char *tst;
+ int rc;
+
+ tst = strdup(old);
+ rc = strlcpy(tst, src_str, sizeof(old));
+ assert_int_equal(rc, strlen(src_str));
+ assert_string_equal(src_str, tst);
+ assert_string_equal(tst + sizeof(src_str), old + sizeof(src_str));
+ free(tst);
+}
+
+/* strlcpy with dst length = src length, dst not terminated */
+static void test_strlcpy_5(void **state)
+{
+ char *tst;
+ int rc;
+
+ tst = malloc(sizeof(src_str));
+ memset(tst, 'f', sizeof(src_str));
+
+ rc = strlcpy(tst, src_str, sizeof(src_str));
+ assert_int_equal(rc, strlen(src_str));
+ assert_string_equal(src_str, tst);
+
+ free(tst);
+}
+
+/* strlcpy with dst length > src length, dst not terminated */
+static void test_strlcpy_6(void **state)
+{
+ char *tst;
+ int rc;
+
+ tst = malloc(sizeof(src_str) + 2);
+ memset(tst, 'f', sizeof(src_str) + 2);
+
+ rc = strlcpy(tst, src_str, sizeof(src_str) + 2);
+ assert_int_equal(rc, strlen(src_str));
+ assert_string_equal(src_str, tst);
+ assert_int_equal(tst[sizeof(src_str)], 'f');
+ assert_int_equal(tst[sizeof(src_str) + 1], 'f');
+
+ free(tst);
+}
+
+/* strlcpy with empty src */
+static void test_strlcpy_7(void **state)
+{
+ char tst[] = "word";
+ static const char empty[] = "";
+ int rc;
+
+ rc = strlcpy(tst, empty, sizeof(tst));
+ assert_int_equal(rc, strlen(empty));
+ assert_string_equal(empty, tst);
+ assert_string_equal(tst + 1, "ord");
+}
+
+/* strlcpy with empty src, length 0 */
+static void test_strlcpy_8(void **state)
+{
+ char tst[] = "word";
+ static const char empty[] = "";
+ int rc;
+
+ rc = strlcpy(tst, empty, 0);
+ assert_int_equal(rc, strlen(empty));
+ assert_string_equal("word", tst);
+}
+
+static int test_strlcpy(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_strlcpy_0),
+ cmocka_unit_test(test_strlcpy_1),
+ cmocka_unit_test(test_strlcpy_2),
+ cmocka_unit_test(test_strlcpy_3),
+ cmocka_unit_test(test_strlcpy_4),
+ cmocka_unit_test(test_strlcpy_5),
+ cmocka_unit_test(test_strlcpy_6),
+ cmocka_unit_test(test_strlcpy_7),
+ cmocka_unit_test(test_strlcpy_8),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
int main(void)
{
int ret = 0;
ret += test_basenamecpy();
+ ret += test_strlcpy();
return ret;
}
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (c) 2019 Martin Wilck, SUSE Linux GmbH, Nuremberg */
+
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <regex.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <scsi/sg.h>
+#include "unaligned.h"
+#include "debug.h"
+#include "vector.h"
+#include "structs.h"
+#include "discovery.h"
+#include "globals.c"
+
+#define VPD_BUFSIZ 4096
+
+struct vpdtest {
+ unsigned char vpdbuf[VPD_BUFSIZ];
+ char wwid[WWID_SIZE];
+};
+
+static int setup(void **state)
+{
+ struct vpdtest *vt = malloc(sizeof(*vt));
+
+ if (vt == NULL)
+ return -1;
+ *state = vt;
+ return 0;
+}
+
+static int teardown(void **state)
+{
+ struct vpdtest *vt = *state;
+
+ free(vt);
+ *state = NULL;
+ return 0;
+}
+
+/* vendor_id should have less than 8 chars to test space handling */
+static const char vendor_id[] = "Linux";
+static const char test_id[] =
+ "A123456789AbcDefB123456789AbcDefC123456789AbcDefD123456789AbcDef";
+
+int __wrap_ioctl(int fd, unsigned long request, void *param)
+{
+ int len;
+ struct sg_io_hdr *io_hdr;
+ unsigned char *val;
+
+ len = mock();
+ io_hdr = (struct sg_io_hdr *)param;
+ assert_in_range(len, 0, io_hdr->dxfer_len);
+ val = mock_ptr_type(unsigned char *);
+ io_hdr->status = 0;
+ memcpy(io_hdr->dxferp, val, len);
+ return 0;
+}
+
+
+/**
+ * create_vpd80() - create a "unit serial number" VPD page.
+ * @buf: VPD buffer
+ * @bufsiz: length of VPD buffer
+ * @id: input ID
+ * @size: value for the "page length" field
+ * @len: actual number of characters to use from @id
+ *
+ * If len < size, the content will be right aligned, as mandated by the
+ * SPC spec.
+ *
+ * Return: VPD length.
+ */
+static int create_vpd80(unsigned char *buf, size_t bufsiz, const char *id,
+ int size, int len)
+{
+ assert_true(len <= size);
+
+ memset(buf, 0, bufsiz);
+ buf[1] = 0x80;
+ put_unaligned_be16(size, buf + 2);
+
+ memset(buf + 4, ' ', size - len);
+ memcpy(buf + 4 + size - len, id, len);
+ return size + 4;
+}
+
+static int _hex2bin(const char hx)
+{
+ assert_true(isxdigit(hx));
+ if (hx >= '0' && hx <= '9')
+ return hx - '0';
+ if (hx >= 'a' && hx <= 'f')
+ return hx - 'a' + 10;
+ if (hx >= 'A' && hx <= 'F')
+ return hx - 'A' + 10;
+ return -1;
+}
+
+static void hex2bin(unsigned char *dst, const char *src,
+ size_t dstlen, size_t srclen)
+{
+ const char *sc;
+ unsigned char *ds;
+
+ assert(srclen % 2 == 0);
+ for (sc = src, ds = dst;
+ sc < src + srclen && ds < dst + dstlen;
+ sc += 2, ++ds)
+ *ds = 16 * _hex2bin(sc[0]) + _hex2bin(sc[1]);
+}
+
+/**
+ * create_t10_vendor_id_desc() - Create T10 vendor ID
+ * @desc: descriptor buffer
+ * @id: input ID
+ * @n: number of characters to use for ID
+ * (includes 8 bytes for vendor ID!)
+ *
+ * Create a "T10 vendor specific ID" designation descriptor.
+ * The vendor field (8 bytes) is filled with vendor_id (above).
+ *
+ * Return: descriptor length.
+ */
+static int create_t10_vendor_id_desc(unsigned char *desc,
+ const char *id, size_t n)
+{
+ int vnd_len = sizeof(vendor_id) - 1;
+
+ /* code set: ascii */
+ desc[0] = 2;
+ /* type: 10 vendor ID */
+ desc[1] = 1;
+ desc[2] = 0;
+ desc[3] = n;
+
+ memcpy(desc + 4, (const unsigned char *)vendor_id, vnd_len);
+ memset(desc + 4 + vnd_len, ' ', 8 - vnd_len);
+ memcpy(desc + 4 + 8, (const unsigned char *)id, n - 8);
+
+ return n + 4;
+}
+
+/**
+ * create_eui64_desc() - create EUI64 descriptor.
+ * @desc, @id: see above.
+ * @n: number of bytes (8, 12, or 16).
+ *
+ * Create an EUI64 designation descriptor.
+ *
+ * Return: descriptor length.
+ */
+static int create_eui64_desc(unsigned char *desc,
+ const char *id, size_t n)
+{
+ assert_true(n == 8 || n == 12 || n == 16);
+
+ /* code set: binary */
+ desc[0] = 1;
+ /* type: EUI64 */
+ desc[1] = 2;
+ desc[2] = 0;
+ desc[3] = n;
+
+ hex2bin(desc + 4, id, n, 2 * n);
+ return n + 4;
+}
+
+/**
+ * create_naa_desc() - create an NAA designation descriptor
+ * @desc, @id: see above.
+ * @naa: Name Address Authority field (2, 3, 5, or 6).
+ *
+ * Return: descriptor length.
+ */
+static int create_naa_desc(unsigned char *desc,
+ const char *id, int naa)
+{
+ assert_true(naa == 2 || naa == 3 || naa == 5 || naa == 6);
+
+ /* code set: binary */
+ desc[0] = 1;
+ /* type: NAA */
+ desc[1] = 3;
+ desc[2] = 0;
+ desc[4] = _hex2bin(id[0]) | (naa << 4);
+ switch (naa) {
+ case 2:
+ case 3:
+ case 5:
+ hex2bin(desc + 5, id + 1, 7, 14);
+ desc[3] = 8;
+ return 12;
+ case 6:
+ hex2bin(desc + 5, id + 1, 15, 30);
+ desc[3] = 16;
+ return 20;
+ default:
+ return 0;
+ }
+}
+
+/* type and flags for SCSI name string designation descriptor */
+enum {
+ STR_EUI = 0,
+ STR_NAA,
+ STR_IQN,
+ STR_MASK = 0xf,
+ ZERO_LAST = 0x10, /* flag to zero out some bytes at string end */
+};
+
+static const char * const str_prefix[] = {
+ [STR_EUI] = "eui.",
+ [STR_NAA] = "naa.",
+ [STR_IQN] = "iqn.",
+};
+
+static const char byte0[] = {
+ [STR_EUI] = '2',
+ [STR_NAA] = '3',
+ [STR_IQN] = '8',
+};
+
+/**
+ * create_scsi_string_desc() - create a SCSI name string descriptor.
+ * @desc, @id: see above.
+ * @typ: one of STR_EUI, STR_NAA, STR_IQN, possibly ORd with ZERO_LAST
+ * @maxlen: number of characters to use from input ID.
+ *
+ * If ZERO_LAST is set, zero out the last byte.
+ *
+ * Return: descriptor length.
+ */
+static int create_scsi_string_desc(unsigned char *desc,
+ const char *id, int typ, int maxlen)
+{
+ int len, plen;
+ int type = typ & STR_MASK;
+
+ /* code set: UTF-8 */
+ desc[0] = 3;
+ /* type: SCSI string */
+ desc[1] = 8;
+ desc[2] = 0;
+
+ assert_in_range(type, STR_EUI, STR_IQN);
+ assert_true(maxlen % 4 == 0);
+ len = snprintf((char *)(desc + 4), maxlen, "%s%s",
+ str_prefix[type], id);
+ if (len > maxlen)
+ len = maxlen;
+ /* zero-pad */
+ if (typ & ZERO_LAST)
+ len -= 2;
+ plen = 4 * ((len - 1) / 4) + 4;
+ memset(desc + 4 + len, '\0', plen - len);
+ desc[3] = plen;
+ return plen + 4;
+}
+
+/**
+ * create_vpd83() - create "device identification" VPD page
+ * @buf, @bufsiz, @id: see above.
+ * @type: descriptor type to use (1, 2, 3, 8)
+ * @parm: opaque parameter (e.g. means "naa" for NAA type)
+ * @len: designator length (exact meaning depends on designator type)
+ *
+ * Create a "device identification" VPD page with a single
+ * designation descriptor.
+ *
+ * Return: VPD page length.
+ */
+static int create_vpd83(unsigned char *buf, size_t bufsiz, const char *id,
+ uint8_t type, int parm, int len)
+{
+ unsigned char *desc;
+ int n = 0;
+
+ memset(buf, 0, bufsiz);
+ buf[1] = 0x83;
+
+ desc = buf + 4;
+ switch (type) {
+ case 1:
+ n = create_t10_vendor_id_desc(desc, id, len);
+ break;
+ case 2:
+ n = create_eui64_desc(desc, id, len);
+ break;
+ case 3:
+ n = create_naa_desc(desc, id, parm);
+ break;
+ case 8:
+ n = create_scsi_string_desc(desc, id, parm, len);
+ break;
+ default:
+ break;
+ }
+ put_unaligned_be16(n, buf + 2);
+ return n + 4;
+}
+
+/**
+ * assert_correct_wwid() - test that a retrieved WWID matches expectations
+ * @test: test name
+ * @expected: expected WWID length
+ * @returned: WWID length as returned by code under test
+ * @byte0, @byte1: leading chars that our code prepends to the ID
+ * (e.g. "36" for "NAA registered extended" type)
+ * @lowercase: set if lower case WWID is expected
+ * @orig: original ID string, may be longer than wwid
+ * @wwid: WWID as returned by code under test
+ */
+static void assert_correct_wwid(const char *test,
+ int expected, int returned,
+ int byte0, int byte1, bool lowercase,
+ const char *orig,
+ const char *wwid)
+{
+ int ofs = 0, i;
+
+ condlog(2, "%s: exp/ret: %d/%d, wwid: %s", test,
+ expected, returned, wwid);
+ /*
+ * byte0 and byte1 are the leading chars that our code prepends
+ * to the ID to indicate the designation descriptor type, .
+ */
+ if (byte0 != 0) {
+ assert_int_equal(byte0, wwid[0]);
+ ++ofs;
+ }
+ if (byte1 != 0) {
+ assert_int_equal(byte1, wwid[1]);
+ ++ofs;
+ }
+ /* check matching length, and length of WWID string */
+ assert_int_equal(expected, returned);
+ assert_int_equal(returned, strlen(wwid));
+ /* check expected string value up to expected length */
+ for (i = 0; i < returned - ofs; i++)
+ assert_int_equal(wwid[ofs + i],
+ lowercase ? tolower(orig[i]) : orig[i]);
+}
+
+/*
+ * For T10 vendor ID - replace sequences of spaces with a single underscore.
+ * Use a different implementation then libmultipath, deliberately.
+ */
+static char *subst_spaces(const char *src)
+{
+ char *dst = calloc(1, strlen(src) + 1);
+ char *p;
+ static regex_t *re;
+ regmatch_t match;
+ int rc;
+
+ assert_non_null(dst);
+ if (re == NULL) {
+ re = calloc(1, sizeof(*re));
+ assert_non_null(re);
+ rc = regcomp(re, " +", REG_EXTENDED);
+ assert_int_equal(rc, 0);
+ }
+
+ for (rc = regexec(re, src, 1, &match, 0), p = dst;
+ rc == 0;
+ src += match.rm_eo, rc = regexec(re, src, 1, &match, 0)) {
+ memcpy(p, src, match.rm_so);
+ p += match.rm_so;
+ *p = '_';
+ ++p;
+ }
+ assert_int_equal(rc, REG_NOMATCH);
+ strcpy(p, src);
+ return dst;
+}
+
+/**
+ * test_vpd_vnd_LEN_WLEN() - test code for VPD 83, T10 vendor ID
+ * @LEN: ID length in the VPD page (includes 8 byte vendor ID)
+ * @WLEN: WWID buffer size
+ *
+ * The input ID is modified by inserting some spaces, to be able to
+ * test the handling of spaces by the code. This is relevant only for
+ * a minimum input length of 24.
+ * The expected result must be adjusted accordingly.
+ */
+#define make_test_vpd_vnd(len, wlen) \
+static void test_vpd_vnd_ ## len ## _ ## wlen(void **state) \
+{ \
+ struct vpdtest *vt = *state; \
+ int n, ret, rc; \
+ int exp_len; \
+ char *exp_wwid, *exp_subst, *input; \
+ \
+ input = strdup(test_id); \
+ /* 8 vendor bytes collapsed to actual vendor ID length + 1 */ \
+ /* and one '1' prepended */ \
+ exp_len = len - 8 + sizeof(vendor_id) + 1; \
+ \
+ /* insert some spaces to test space collapsing */ \
+ input[15] = input[17] = input[18] = ' '; \
+ /* adjust expectation for space treatment */ \
+ /* drop char for 2nd space on offset 17/18 */ \
+ if (len >= 18 + 9) \
+ --exp_len; \
+ /* drop trailing single '_' if input ends with space */ \
+ if (len == 15 + 9 || len == 17 + 9 || len == 18 + 9) \
+ --exp_len; \
+ if (exp_len >= wlen) \
+ exp_len = wlen - 1; \
+ n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), input, \
+ 1, 0, len); \
+ rc = asprintf(&exp_wwid, "%s_%s", vendor_id, input); \
+ assert_int_not_equal(rc, -1); \
+ free(input); \
+ /* Replace spaces, like code under test */ \
+ exp_subst = subst_spaces(exp_wwid); \
+ free(exp_wwid); \
+ will_return(__wrap_ioctl, n); \
+ will_return(__wrap_ioctl, vt->vpdbuf); \
+ ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen); \
+ assert_correct_wwid("test_vpd_vnd_" #len "_" #wlen, \
+ exp_len, ret, '1', 0, false, \
+ exp_subst, vt->wwid); \
+ free(exp_subst); \
+}
+
+/**
+ * test_vpd_str_TYP_LEN_WLEN() - test code for VPD 83, SCSI name string
+ * @TYP: numeric value of STR_EUI, STR_NAA, STR_IQN above
+ * @LEN: ID length the VPD page
+ * @WLEN: WWID buffer size
+ */
+#define make_test_vpd_str(typ, len, wlen) \
+static void test_vpd_str_ ## typ ## _ ## len ## _ ## wlen(void **state) \
+{ \
+ struct vpdtest *vt = *state; \
+ int n, ret; \
+ int exp_len; \
+ int type = typ & STR_MASK; \
+ \
+ n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, \
+ 8, typ, len); \
+ exp_len = len - strlen(str_prefix[type]); \
+ if (typ & ZERO_LAST) \
+ exp_len--; \
+ if (exp_len >= wlen) \
+ exp_len = wlen - 1; \
+ will_return(__wrap_ioctl, n); \
+ will_return(__wrap_ioctl, vt->vpdbuf); \
+ ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen); \
+ assert_correct_wwid("test_vpd_str_" #typ "_" #len "_" #wlen, \
+ exp_len, ret, byte0[type], 0, \
+ type != STR_IQN, \
+ test_id, vt->wwid); \
+}
+
+/**
+ * test_vpd_naa_NAA_WLEN() - test code for VPD 83 NAA designation
+ * @NAA: Network Name Authority (2, 3, 5, or 6)
+ * @WLEN: WWID buffer size
+ */
+#define make_test_vpd_naa(naa, wlen) \
+static void test_vpd_naa_ ## naa ## _ ## wlen(void **state) \
+{ \
+ struct vpdtest *vt = *state; \
+ int n, ret; \
+ int len, exp_len; \
+ \
+ switch (naa) { \
+ case 2: \
+ case 3: \
+ case 5: \
+ len = 17; \
+ break; \
+ case 6: \
+ len = 33; \
+ break; \
+ } \
+ /* returned size is always uneven */ \
+ exp_len = wlen > len ? len : \
+ wlen % 2 == 0 ? wlen - 1 : wlen - 2; \
+ \
+ n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, \
+ 3, naa, 0); \
+ will_return(__wrap_ioctl, n); \
+ will_return(__wrap_ioctl, vt->vpdbuf); \
+ ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen); \
+ assert_correct_wwid("test_vpd_naa_" #naa "_" #wlen, \
+ exp_len, ret, '3', '0' + naa, true, \
+ test_id, vt->wwid); \
+}
+
+/**
+ * test_vpd_eui_LEN_WLEN() - test code for VPD 83, EUI64
+ * @LEN: EUI64 length (8, 12, or 16)
+ * @WLEN: WWID buffer size
+ */
+#define make_test_vpd_eui(len, wlen) \
+static void test_vpd_eui_ ## len ## _ ## wlen(void **state) \
+{ \
+ struct vpdtest *vt = *state; \
+ int n, ret; \
+ /* returned size is always uneven */ \
+ int exp_len = wlen > 2 * len + 1 ? 2 * len + 1 : \
+ wlen % 2 == 0 ? wlen - 1 : wlen - 2; \
+ \
+ n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, \
+ 2, 0, len); \
+ will_return(__wrap_ioctl, n); \
+ will_return(__wrap_ioctl, vt->vpdbuf); \
+ ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen); \
+ assert_correct_wwid("test_vpd_eui_" #len "_" #wlen, \
+ exp_len, ret, '2', 0, true, \
+ test_id, vt->wwid); \
+}
+
+/**
+ * test_vpd80_SIZE_LEN_WLEN() - test code for VPD 80
+ * @SIZE, @LEN: see create_vpd80()
+ * @WLEN: WWID buffer size
+ */
+#define make_test_vpd80(size, len, wlen) \
+static void test_vpd80_ ## size ## _ ## len ## _ ## wlen(void **state) \
+{ \
+ struct vpdtest *vt = *state; \
+ int n, ret; \
+ int exp_len = len > 20 ? 20 : len; \
+ char *input = strdup(test_id); \
+ \
+ /* insert trailing whitespace after pos 20 */ \
+ memset(input + 20, ' ', sizeof(test_id) - 20); \
+ if (exp_len >= wlen) \
+ exp_len = wlen - 1; \
+ n = create_vpd80(vt->vpdbuf, sizeof(vt->vpdbuf), input, \
+ size, len); \
+ will_return(__wrap_ioctl, n); \
+ will_return(__wrap_ioctl, vt->vpdbuf); \
+ ret = get_vpd_sgio(10, 0x80, vt->wwid, wlen); \
+ assert_correct_wwid("test_vpd80_" #size "_" #len "_" #wlen, \
+ exp_len, ret, 0, 0, false, \
+ input, vt->wwid); \
+ free(input); \
+}
+
+/* VPD 80 */
+/* Tests without trailing whitespace: 21 WWID bytes required */
+make_test_vpd80(20, 20, 30);
+make_test_vpd80(20, 20, 21);
+make_test_vpd80(20, 20, 20);
+make_test_vpd80(20, 20, 10);
+
+/* Tests with 4 byte trailing whitespace: 21 WWID bytes required */
+make_test_vpd80(24, 24, 30);
+make_test_vpd80(24, 24, 25);
+make_test_vpd80(24, 24, 24);
+make_test_vpd80(24, 24, 21);
+make_test_vpd80(24, 24, 20);
+
+/* Tests with 4 byte leading whitespace: 17 WWID bytes required */
+make_test_vpd80(20, 16, 30);
+make_test_vpd80(20, 16, 17);
+make_test_vpd80(20, 16, 16);
+
+/* Tests with 4 byte leading whitespace: 21 WWID bytes required */
+make_test_vpd80(24, 20, 21);
+make_test_vpd80(24, 20, 20);
+
+/* Tests with leading and trailing whitespace: 21 WWID bytes required */
+make_test_vpd80(30, 24, 30);
+make_test_vpd80(30, 24, 21);
+make_test_vpd80(30, 24, 20);
+
+/* VPD 83, T10 vendor ID */
+make_test_vpd_vnd(40, 40);
+make_test_vpd_vnd(40, 30);
+make_test_vpd_vnd(30, 20);
+make_test_vpd_vnd(29, 30);
+make_test_vpd_vnd(28, 30);
+make_test_vpd_vnd(27, 30); /* space at end */
+make_test_vpd_vnd(26, 30); /* space at end */
+make_test_vpd_vnd(25, 30);
+make_test_vpd_vnd(24, 30); /* space at end */
+make_test_vpd_vnd(23, 30);
+make_test_vpd_vnd(24, 20);
+make_test_vpd_vnd(23, 20);
+make_test_vpd_vnd(22, 20);
+make_test_vpd_vnd(21, 20);
+make_test_vpd_vnd(20, 20);
+make_test_vpd_vnd(19, 20);
+make_test_vpd_vnd(20, 10);
+make_test_vpd_vnd(10, 10);
+
+/* EUI64 tests */
+/* 64bit, WWID size: 18 */
+make_test_vpd_eui(8, 32);
+make_test_vpd_eui(8, 18);
+make_test_vpd_eui(8, 17);
+make_test_vpd_eui(8, 16);
+make_test_vpd_eui(8, 10);
+
+/* 96 bit, WWID size: 26 */
+make_test_vpd_eui(12, 32);
+make_test_vpd_eui(12, 26);
+make_test_vpd_eui(12, 25);
+make_test_vpd_eui(12, 20);
+make_test_vpd_eui(12, 10);
+
+/* 128 bit, WWID size: 34 */
+make_test_vpd_eui(16, 40);
+make_test_vpd_eui(16, 34);
+make_test_vpd_eui(16, 33);
+make_test_vpd_eui(16, 20);
+
+/* NAA IEEE registered extended (36), WWID size: 34 */
+make_test_vpd_naa(6, 40);
+make_test_vpd_naa(6, 34);
+make_test_vpd_naa(6, 33);
+make_test_vpd_naa(6, 32);
+make_test_vpd_naa(6, 20);
+
+/* NAA IEEE registered (35), WWID size: 18 */
+make_test_vpd_naa(5, 20);
+make_test_vpd_naa(5, 18);
+make_test_vpd_naa(5, 17);
+make_test_vpd_naa(5, 16);
+
+/* NAA local (33), WWID size: 18 */
+make_test_vpd_naa(3, 20);
+make_test_vpd_naa(3, 18);
+make_test_vpd_naa(3, 17);
+make_test_vpd_naa(3, 16);
+
+/* NAA IEEE extended (32), WWID size: 18 */
+make_test_vpd_naa(2, 20);
+make_test_vpd_naa(2, 18);
+make_test_vpd_naa(2, 17);
+make_test_vpd_naa(2, 16);
+
+/* SCSI Name string: EUI64, WWID size: 17 */
+make_test_vpd_str(0, 20, 18)
+make_test_vpd_str(0, 20, 17)
+make_test_vpd_str(0, 20, 16)
+make_test_vpd_str(0, 20, 15)
+
+/* SCSI Name string: EUI64, zero padded, WWID size: 16 */
+make_test_vpd_str(16, 20, 18)
+make_test_vpd_str(16, 20, 17)
+make_test_vpd_str(16, 20, 16)
+make_test_vpd_str(16, 20, 15)
+
+/* SCSI Name string: NAA, WWID size: 17 */
+make_test_vpd_str(1, 20, 18)
+make_test_vpd_str(1, 20, 17)
+make_test_vpd_str(1, 20, 16)
+make_test_vpd_str(1, 20, 15)
+
+/* SCSI Name string: NAA, zero padded, WWID size: 16 */
+make_test_vpd_str(17, 20, 18)
+make_test_vpd_str(17, 20, 17)
+make_test_vpd_str(17, 20, 16)
+make_test_vpd_str(17, 20, 15)
+
+/* SCSI Name string: IQN, WWID size: 17 */
+make_test_vpd_str(2, 20, 18)
+make_test_vpd_str(2, 20, 17)
+make_test_vpd_str(2, 20, 16)
+make_test_vpd_str(2, 20, 15)
+
+/* SCSI Name string: IQN, zero padded, WWID size: 16 */
+make_test_vpd_str(18, 20, 18)
+make_test_vpd_str(18, 20, 17)
+make_test_vpd_str(18, 20, 16)
+make_test_vpd_str(18, 20, 15)
+
+static int test_vpd(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_vpd80_20_20_30),
+ cmocka_unit_test(test_vpd80_20_20_21),
+ cmocka_unit_test(test_vpd80_20_20_20),
+ cmocka_unit_test(test_vpd80_20_20_10),
+ cmocka_unit_test(test_vpd80_24_24_30),
+ cmocka_unit_test(test_vpd80_24_24_25),
+ cmocka_unit_test(test_vpd80_24_24_24),
+ cmocka_unit_test(test_vpd80_24_24_21),
+ cmocka_unit_test(test_vpd80_24_24_20),
+ cmocka_unit_test(test_vpd80_20_16_30),
+ cmocka_unit_test(test_vpd80_20_16_17),
+ cmocka_unit_test(test_vpd80_20_16_16),
+ cmocka_unit_test(test_vpd80_24_20_21),
+ cmocka_unit_test(test_vpd80_24_20_20),
+ cmocka_unit_test(test_vpd80_30_24_30),
+ cmocka_unit_test(test_vpd80_30_24_21),
+ cmocka_unit_test(test_vpd80_30_24_20),
+ cmocka_unit_test(test_vpd_vnd_40_40),
+ cmocka_unit_test(test_vpd_vnd_40_30),
+ cmocka_unit_test(test_vpd_vnd_30_20),
+ cmocka_unit_test(test_vpd_vnd_29_30),
+ cmocka_unit_test(test_vpd_vnd_28_30),
+ cmocka_unit_test(test_vpd_vnd_27_30),
+ cmocka_unit_test(test_vpd_vnd_26_30),
+ cmocka_unit_test(test_vpd_vnd_25_30),
+ cmocka_unit_test(test_vpd_vnd_24_30),
+ cmocka_unit_test(test_vpd_vnd_23_30),
+ cmocka_unit_test(test_vpd_vnd_24_20),
+ cmocka_unit_test(test_vpd_vnd_23_20),
+ cmocka_unit_test(test_vpd_vnd_22_20),
+ cmocka_unit_test(test_vpd_vnd_21_20),
+ cmocka_unit_test(test_vpd_vnd_20_20),
+ cmocka_unit_test(test_vpd_vnd_19_20),
+ cmocka_unit_test(test_vpd_vnd_20_10),
+ cmocka_unit_test(test_vpd_vnd_10_10),
+ cmocka_unit_test(test_vpd_eui_8_32),
+ cmocka_unit_test(test_vpd_eui_8_18),
+ cmocka_unit_test(test_vpd_eui_8_17),
+ cmocka_unit_test(test_vpd_eui_8_16),
+ cmocka_unit_test(test_vpd_eui_8_10),
+ cmocka_unit_test(test_vpd_eui_12_32),
+ cmocka_unit_test(test_vpd_eui_12_26),
+ cmocka_unit_test(test_vpd_eui_12_25),
+ cmocka_unit_test(test_vpd_eui_12_20),
+ cmocka_unit_test(test_vpd_eui_12_10),
+ cmocka_unit_test(test_vpd_eui_16_40),
+ cmocka_unit_test(test_vpd_eui_16_34),
+ cmocka_unit_test(test_vpd_eui_16_33),
+ cmocka_unit_test(test_vpd_eui_16_20),
+ cmocka_unit_test(test_vpd_naa_6_40),
+ cmocka_unit_test(test_vpd_naa_6_34),
+ cmocka_unit_test(test_vpd_naa_6_33),
+ cmocka_unit_test(test_vpd_naa_6_32),
+ cmocka_unit_test(test_vpd_naa_6_20),
+ cmocka_unit_test(test_vpd_naa_5_20),
+ cmocka_unit_test(test_vpd_naa_5_18),
+ cmocka_unit_test(test_vpd_naa_5_17),
+ cmocka_unit_test(test_vpd_naa_5_16),
+ cmocka_unit_test(test_vpd_naa_3_20),
+ cmocka_unit_test(test_vpd_naa_3_18),
+ cmocka_unit_test(test_vpd_naa_3_17),
+ cmocka_unit_test(test_vpd_naa_3_16),
+ cmocka_unit_test(test_vpd_naa_2_20),
+ cmocka_unit_test(test_vpd_naa_2_18),
+ cmocka_unit_test(test_vpd_naa_2_17),
+ cmocka_unit_test(test_vpd_naa_2_16),
+ cmocka_unit_test(test_vpd_str_0_20_18),
+ cmocka_unit_test(test_vpd_str_0_20_17),
+ cmocka_unit_test(test_vpd_str_0_20_16),
+ cmocka_unit_test(test_vpd_str_0_20_15),
+ cmocka_unit_test(test_vpd_str_16_20_18),
+ cmocka_unit_test(test_vpd_str_16_20_17),
+ cmocka_unit_test(test_vpd_str_16_20_16),
+ cmocka_unit_test(test_vpd_str_16_20_15),
+ cmocka_unit_test(test_vpd_str_1_20_18),
+ cmocka_unit_test(test_vpd_str_1_20_17),
+ cmocka_unit_test(test_vpd_str_1_20_16),
+ cmocka_unit_test(test_vpd_str_1_20_15),
+ cmocka_unit_test(test_vpd_str_17_20_18),
+ cmocka_unit_test(test_vpd_str_17_20_17),
+ cmocka_unit_test(test_vpd_str_17_20_16),
+ cmocka_unit_test(test_vpd_str_17_20_15),
+ cmocka_unit_test(test_vpd_str_2_20_18),
+ cmocka_unit_test(test_vpd_str_2_20_17),
+ cmocka_unit_test(test_vpd_str_2_20_16),
+ cmocka_unit_test(test_vpd_str_2_20_15),
+ cmocka_unit_test(test_vpd_str_18_20_18),
+ cmocka_unit_test(test_vpd_str_18_20_17),
+ cmocka_unit_test(test_vpd_str_18_20_16),
+ cmocka_unit_test(test_vpd_str_18_20_15),
+ };
+ return cmocka_run_group_tests(tests, setup, teardown);
+}
+
+int main(void)
+{
+ int ret = 0;
+
+ ret += test_vpd();
+ return ret;
+}