/* * 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 #include #include #include #include #include #include #include #include #include #ifdef RUMP_ACTION #include #include #include #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 posibility 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 dependiences 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" witg 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 dependiences 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 dependiences \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; }