#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
#define DMCTL_CMD_REQ_NODEVNAME 0
#define DMCTL_CMD_REQ_DEVNAME 1
#define DMCTL_CMD_REQ_NEWNAME 2
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;
static const char *cmdname;
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
if (argc < 2)
usage();
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();
}
task = libdm_task_create(cmdname);
(*commands[i].cmd_func)(argc, argv, task);
return 0;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}