/*
* Copyright (c) 2005 Christophe Varoqui
*/
#define _GNU_SOURCE
#include "checkers.h"
#include "memory.h"
#include "vector.h"
#include "structs.h"
#include "structs_vec.h"
#include <libdevmapper.h>
#include "devmapper.h"
#include "discovery.h"
#include "config.h"
#include "configure.h"
#include "blacklist.h"
#include "debug.h"
#include "dm-generic.h"
#include "print.h"
#include "sysfs.h"
#include <errno.h>
#include <libudev.h>
#include <mpath_persist.h>
#include "util.h"
#include "prkey.h"
#include "propsel.h"
#include "main.h"
#include "mpath_cmd.h"
#include "cli.h"
#include "uevent.h"
#include "foreign.h"
#include "cli_handlers.h"
int
show_paths (char ** r, int * len, struct vectors * vecs, char * style,
int pretty)
{
int i;
struct path * pp;
char * c;
char * reply, * header;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
get_path_layout(vecs->pathvec, 1);
foreign_path_layout();
reply = MALLOC(maxlen);
while (again) {
if (!reply)
return 1;
c = reply;
if (pretty)
c += snprint_path_header(c, reply + maxlen - c,
style);
header = c;
vector_foreach_slot(vecs->pathvec, pp, i)
c += snprint_path(c, reply + maxlen - c,
style, pp, pretty);
c += snprint_foreign_paths(c, reply + maxlen - c,
style, pretty);
again = ((c - reply) == (maxlen - 1));
REALLOC_REPLY(reply, again, maxlen);
}
if (pretty && c == header) {
/* No output - clear header */
*reply = '\0';
c = reply;
}
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
char * style)
{
char * c;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
get_path_layout(vecs->pathvec, 1);
reply = MALLOC(maxlen);
while (again) {
if (!reply)
return 1;
c = reply;
c += snprint_path(c, reply + maxlen - c, style, pp, 0);
again = ((c - reply) == (maxlen - 1));
REALLOC_REPLY(reply, again, maxlen);
}
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
show_map_topology (char ** r, int * len, struct multipath * mpp,
struct vectors * vecs)
{
char * c;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
if (update_multipath(vecs, mpp->alias, 0))
return 1;
reply = MALLOC(maxlen);
while (again) {
if (!reply)
return 1;
c = reply;
c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2);
again = ((c - reply) == (maxlen - 1));
REALLOC_REPLY(reply, again, maxlen);
}
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
show_maps_topology (char ** r, int * len, struct vectors * vecs)
{
int i;
struct multipath * mpp;
char * c;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
get_path_layout(vecs->pathvec, 0);
foreign_path_layout();
reply = MALLOC(maxlen);
while (again) {
if (!reply)
return 1;
c = reply;
vector_foreach_slot(vecs->mpvec, mpp, i) {
if (update_multipath(vecs, mpp->alias, 0)) {
i--;
continue;
}
c += snprint_multipath_topology(c, reply + maxlen - c,
mpp, 2);
}
c += snprint_foreign_topology(c, reply + maxlen - c, 2);
again = ((c - reply) == (maxlen - 1));
REALLOC_REPLY(reply, again, maxlen);
}
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
show_maps_json (char ** r, int * len, struct vectors * vecs)
{
int i;
struct multipath * mpp;
char * c;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
if (VECTOR_SIZE(vecs->mpvec) > 0)
maxlen *= PRINT_JSON_MULTIPLIER * VECTOR_SIZE(vecs->mpvec);
vector_foreach_slot(vecs->mpvec, mpp, i) {
if (update_multipath(vecs, mpp->alias, 0)) {
return 1;
}
}
reply = MALLOC(maxlen);
while (again) {
if (!reply)
return 1;
c = reply;
c += snprint_multipath_topology_json(c, maxlen, vecs);
again = ((c - reply) == maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
*r = reply;
*len = (int)(c - reply);
return 0;
}
int
show_map_json (char ** r, int * len, struct multipath * mpp,
struct vectors * vecs)
{
char * c;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
if (update_multipath(vecs, mpp->alias, 0))
return 1;
reply = MALLOC(maxlen);
while (again) {
if (!reply)
return 1;
c = reply;
c += snprint_multipath_map_json(c, maxlen, mpp);
again = ((c - reply) == maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
*r = reply;
*len = (int)(c - reply);
return 0;
}
static int
show_config (char ** r, int * len, const struct _vector *hwtable,
const struct _vector *mpvec)
{
struct config *conf;
char *reply;
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
reply = snprint_config(conf, len, hwtable, mpvec);
pthread_cleanup_pop(1);
if (reply == NULL)
return 1;
*r = reply;
return 0;
}
void
reset_stats(struct multipath * mpp)
{
mpp->stat_switchgroup = 0;
mpp->stat_path_failures = 0;
mpp->stat_map_loads = 0;
mpp->stat_total_queueing_time = 0;
mpp->stat_queueing_timeouts = 0;
mpp->stat_map_failures = 0;
}
int
cli_list_config (void * v, char ** reply, int * len, void * data)
{
condlog(3, "list config (operator)");
return show_config(reply, len, NULL, NULL);
}
static void v_free(void *x)
{
vector_free(x);
}
int
cli_list_config_local (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
vector hwes;
int ret;
condlog(3, "list config local (operator)");
hwes = get_used_hwes(vecs->pathvec);
pthread_cleanup_push(v_free, hwes);
ret = show_config(reply, len, hwes, vecs->mpvec);
pthread_cleanup_pop(1);
return ret;
}
int
cli_list_paths (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
condlog(3, "list paths (operator)");
return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1);
}
int
cli_list_paths_fmt (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * fmt = get_keyparam(v, FMT);
condlog(3, "list paths (operator)");
return show_paths(reply, len, vecs, fmt, 1);
}
int
cli_list_paths_raw (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * fmt = get_keyparam(v, FMT);
condlog(3, "list paths (operator)");
return show_paths(reply, len, vecs, fmt, 0);
}
int
cli_list_path (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, PATH);
struct path *pp;
param = convert_dev(param, 1);
condlog(3, "%s: list path (operator)", param);
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp)
return 1;
return show_path(reply, len, vecs, pp, "%o");
}
int
cli_list_map_topology (void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
param = convert_dev(param, 0);
get_path_layout(vecs->pathvec, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
if (!mpp)
return 1;
condlog(3, "list multipath %s (operator)", param);
return show_map_topology(reply, len, mpp, vecs);
}
int
cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
condlog(3, "list multipaths (operator)");
return show_maps_topology(reply, len, vecs);
}
int
cli_list_map_json (void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
param = convert_dev(param, 0);
get_path_layout(vecs->pathvec, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
if (!mpp)
return 1;
condlog(3, "list multipath json %s (operator)", param);
return show_map_json(reply, len, mpp, vecs);
}
int
cli_list_maps_json (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
condlog(3, "list multipaths json (operator)");
return show_maps_json(reply, len, vecs);
}
int
cli_list_wildcards (void * v, char ** reply, int * len, void * data)
{
char * c;
*reply = MALLOC(INITIAL_REPLY_LEN);
if (!*reply)
return 1;
c = *reply;
c += snprint_wildcards(c, INITIAL_REPLY_LEN);
*len = INITIAL_REPLY_LEN;
return 0;
}
int
show_status (char ** r, int *len, struct vectors * vecs)
{
char * c;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
reply = MALLOC(maxlen);
if (!reply)
return 1;
c = reply;
c += snprint_status(c, reply + maxlen - c, vecs);
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
show_daemon (char ** r, int *len)
{
char * c;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
reply = MALLOC(maxlen);
if (!reply)
return 1;
c = reply;
c += snprintf(c, INITIAL_REPLY_LEN, "pid %d %s\n",
daemon_pid, daemon_status());
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
show_map (char ** r, int *len, struct multipath * mpp, char * style,
int pretty)
{
char * c;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
reply = MALLOC(maxlen);
while (again) {
if (!reply)
return 1;
c = reply;
c += snprint_multipath(c, reply + maxlen - c, style,
mpp, pretty);
again = ((c - reply) == (maxlen - 1));
REALLOC_REPLY(reply, again, maxlen);
}
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
show_maps (char ** r, int *len, struct vectors * vecs, char * style,
int pretty)
{
int i;
struct multipath * mpp;
char * c, *header;
char * reply;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
get_multipath_layout(vecs->mpvec, 1);
foreign_multipath_layout();
reply = MALLOC(maxlen);
while (again) {
if (!reply)
return 1;
c = reply;
if (pretty)
c += snprint_multipath_header(c, reply + maxlen - c,
style);
header = c;
vector_foreach_slot(vecs->mpvec, mpp, i) {
if (update_multipath(vecs, mpp->alias, 0)) {
i--;
continue;
}
c += snprint_multipath(c, reply + maxlen - c,
style, mpp, pretty);
}
c += snprint_foreign_multipaths(c, reply + maxlen - c,
style, pretty);
again = ((c - reply) == (maxlen - 1));
REALLOC_REPLY(reply, again, maxlen);
}
if (pretty && c == header) {
/* No output - clear header */
*reply = '\0';
c = reply;
}
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
cli_list_maps_fmt (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * fmt = get_keyparam(v, FMT);
condlog(3, "list maps (operator)");
return show_maps(reply, len, vecs, fmt, 1);
}
int
cli_list_maps_raw (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * fmt = get_keyparam(v, FMT);
condlog(3, "list maps (operator)");
return show_maps(reply, len, vecs, fmt, 0);
}
int
cli_list_map_fmt (void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
char * fmt = get_keyparam(v, FMT);
param = convert_dev(param, 0);
get_path_layout(vecs->pathvec, 0);
get_multipath_layout(vecs->mpvec, 1);
mpp = find_mp_by_str(vecs->mpvec, param);
if (!mpp)
return 1;
condlog(3, "list map %s fmt %s (operator)", param, fmt);
return show_map(reply, len, mpp, fmt, 1);
}
int
cli_list_map_raw (void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
char * fmt = get_keyparam(v, FMT);
param = convert_dev(param, 0);
get_path_layout(vecs->pathvec, 0);
get_multipath_layout(vecs->mpvec, 1);
mpp = find_mp_by_str(vecs->mpvec, param);
if (!mpp)
return 1;
condlog(3, "list map %s fmt %s (operator)", param, fmt);
return show_map(reply, len, mpp, fmt, 0);
}
int
cli_list_maps (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
condlog(3, "list maps (operator)");
return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1);
}
int
cli_list_status (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
condlog(3, "list status (operator)");
return show_status(reply, len, vecs);
}
int
cli_list_maps_status (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
condlog(3, "list maps status (operator)");
return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1);
}
int
cli_list_maps_stats (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
condlog(3, "list maps stats (operator)");
return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1);
}
int
cli_list_daemon (void * v, char ** reply, int * len, void * data)
{
condlog(3, "list daemon (operator)");
return show_daemon(reply, len);
}
int
cli_reset_maps_stats (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
int i;
struct multipath * mpp;
condlog(3, "reset multipaths stats (operator)");
vector_foreach_slot(vecs->mpvec, mpp, i) {
reset_stats(mpp);
}
return 0;
}
int
cli_reset_map_stats (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
struct multipath * mpp;
char * param = get_keyparam(v, MAP);
param = convert_dev(param, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
if (!mpp)
return 1;
condlog(3, "reset multipath %s stats (operator)", param);
reset_stats(mpp);
return 0;
}
int
cli_add_path (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, PATH);
struct path *pp;
int r;
struct config *conf;
int invalid = 0;
param = convert_dev(param, 1);
condlog(2, "%s: add path (operator)", param);
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
param) > 0)
invalid = 1;
pthread_cleanup_pop(1);
if (invalid)
goto blacklisted;
pp = find_path_by_dev(vecs->pathvec, param);
if (pp) {
condlog(2, "%s: path already in pathvec", param);
if (pp->mpp)
return 0;
} else {
struct udev_device *udevice;
udevice = udev_device_new_from_subsystem_sysname(udev,
"block",
param);
if (!udevice) {
condlog(0, "%s: can't find path", param);
return 1;
}
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
r = store_pathinfo(vecs->pathvec, conf,
udevice, DI_ALL | DI_BLACKLIST, &pp);
pthread_cleanup_pop(1);
udev_device_unref(udevice);
if (!pp) {
if (r == 2)
goto blacklisted;
condlog(0, "%s: failed to store path info", param);
return 1;
}
}
return ev_add_path(pp, vecs, 1);
blacklisted:
*reply = strdup("blacklisted\n");
*len = strlen(*reply) + 1;
condlog(2, "%s: path blacklisted", param);
return 0;
}
int
cli_del_path (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, PATH);
struct path *pp;
param = convert_dev(param, 1);
condlog(2, "%s: remove path (operator)", param);
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp) {
condlog(0, "%s: path already removed", param);
return 1;
}
return ev_remove_path(pp, vecs, 1);
}
int
cli_add_map (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
int major, minor;
char dev_path[PATH_SIZE];
char *refwwid, *alias = NULL;
int rc, count = 0;
struct config *conf;
int invalid = 0;
param = convert_dev(param, 0);
condlog(2, "%s: add map (operator)", param);
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0)
invalid = 1;
pthread_cleanup_pop(1);
if (invalid) {
*reply = strdup("blacklisted\n");
*len = strlen(*reply) + 1;
condlog(2, "%s: map blacklisted", param);
return 1;
}
do {
if (dm_get_major_minor(param, &major, &minor) < 0)
condlog(2, "%s: not a device mapper table", param);
else {
sprintf(dev_path, "dm-%d", minor);
alias = dm_mapname(major, minor);
}
/*if there is no mapname found, we first create the device*/
if (!alias && !count) {
condlog(2, "%s: mapname not found for %d:%d",
param, major, minor);
get_refwwid(CMD_NONE, param, DEV_DEVMAP,
vecs->pathvec, &refwwid);
if (refwwid) {
if (coalesce_paths(vecs, NULL, refwwid,
FORCE_RELOAD_NONE, CMD_NONE)
!= CP_OK)
condlog(2, "%s: coalesce_paths failed",
param);
dm_lib_release();
FREE(refwwid);
}
} /*we attempt to create device only once*/
count++;
} while (!alias && (count < 2));
if (!alias) {
condlog(2, "%s: add map failed", param);
return 1;
}
rc = ev_add_map(dev_path, alias, vecs);
FREE(alias);
return rc;
}
int
cli_del_map (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
int major, minor;
char *alias;
int rc;
param = convert_dev(param, 0);
condlog(2, "%s: remove map (operator)", param);
if (dm_get_major_minor(param, &major, &minor) < 0) {
condlog(2, "%s: not a device mapper table", param);
return 1;
}
alias = dm_mapname(major, minor);
if (!alias) {
condlog(2, "%s: mapname not found for %d:%d",
param, major, minor);
return 1;
}
rc = ev_remove_map(param, alias, minor, vecs);
FREE(alias);
return rc;
}
int
cli_reload(void *v, char **reply, int *len, void *data)
{
struct vectors * vecs = (struct vectors *)data;
char * mapname = get_keyparam(v, MAP);
struct multipath *mpp;
int minor;
mapname = convert_dev(mapname, 0);
condlog(2, "%s: reload map (operator)", mapname);
if (sscanf(mapname, "dm-%d", &minor) == 1)
mpp = find_mp_by_minor(vecs->mpvec, minor);
else
mpp = find_mp_by_alias(vecs->mpvec, mapname);
if (!mpp) {
condlog(0, "%s: invalid map name. cannot reload", mapname);
return 1;
}
if (mpp->wait_for_udev) {
condlog(2, "%s: device not fully created, failing reload",
mpp->alias);
return 1;
}
return update_path_groups(mpp, vecs, 0);
}
int resize_map(struct multipath *mpp, unsigned long long size,
struct vectors * vecs)
{
char params[PARAMS_SIZE] = {0};
unsigned long long orig_size = mpp->size;
mpp->size = size;
update_mpp_paths(mpp, vecs->pathvec);
if (setup_map(mpp, params, PARAMS_SIZE, vecs) != 0) {
condlog(0, "%s: failed to setup map for resize : %s",
mpp->alias, strerror(errno));
mpp->size = orig_size;
return 1;
}
mpp->action = ACT_RESIZE;
mpp->force_udev_reload = 1;
if (domap(mpp, params, 1) == DOMAP_FAIL) {
condlog(0, "%s: failed to resize map : %s", mpp->alias,
strerror(errno));
mpp->size = orig_size;
return 1;
}
return 0;
}
int
cli_resize(void *v, char **reply, int *len, void *data)
{
struct vectors * vecs = (struct vectors *)data;
char * mapname = get_keyparam(v, MAP);
struct multipath *mpp;
int minor;
unsigned long long size;
struct pathgroup *pgp;
struct path *pp;
mapname = convert_dev(mapname, 0);
condlog(2, "%s: resize map (operator)", mapname);
if (sscanf(mapname, "dm-%d", &minor) == 1)
mpp = find_mp_by_minor(vecs->mpvec, minor);
else
mpp = find_mp_by_alias(vecs->mpvec, mapname);
if (!mpp) {
condlog(0, "%s: invalid map name. cannot resize", mapname);
return 1;
}
if (mpp->wait_for_udev) {
condlog(2, "%s: device not fully created, failing resize",
mpp->alias);
return 1;
}
pgp = VECTOR_SLOT(mpp->pg, 0);
if (!pgp){
condlog(0, "%s: couldn't get path group. cannot resize",
mapname);
return 1;
}
pp = VECTOR_SLOT(pgp->paths, 0);
if (!pp){
condlog(0, "%s: couldn't get path. cannot resize", mapname);
return 1;
}
if (!pp->udev || sysfs_get_size(pp, &size)) {
condlog(0, "%s: couldn't get size for sysfs. cannot resize",
mapname);
return 1;
}
if (size == mpp->size) {
condlog(0, "%s: map is still the same size (%llu)", mapname,
mpp->size);
return 0;
}
condlog(3, "%s old size is %llu, new size is %llu", mapname, mpp->size,
size);
if (resize_map(mpp, size, vecs) != 0)
return 1;
dm_lib_release();
if (setup_multipath(vecs, mpp) != 0)
return 1;
sync_map_state(mpp);
return 0;
}
int
cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
{
struct config *conf;
condlog(2, "force queue_without_daemon (operator)");
conf = get_multipath_config();
if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF)
conf->queue_without_daemon = QUE_NO_DAEMON_FORCE;
put_multipath_config(conf);
return 0;
}
int
cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
{
struct config *conf;
condlog(2, "restore queue_without_daemon (operator)");
conf = get_multipath_config();
if (conf->queue_without_daemon == QUE_NO_DAEMON_FORCE)
conf->queue_without_daemon = QUE_NO_DAEMON_OFF;
put_multipath_config(conf);
return 0;
}
int
cli_restore_queueing(void *v, char **reply, int *len, void *data)
{
struct vectors * vecs = (struct vectors *)data;
char * mapname = get_keyparam(v, MAP);
struct multipath *mpp;
int minor;
struct config *conf;
mapname = convert_dev(mapname, 0);
condlog(2, "%s: restore map queueing (operator)", mapname);
if (sscanf(mapname, "dm-%d", &minor) == 1)
mpp = find_mp_by_minor(vecs->mpvec, minor);
else
mpp = find_mp_by_alias(vecs->mpvec, mapname);
if (!mpp) {
condlog(0, "%s: invalid map name, cannot restore queueing", mapname);
return 1;
}
mpp->disable_queueing = 0;
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
select_no_path_retry(conf, mpp);
pthread_cleanup_pop(1);
/*
* Don't call set_no_path_retry() for the NO_PATH_RETRY_FAIL case.
* That would disable queueing when "restorequeueing" is called,
* and the code never behaved that way. Users might not expect it.
* In almost all cases, queueing will be disabled anyway when we
* are here.
*/
if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
mpp->no_path_retry != NO_PATH_RETRY_FAIL)
set_no_path_retry(mpp);
return 0;
}
int
cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
{
struct vectors * vecs = (struct vectors *)data;
struct multipath *mpp;
int i;
condlog(2, "restore queueing (operator)");
vector_foreach_slot(vecs->mpvec, mpp, i) {
mpp->disable_queueing = 0;
struct config *conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
select_no_path_retry(conf, mpp);
pthread_cleanup_pop(1);
/* See comment in cli_restore_queueing() */
if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
mpp->no_path_retry != NO_PATH_RETRY_FAIL)
set_no_path_retry(mpp);
}
return 0;
}
int
cli_disable_queueing(void *v, char **reply, int *len, void *data)
{
struct vectors * vecs = (struct vectors *)data;
char * mapname = get_keyparam(v, MAP);
struct multipath *mpp;
int minor;
mapname = convert_dev(mapname, 0);
condlog(2, "%s: disable map queueing (operator)", mapname);
if (sscanf(mapname, "dm-%d", &minor) == 1)
mpp = find_mp_by_minor(vecs->mpvec, minor);
else
mpp = find_mp_by_alias(vecs->mpvec, mapname);
if (!mpp) {
condlog(0, "%s: invalid map name, cannot disable queueing", mapname);
return 1;
}
if (count_active_paths(mpp) == 0)
mpp->stat_map_failures++;
mpp->retry_tick = 0;
mpp->no_path_retry = NO_PATH_RETRY_FAIL;
mpp->disable_queueing = 1;
set_no_path_retry(mpp);
return 0;
}
int
cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
{
struct vectors * vecs = (struct vectors *)data;
struct multipath *mpp;
int i;
condlog(2, "disable queueing (operator)");
vector_foreach_slot(vecs->mpvec, mpp, i) {
if (count_active_paths(mpp) == 0)
mpp->stat_map_failures++;
mpp->retry_tick = 0;
mpp->no_path_retry = NO_PATH_RETRY_FAIL;
mpp->disable_queueing = 1;
set_no_path_retry(mpp);
}
return 0;
}
int
cli_switch_group(void * v, char ** reply, int * len, void * data)
{
char * mapname = get_keyparam(v, MAP);
int groupnum = atoi(get_keyparam(v, GROUP));
mapname = convert_dev(mapname, 0);
condlog(2, "%s: switch to path group #%i (operator)", mapname, groupnum);
return dm_switchgroup(mapname, groupnum);
}
int
cli_reconfigure(void * v, char ** reply, int * len, void * data)
{
int rc;
condlog(2, "reconfigure (operator)");
rc = set_config_state(DAEMON_CONFIGURE);
if (rc == ETIMEDOUT) {
condlog(2, "timeout starting reconfiguration");
return 1;
} else if (rc == EINVAL)
/* daemon shutting down */
return 1;
return 0;
}
int
cli_suspend(void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
int r;
struct multipath * mpp;
param = convert_dev(param, 0);
mpp = find_mp_by_alias(vecs->mpvec, param);
if (!mpp)
return 1;
if (mpp->wait_for_udev) {
condlog(2, "%s: device not fully created, failing suspend",
mpp->alias);
return 1;
}
r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param, 0);
condlog(2, "%s: suspend (operator)", param);
if (!r) /* error */
return 1;
dm_get_info(param, &mpp->dmi);
return 0;
}
int
cli_resume(void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
int r;
struct multipath * mpp;
uint16_t udev_flags;
param = convert_dev(param, 0);
mpp = find_mp_by_alias(vecs->mpvec, param);
if (!mpp)
return 1;
udev_flags = (mpp->skip_kpartx)? MPATH_UDEV_NO_KPARTX_FLAG : 0;
if (mpp->wait_for_udev) {
condlog(2, "%s: device not fully created, failing resume",
mpp->alias);
return 1;
}
r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param, udev_flags);
condlog(2, "%s: resume (operator)", param);
if (!r) /* error */
return 1;
dm_get_info(param, &mpp->dmi);
return 0;
}
int
cli_reinstate(void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, PATH);
struct path * pp;
param = convert_dev(param, 1);
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp)
pp = find_path_by_devt(vecs->pathvec, param);
if (!pp || !pp->mpp || !pp->mpp->alias)
return 1;
condlog(2, "%s: reinstate path %s (operator)",
pp->mpp->alias, pp->dev_t);
checker_enable(&pp->checker);
return dm_reinstate_path(pp->mpp->alias, pp->dev_t);
}
int
cli_reassign (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
struct multipath *mpp;
param = convert_dev(param, 0);
mpp = find_mp_by_alias(vecs->mpvec, param);
if (!mpp)
return 1;
if (mpp->wait_for_udev) {
condlog(2, "%s: device not fully created, failing reassign",
mpp->alias);
return 1;
}
condlog(3, "%s: reset devices (operator)", param);
dm_reassign(param);
return 0;
}
int
cli_fail(void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, PATH);
struct path * pp;
int r;
param = convert_dev(param, 1);
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp)
pp = find_path_by_devt(vecs->pathvec, param);
if (!pp || !pp->mpp || !pp->mpp->alias)
return 1;
condlog(2, "%s: fail path %s (operator)",
pp->mpp->alias, pp->dev_t);
r = dm_fail_path(pp->mpp->alias, pp->dev_t);
/*
* Suspend path checking to avoid auto-reinstating the path
*/
if (!r)
checker_disable(&pp->checker);
return r;
}
int
show_blacklist (char ** r, int * len)
{
char *c = NULL;
char *reply = NULL;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
struct config *conf;
int fail = 0;
reply = MALLOC(maxlen);
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
while (again) {
if (!reply) {
fail = 1;
break;
}
c = reply;
c += snprint_blacklist_report(conf, c, maxlen);
again = ((c - reply) == maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
pthread_cleanup_pop(1);
if (fail)
return 1;
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
cli_list_blacklist (void * v, char ** reply, int * len, void * data)
{
condlog(3, "list blacklist (operator)");
return show_blacklist(reply, len);
}
int
show_devices (char ** r, int * len, struct vectors *vecs)
{
char *c = NULL;
char *reply = NULL;
unsigned int maxlen = INITIAL_REPLY_LEN;
int again = 1;
struct config *conf;
int fail = 0;
reply = MALLOC(maxlen);
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
while (again) {
if (!reply) {
fail = 1;
break;
}
c = reply;
c += snprint_devices(conf, c, maxlen, vecs);
again = ((c - reply) == maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
pthread_cleanup_pop(1);
if (fail)
return 1;
*r = reply;
*len = (int)(c - reply + 1);
return 0;
}
int
cli_list_devices (void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
condlog(3, "list devices (operator)");
return show_devices(reply, len, vecs);
}
int
cli_quit (void * v, char ** reply, int * len, void * data)
{
return 0;
}
int
cli_shutdown (void * v, char ** reply, int * len, void * data)
{
condlog(3, "shutdown (operator)");
exit_daemon();
return 0;
}
int
cli_getprstatus (void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
param = convert_dev(param, 0);
get_path_layout(vecs->pathvec, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
if (!mpp)
return 1;
condlog(3, "%s: prflag = %u", param, (unsigned int)mpp->prflag);
*len = asprintf(reply, "%d", mpp->prflag);
if (*len < 0)
return 1;
condlog(3, "%s: reply = %s", param, *reply);
return 0;
}
int
cli_setprstatus(void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
param = convert_dev(param, 0);
get_path_layout(vecs->pathvec, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
if (!mpp)
return 1;
if (!mpp->prflag) {
mpp->prflag = 1;
condlog(2, "%s: prflag set", param);
}
return 0;
}
int
cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
param = convert_dev(param, 0);
get_path_layout(vecs->pathvec, 0);
mpp = find_mp_by_str(vecs->mpvec, param);
if (!mpp)
return 1;
if (mpp->prflag) {
mpp->prflag = 0;
condlog(2, "%s: prflag unset", param);
}
return 0;
}
int
cli_getprkey(void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char *mapname = get_keyparam(v, MAP);
char *flagstr = "";
mapname = convert_dev(mapname, 0);
condlog(3, "%s: get persistent reservation key (operator)", mapname);
mpp = find_mp_by_str(vecs->mpvec, mapname);
if (!mpp)
return 1;
*reply = malloc(26);
if (!get_be64(mpp->reservation_key)) {
sprintf(*reply, "none\n");
*len = strlen(*reply) + 1;
return 0;
}
if (mpp->sa_flags & MPATH_F_APTPL_MASK)
flagstr = ":aptpl";
snprintf(*reply, 26, "0x%" PRIx64 "%s\n",
get_be64(mpp->reservation_key), flagstr);
(*reply)[19] = '\0';
*len = strlen(*reply) + 1;
return 0;
}
int
cli_unsetprkey(void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char *mapname = get_keyparam(v, MAP);
int ret;
struct config *conf;
mapname = convert_dev(mapname, 0);
condlog(3, "%s: unset persistent reservation key (operator)", mapname);
mpp = find_mp_by_str(vecs->mpvec, mapname);
if (!mpp)
return 1;
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
ret = set_prkey(conf, mpp, 0, 0);
pthread_cleanup_pop(1);
return ret;
}
int
cli_setprkey(void * v, char ** reply, int * len, void * data)
{
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char *mapname = get_keyparam(v, MAP);
char *keyparam = get_keyparam(v, KEY);
uint64_t prkey;
uint8_t flags;
int ret;
struct config *conf;
mapname = convert_dev(mapname, 0);
condlog(3, "%s: set persistent reservation key (operator)", mapname);
mpp = find_mp_by_str(vecs->mpvec, mapname);
if (!mpp)
return 1;
if (parse_prkey_flags(keyparam, &prkey, &flags) != 0) {
condlog(0, "%s: invalid prkey : '%s'", mapname, keyparam);
return 1;
}
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
ret = set_prkey(conf, mpp, prkey, flags);
pthread_cleanup_pop(1);
return ret;
}
int cli_set_marginal(void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, PATH);
struct path * pp;
param = convert_dev(param, 1);
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp)
pp = find_path_by_devt(vecs->pathvec, param);
if (!pp || !pp->mpp || !pp->mpp->alias)
return 1;
condlog(2, "%s: set marginal path %s (operator)",
pp->mpp->alias, pp->dev_t);
if (pp->mpp->wait_for_udev) {
condlog(2, "%s: device not fully created, failing set marginal",
pp->mpp->alias);
return 1;
}
pp->marginal = 1;
return update_path_groups(pp->mpp, vecs, 0);
}
int cli_unset_marginal(void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, PATH);
struct path * pp;
param = convert_dev(param, 1);
pp = find_path_by_dev(vecs->pathvec, param);
if (!pp)
pp = find_path_by_devt(vecs->pathvec, param);
if (!pp || !pp->mpp || !pp->mpp->alias)
return 1;
condlog(2, "%s: unset marginal path %s (operator)",
pp->mpp->alias, pp->dev_t);
if (pp->mpp->wait_for_udev) {
condlog(2, "%s: device not fully created, "
"failing unset marginal", pp->mpp->alias);
return 1;
}
pp->marginal = 0;
return update_path_groups(pp->mpp, vecs, 0);
}
int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
char * mapname = get_keyparam(v, MAP);
struct multipath *mpp;
struct pathgroup * pgp;
struct path * pp;
unsigned int i, j;
int minor;
mapname = convert_dev(mapname, 0);
condlog(2, "%s: unset all marginal paths (operator)",
mapname);
if (sscanf(mapname, "dm-%d", &minor) == 1)
mpp = find_mp_by_minor(vecs->mpvec, minor);
else
mpp = find_mp_by_alias(vecs->mpvec, mapname);
if (!mpp) {
condlog(0, "%s: invalid map name. "
"cannot unset marginal paths", mapname);
return 1;
}
if (mpp->wait_for_udev) {
condlog(2, "%s: device not fully created, "
"failing unset all marginal", mpp->alias);
return 1;
}
vector_foreach_slot (mpp->pg, pgp, i)
vector_foreach_slot (pgp->paths, pp, j)
pp->marginal = 0;
return update_path_groups(mpp, vecs, 0);
}