NetBSD/sbin/dmctl/dmctl.c

580 lines
14 KiB
C

/*
* Copyright (c) 2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Adam Hamsik.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <prop/proplib.h>
#include <dm.h>
#ifdef RUMP_ACTION
#include <rump/rump.h>
#include <rump/rumpclient.h>
#include <rump/rump_syscalls.h>
#endif
/* dmctl command is used to communicate with device-mapper driver in NetBSD
* it uses libdm library to create and send required data to kernel.
*
* Main purpose of dmctl is to add possibility to use device-mapper driver
* from outside of LVM scope.
*/
#define DMCTL_CMD_REQ_NODEVNAME 0 /* Command do not require device name */
#define DMCTL_CMD_REQ_DEVNAME 1 /* Command require device name to work */
#define DMCTL_CMD_REQ_NEWNAME 2 /* Command require device name and
newname to work */
struct command {
const char *cmd_name;
const char *cmd_help;
const char *ioctl_cmd_name;
int min_args;
int (*cmd_func)(int, char *[], libdm_task_t);
};
static const char *dvname; /* device name */
static const char *cmdname; /* command user issued */
static char * parse_stdin(char *);
static int dmctl_get_version(int, char *[], libdm_task_t);
static int dmctl_get_targets(int, char *[], libdm_task_t);
static int dmctl_get_device_info(int, char *[], libdm_task_t);
static int dmctl_create_dev(int, char *[], libdm_task_t);
static int dmctl_dev_rename(int, char *[], libdm_task_t);
static int dmctl_dev_remove(int, char *[], libdm_task_t);
static int dmctl_dev_resume(int, char *[], libdm_task_t);
static int dmctl_dev_suspend(int, char *[], libdm_task_t);
static int dmctl_dev_deps(int, char *[], libdm_task_t);
static int dmctl_list_devices(int, char *[], libdm_task_t);
static int dmctl_table_reload(int, char *[], libdm_task_t);
static int dmctl_table_status(int, char *[], libdm_task_t);
__dead static void usage(void);
static struct command commands[] = {
{ "version",
"Print driver and lib version.",
NULL, DMCTL_CMD_REQ_NODEVNAME,
dmctl_get_version },
{ "targets",
"List available kernel targets.",
NULL, DMCTL_CMD_REQ_NODEVNAME,
dmctl_get_targets },
{ "create",
"Create device with [dm device name].",
NULL, DMCTL_CMD_REQ_DEVNAME,
dmctl_create_dev },
{ "ls",
"List existing dm devices.",
"names", DMCTL_CMD_REQ_NODEVNAME,
dmctl_list_devices },
{ "info",
"Get info about device with [dm device name].",
NULL, DMCTL_CMD_REQ_DEVNAME,
dmctl_get_device_info },
{ "rename",
"Rename device with [dm device name] to [dm device new name].",
NULL, DMCTL_CMD_REQ_NEWNAME,
dmctl_dev_rename },
{ "remove",
"Remove device with [dm device name].",
NULL, DMCTL_CMD_REQ_DEVNAME,
dmctl_dev_remove },
{ "resume",
"Resume IO on dm device [dm device name].",
NULL, DMCTL_CMD_REQ_DEVNAME,
dmctl_dev_resume },
{ "suspend",
"Suspend IO on dm device [dm device name].",
NULL, DMCTL_CMD_REQ_DEVNAME,
dmctl_dev_suspend },
{ "deps",
"Print physical dependencies for dm device [dm device name].",
NULL, DMCTL_CMD_REQ_DEVNAME,
dmctl_dev_deps },
{ "reload",
"Switch active and passive tables for device with [dm device name].",
NULL, DMCTL_CMD_REQ_DEVNAME,
dmctl_table_reload },
{ "status",
"Print status for device with [dm device name].",
"table", DMCTL_CMD_REQ_DEVNAME,
dmctl_table_status },
{ "table",
"Print active table for device with [dm device name].",
NULL, DMCTL_CMD_REQ_DEVNAME,
dmctl_table_status },
{ NULL,
NULL,
NULL, 0,
NULL },
};
int
main(int argc, char *argv[])
{
int i;
int oargc;
libdm_task_t task;
oargc = 0;
#ifdef RUMP_ACTION
if (rumpclient_init() == -1)
err(EXIT_FAILURE, "rump client init failed");
#endif
/* Must have at least: device command */
if (argc < 2)
usage();
/* Skip program name, get and skip device name and command. */
cmdname = argv[1];
if (argc > 2) {
oargc = 1;
dvname = argv[2];
}
if (argc > 3) {
argv += 3;
argc -= 3;
oargc = 2;
} else {
argv = 0;
argc = 0;
}
for (i = 0; commands[i].cmd_name != NULL; i++)
if (strcmp(cmdname, commands[i].cmd_name) == 0)
break;
if (commands[i].cmd_name == NULL)
errx(EXIT_FAILURE, "unknown command: %s", cmdname);
if (commands[i].ioctl_cmd_name != NULL)
cmdname = commands[i].ioctl_cmd_name;
if (oargc != commands[i].min_args) {
(void)fprintf(stderr, "Insufficient number of arguments for "
"command: %s specified\n", commands[i].cmd_name);
usage();
}
/*
* Create libdm task, and pass it to command handler later.
* Don't release it here because it will be replaced by different
* dictionary received from kernel after libdm_task_run.
*/
task = libdm_task_create(cmdname);
(*commands[i].cmd_func)(argc, argv, task);
return 0;
}
/*
* Print library and kernel driver versions if command can be used only when
* major, minor number of library version is <= kernel.
*/
static int
dmctl_get_version(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
uint32_t ver[3];
(void)libdm_task_get_cmd_version(task, ver, sizeof(ver));
printf("Library protocol version %d:%d:%d\n", ver[0], ver[1], ver[2]);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_get_version: libdm_task_run failed.");
(void)libdm_task_get_cmd_version(task, ver, 3);
printf("Kernel protocol version %d:%d:%d\n",ver[0], ver[1], ver[2]);
libdm_task_destroy(task);
return 0;
}
/*
* Get list of available targets from kernel and print them.
*/
static int
dmctl_get_targets(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
libdm_cmd_t cmd;
libdm_iter_t iter;
libdm_target_t target;
uint32_t ver[3];
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_get_targets: libdm_task_run failed.");
if ((cmd = libdm_task_get_cmd(task)) == NULL)
return ENOENT;
iter = libdm_cmd_iter_create(cmd);
while((target = libdm_cmd_get_target(iter)) != NULL){
printf("Target name: %s\n", libdm_target_get_name(target));
libdm_target_get_version(target, ver, sizeof(ver));
printf("Target version %d.%d.%d\n\n", ver[0], ver[1], ver[2]);
libdm_target_destroy(target);
}
libdm_iter_destroy(iter);
libdm_cmd_destroy(cmd);
libdm_task_destroy(task);
return 0;
}
/*
* Create device with name used as second parameter.
* TODO: Support for UUIDs here.
*/
static int
dmctl_create_dev(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
libdm_task_set_name(dvname, task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_create_dev: libdm_task_run failed.");
libdm_task_destroy(task);
return 0;
}
/*
* Get basic device info from device-mapper driver.
*/
static int
dmctl_get_device_info(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
libdm_task_set_name(dvname, task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "%s: libdm_task_run failed", __func__);
printf("Printing Device info for:\n");
printf("Device name: \t\t%s\n", libdm_task_get_name(task));
printf("Device uuid: \t\t%s\n", libdm_task_get_uuid(task));
printf("Device minor: \t\t%d\n", libdm_task_get_minor(task));
printf("Device target number: \t%d\n", libdm_task_get_target_num(task));
printf("Device flags: \t\t%d\n", libdm_task_get_flags(task));
libdm_task_destroy(task);
return 0;
}
/*
* List all device in device-mapper driver.
*/
static int
dmctl_list_devices(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
libdm_cmd_t cmd;
libdm_iter_t iter;
libdm_dev_t dev;
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_list_devices: libdm_task_run failed.");
if ((cmd = libdm_task_get_cmd(task)) == NULL)
return ENOENT;
iter = libdm_cmd_iter_create(cmd);
while((dev = libdm_cmd_get_dev(iter)) != NULL){
printf("Device name: %s, device minor: %d \n",
libdm_dev_get_name(dev), libdm_dev_get_minor(dev));
libdm_dev_destroy(dev);
}
libdm_iter_destroy(iter);
libdm_cmd_destroy(cmd);
libdm_task_destroy(task);
return 0;
}
/*
* Rename device to new name
*/
static int
dmctl_dev_rename(int argc __unused, char *argv[], libdm_task_t task)
{
libdm_cmd_t cmd;
libdm_task_set_name(dvname, task);
cmd = libdm_cmd_create();
libdm_dev_set_newname(argv[0], cmd);
libdm_task_set_cmd(cmd, task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_dev_rename: libdm_task_run failed.");
libdm_cmd_destroy(cmd);
libdm_task_destroy(task);
return 0;
}
/*
* Remove device from dm device list.
*/
static int
dmctl_dev_remove(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
if (dvname == NULL)
return (ENOENT);
libdm_task_set_name(dvname, task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_dev_remove: libdm_task_run failed.");
libdm_task_destroy(task);
return 0;
}
/*
* Resume device which was suspended or created right now.
* Replace table in "active slot" with table in "inactive slot".
*/
static int
dmctl_dev_resume(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
libdm_task_set_name(dvname, task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_dev_resume: libdm_task_run failed.");
libdm_task_destroy(task);
return 0;
}
/*
* Resume device which was suspended or created right now.
* Replace table in "active slot" with table in "inactive slot".
*/
static int
dmctl_dev_suspend(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
libdm_task_set_name(dvname, task);
libdm_task_set_suspend_flag(task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_dev_suspend: libdm_task_run failed.");
libdm_task_destroy(task);
return 0;
}
/*
* Get device dependencies from device-mapper. Device dependency is physical
* device on which dm device depends.
*/
static int
dmctl_dev_deps(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
libdm_cmd_t cmd;
libdm_iter_t iter;
dev_t dev_deps;
libdm_task_set_name(dvname, task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "dmctl_dev_deps: libdm_task_run failed.");
if ((cmd = libdm_task_get_cmd(task)) == NULL)
return ENOENT;
iter = libdm_cmd_iter_create(cmd);
printf("Device %s dependencies \n", dvname);
while((dev_deps = libdm_cmd_get_deps(iter)) != 0)
printf("major: %d minor: %d\n", major(dev_deps), minor(dev_deps));
libdm_iter_destroy(iter);
libdm_cmd_destroy(cmd);
libdm_task_destroy(task);
return 0;
}
/*
* Reload device table to get new one to use.
*/
static int
dmctl_table_reload(int argc, char *argv[], libdm_task_t task)
{
libdm_cmd_t cmd;
libdm_table_t table;
char *params;
char *file_path;
char target[128];
int len;
uint64_t start, length;
file_path = NULL;
params = NULL;
cmd = libdm_cmd_create();
libdm_task_set_name(dvname, task);
if (argc != 0)
file_path = argv[0];
while ((params = parse_stdin(file_path)) != NULL) {
table = libdm_table_create();
sscanf(params, "%"PRIu64" %"PRIu64" %s %n", &start, &length, target, &len);
libdm_table_set_start(start, table);
libdm_table_set_length(length, table);
libdm_table_set_target(target, table);
libdm_table_set_params(params + len, table);
libdm_cmd_set_table(table, cmd);
libdm_table_destroy(table);
free(params);
}
libdm_task_set_cmd(cmd, task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "libdm_task_run: from dmctl_table_reload failed.");
libdm_cmd_destroy(cmd);
libdm_task_destroy(task);
free(params);
return 0;
}
/*
* Get table status from device.
*/
static int
dmctl_table_status(int argc __unused, char *argv[] __unused, libdm_task_t task)
{
libdm_cmd_t cmd;
libdm_table_t table;
libdm_iter_t iter;
libdm_task_set_name(dvname, task);
libdm_task_set_status_flag(task);
if (libdm_task_run(task) != 0)
err(EXIT_FAILURE, "libdm_task_run");
if ((cmd = libdm_task_get_cmd(task)) == NULL)
return ENOENT;
iter = libdm_cmd_iter_create(cmd);
printf("Getting device table for device %s\n", dvname);
while ((table = libdm_cmd_get_table(iter)) != NULL) {
printf("%10"PRIu64" %10"PRIu64" %s\n",
libdm_table_get_start(table),
libdm_table_get_length(table),
libdm_table_get_target(table));
libdm_table_destroy(table);
}
libdm_iter_destroy(iter);
libdm_cmd_destroy(cmd);
libdm_task_destroy(task);
return 0;
}
static void
usage(void)
{
int i;
(void)fprintf(stderr, "usage: %s command [dm device name]\n"
"Available commands are:\n ", getprogname());
for (i = 0; commands[i].cmd_name != NULL; i++)
(void)fprintf(stderr, "\t%s\t%s\n", commands[i].cmd_name, commands[i].cmd_help);
exit(EXIT_FAILURE);
}
static char *
parse_stdin(char *file_path)
{
char *buf, *lbuf;
size_t len;
FILE *fp;
lbuf = NULL;
if (file_path) {
if ((fp = fopen(file_path, "r")) == NULL)
err(ENOENT, "Cannot open table file");
} else
fp = stdin;
if ((buf = fgetln(fp, &len)) == NULL)
return NULL;
if (buf[len - 1] != '\n')
len++;
if ((lbuf = malloc(len)) == NULL)
err(EXIT_FAILURE, "malloc");
memcpy(lbuf, buf, len);
lbuf[len - 1] = '\0';
return lbuf;
}