1/* 2 * Copyright (c) 2010 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Adam Hamsik. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/types.h> 31#include <sys/param.h> 32 33#include <ctype.h> 34#include <err.h> 35#include <errno.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39 40#include <prop/proplib.h> 41 42#include <dm.h> 43 44#ifdef RUMP_ACTION 45#include <rump/rump.h> 46#include <rump/rumpclient.h> 47#include <rump/rump_syscalls.h> 48#endif 49 50/* dmctl command is used to communicate with device-mapper driver in NetBSD 51 * it uses libdm library to create and send required data to kernel. 52 * 53 * Main purpose of dmctl is to add posibility to use device-mapper driver 54 * from outside of LVM scope. 55 */ 56 57#define DMCTL_CMD_REQ_NODEVNAME 0 /* Command do not require device name */ 58#define DMCTL_CMD_REQ_DEVNAME 1 /* Command require device name to work */ 59#define DMCTL_CMD_REQ_NEWNAME 2 /* Command require device name and 60 newname to work */ 61struct command { 62 const char *cmd_name; 63 const char *cmd_help; 64 const char *ioctl_cmd_name; 65 int min_args; 66 int (*cmd_func)(int, char *[], libdm_task_t); 67}; 68 69static const char *dvname; /* device name */ 70static const char *cmdname; /* command user issued */ 71 72static char * parse_stdin(char *); 73 74static int dmctl_get_version(int, char *[], libdm_task_t); 75static int dmctl_get_targets(int, char *[], libdm_task_t); 76static int dmctl_get_device_info(int, char *[], libdm_task_t); 77static int dmctl_create_dev(int, char *[], libdm_task_t); 78static int dmctl_dev_rename(int, char *[], libdm_task_t); 79static int dmctl_dev_remove(int, char *[], libdm_task_t); 80static int dmctl_dev_resume(int, char *[], libdm_task_t); 81static int dmctl_dev_suspend(int, char *[], libdm_task_t); 82static int dmctl_dev_deps(int, char *[], libdm_task_t); 83static int dmctl_list_devices(int, char *[], libdm_task_t); 84static int dmctl_table_reload(int, char *[], libdm_task_t); 85static int dmctl_table_status(int, char *[], libdm_task_t); 86__dead static void usage(void); 87 88static struct command commands[] = { 89 { "version", 90 "Print driver and lib version.", 91 NULL, DMCTL_CMD_REQ_NODEVNAME, 92 dmctl_get_version }, 93 { "targets", 94 "List available kernel targets.", 95 NULL, DMCTL_CMD_REQ_NODEVNAME, 96 dmctl_get_targets }, 97 { "create", 98 "Create device with [dm device name].", 99 NULL, DMCTL_CMD_REQ_DEVNAME, 100 dmctl_create_dev }, 101 { "ls", 102 "List existing dm devices.", 103 "names", DMCTL_CMD_REQ_NODEVNAME, 104 dmctl_list_devices }, 105 { "info", 106 "Get info about device with [dm device name].", 107 NULL, DMCTL_CMD_REQ_DEVNAME, 108 dmctl_get_device_info }, 109 { "rename", 110 "Rename device with [dm device name] to [dm device new name].", 111 NULL, DMCTL_CMD_REQ_NEWNAME, 112 dmctl_dev_rename }, 113 { "remove", 114 "Remove device with [dm device name].", 115 NULL, DMCTL_CMD_REQ_DEVNAME, 116 dmctl_dev_remove }, 117 { "resume", 118 "Resume IO on dm device [dm device name].", 119 NULL, DMCTL_CMD_REQ_DEVNAME, 120 dmctl_dev_resume }, 121 { "suspend", 122 "Suspend IO on dm device [dm device name].", 123 NULL, DMCTL_CMD_REQ_DEVNAME, 124 dmctl_dev_suspend }, 125 { "deps", 126 "Print physical dependiences for dm device [dm device name].", 127 NULL, DMCTL_CMD_REQ_DEVNAME, 128 dmctl_dev_deps }, 129 { "reload", 130 "Switch active and passive tables for device with [dm device name].", 131 NULL, DMCTL_CMD_REQ_DEVNAME, 132 dmctl_table_reload }, 133 { "status", 134 "Print status for device with [dm device name].", 135 "table", DMCTL_CMD_REQ_DEVNAME, 136 dmctl_table_status }, 137 { "table", 138 "Print active table for device with [dm device name].", 139 NULL, DMCTL_CMD_REQ_DEVNAME, 140 dmctl_table_status }, 141 { NULL, 142 NULL, 143 NULL, 0, 144 NULL }, 145}; 146 147int 148main(int argc, char *argv[]) 149{ 150 int i; 151 int oargc; 152 libdm_task_t task; 153 154 oargc = 0; 155 156#ifdef RUMP_ACTION 157 if (rumpclient_init() == -1) 158 err(EXIT_FAILURE, "rump client init failed"); 159#endif 160 161 /* Must have at least: device command */ 162 if (argc < 2) 163 usage(); 164 165 /* Skip program name, get and skip device name and command. */ 166 cmdname = argv[1]; 167 if (argc > 2) { 168 oargc = 1; 169 dvname = argv[2]; 170 } 171 172 if (argc > 3) { 173 argv += 3; 174 argc -= 3; 175 oargc = 2; 176 } else { 177 argv = 0; 178 argc = 0; 179 } 180 181 for (i = 0; commands[i].cmd_name != NULL; i++) 182 if (strcmp(cmdname, commands[i].cmd_name) == 0) 183 break; 184 185 if (commands[i].cmd_name == NULL) 186 errx(EXIT_FAILURE, "unknown command: %s", cmdname); 187 188 if (commands[i].ioctl_cmd_name != NULL) 189 cmdname = commands[i].ioctl_cmd_name; 190 191 if (oargc != commands[i].min_args) { 192 (void)fprintf(stderr, "Insufficient number of arguments for " 193 "command: %s specified\n", commands[i].cmd_name); 194 usage(); 195 } 196 197 /* 198 * Create libdm task, and pass it to command handler later. 199 * Don't release it here because it will be replaced by different 200 * dictionary received from kernel after libdm_task_run. 201 */ 202 task = libdm_task_create(cmdname); 203 204 (*commands[i].cmd_func)(argc, argv, task); 205 206 return 0; 207} 208 209/* 210 * Print library and kernel driver versions if command can be used only when 211 * major, minor number of library version is <= kernel. 212 */ 213static int 214dmctl_get_version(int argc __unused, char *argv[] __unused, libdm_task_t task) 215{ 216 uint32_t ver[3], size; 217 218 size = libdm_task_get_cmd_version(task, ver, sizeof(ver)); 219 220 printf("Library protocol version %d:%d:%d\n", ver[0], ver[1], ver[2]); 221 222 if (libdm_task_run(task) != 0) 223 err(EXIT_FAILURE, "dmctl_get_version: libdm_task_run failed."); 224 225 size = libdm_task_get_cmd_version(task, ver, 3); 226 printf("Kernel protocol version %d:%d:%d\n",ver[0], ver[1], ver[2]); 227 228 libdm_task_destroy(task); 229 return 0; 230} 231 232/* 233 * Get list of available targets from kernel and print them. 234 */ 235static int 236dmctl_get_targets(int argc __unused, char *argv[] __unused, libdm_task_t task) 237{ 238 libdm_cmd_t cmd; 239 libdm_iter_t iter; 240 libdm_target_t target; 241 uint32_t ver[3]; 242 243 if (libdm_task_run(task) != 0) 244 err(EXIT_FAILURE, "dmctl_get_targets: libdm_task_run failed."); 245 246 if ((cmd = libdm_task_get_cmd(task)) == NULL) 247 return ENOENT; 248 249 iter = libdm_cmd_iter_create(cmd); 250 251 while((target = libdm_cmd_get_target(iter)) != NULL){ 252 printf("Target name: %s\n", libdm_target_get_name(target)); 253 254 libdm_target_get_version(target, ver, sizeof(ver)); 255 printf("Target version %d.%d.%d\n\n", ver[0], ver[1], ver[2]); 256 257 libdm_target_destroy(target); 258 } 259 260 libdm_iter_destroy(iter); 261 libdm_cmd_destroy(cmd); 262 libdm_task_destroy(task); 263 264 return 0; 265} 266 267/* 268 * Create device with name used as second parameter. 269 * TODO: Support for UUIDs here. 270 */ 271static int 272dmctl_create_dev(int argc __unused, char *argv[] __unused, libdm_task_t task) 273{ 274 275 libdm_task_set_name(dvname, task); 276 277 if (libdm_task_run(task) != 0) 278 err(EXIT_FAILURE, "dmctl_create_dev: libdm_task_run failed."); 279 280 libdm_task_destroy(task); 281 return 0; 282} 283 284/* 285 * Get basic device info from device-mapper driver. 286 */ 287static int 288dmctl_get_device_info(int argc __unused, char *argv[] __unused, libdm_task_t task) 289{ 290 291 libdm_task_set_name(dvname, task); 292 293 if (libdm_task_run(task) != 0) 294 err(EXIT_FAILURE, "dmctl_get_device_info: libdm_task_run failed.\n"); 295 296 printf("Printing Device info for:\n"); 297 printf("Device name: \t\t%s\n", libdm_task_get_name(task)); 298 printf("Device uuid: \t\t%s\n", libdm_task_get_uuid(task)); 299 printf("Device minor: \t\t%d\n", libdm_task_get_minor(task)); 300 printf("Device target number: \t%d\n", libdm_task_get_target_num(task)); 301 printf("Device flags: \t\t%d\n", libdm_task_get_flags(task)); 302 303 libdm_task_destroy(task); 304 return 0; 305} 306 307/* 308 * List all device in device-mapper driver. 309 */ 310static int 311dmctl_list_devices(int argc __unused, char *argv[] __unused, libdm_task_t task) 312{ 313 libdm_cmd_t cmd; 314 libdm_iter_t iter; 315 libdm_dev_t dev; 316 317 if (libdm_task_run(task) != 0) 318 err(EXIT_FAILURE, "dmctl_list_devices: libdm_task_run failed."); 319 320 if ((cmd = libdm_task_get_cmd(task)) == NULL) 321 return ENOENT; 322 323 iter = libdm_cmd_iter_create(cmd); 324 325 while((dev = libdm_cmd_get_dev(iter)) != NULL){ 326 printf("Device name: %s, device minor: %d \n", 327 libdm_dev_get_name(dev), libdm_dev_get_minor(dev)); 328 libdm_dev_destroy(dev); 329 } 330 331 libdm_iter_destroy(iter); 332 libdm_cmd_destroy(cmd); 333 libdm_task_destroy(task); 334 335 return 0; 336} 337 338/* 339 * Rename device to new name 340 */ 341static int 342dmctl_dev_rename(int argc __unused, char *argv[], libdm_task_t task) 343{ 344 libdm_cmd_t cmd; 345 346 libdm_task_set_name(dvname, task); 347 348 cmd = libdm_cmd_create(); 349 libdm_dev_set_newname(argv[0], cmd); 350 libdm_task_set_cmd(cmd, task); 351 352 if (libdm_task_run(task) != 0) 353 err(EXIT_FAILURE, "dmctl_dev_rename: libdm_task_run failed."); 354 355 libdm_cmd_destroy(cmd); 356 libdm_task_destroy(task); 357 358 return 0; 359} 360 361/* 362 * Remove device from dm device list. 363 */ 364static int 365dmctl_dev_remove(int argc __unused, char *argv[] __unused, libdm_task_t task) 366{ 367 368 if (dvname == NULL) 369 return (ENOENT); 370 371 libdm_task_set_name(dvname, task); 372 373 if (libdm_task_run(task) != 0) 374 err(EXIT_FAILURE, "dmctl_dev_remove: libdm_task_run failed."); 375 376 libdm_task_destroy(task); 377 return 0; 378} 379 380/* 381 * Resume device which was suspended or created right now. 382 * Replace table in "active slot" witg table in "inactive slot". 383 */ 384static int 385dmctl_dev_resume(int argc __unused, char *argv[] __unused, libdm_task_t task) 386{ 387 388 libdm_task_set_name(dvname, task); 389 390 if (libdm_task_run(task) != 0) 391 err(EXIT_FAILURE, "dmctl_dev_resume: libdm_task_run failed."); 392 393 libdm_task_destroy(task); 394 return 0; 395} 396 397/* 398 * Resume device which was suspended or created right now. 399 * Replace table in "active slot" with table in "inactive slot". 400 */ 401static int 402dmctl_dev_suspend(int argc __unused, char *argv[] __unused, libdm_task_t task) 403{ 404 405 libdm_task_set_name(dvname, task); 406 libdm_task_set_suspend_flag(task); 407 408 if (libdm_task_run(task) != 0) 409 err(EXIT_FAILURE, "dmctl_dev_suspend: libdm_task_run failed."); 410 411 libdm_task_destroy(task); 412 return 0; 413} 414 415/* 416 * Get device dependiences from device-mapper. Device dependency is physical 417 * device on which dm device depends. 418 */ 419static int 420dmctl_dev_deps(int argc __unused, char *argv[] __unused, libdm_task_t task) 421{ 422 libdm_cmd_t cmd; 423 libdm_iter_t iter; 424 dev_t dev_deps; 425 426 libdm_task_set_name(dvname, task); 427 428 if (libdm_task_run(task) != 0) 429 err(EXIT_FAILURE, "dmctl_dev_deps: libdm_task_run failed."); 430 431 if ((cmd = libdm_task_get_cmd(task)) == NULL) 432 return ENOENT; 433 434 iter = libdm_cmd_iter_create(cmd); 435 436 printf("Device %s dependiences \n", dvname); 437 438 while((dev_deps = libdm_cmd_get_deps(iter)) != 0) 439 printf("major: %d minor: %d\n", major(dev_deps), minor(dev_deps)); 440 441 libdm_iter_destroy(iter); 442 libdm_cmd_destroy(cmd); 443 libdm_task_destroy(task); 444 445 return 0; 446} 447 448/* 449 * Reload device table to get new one to use. 450 */ 451static int 452dmctl_table_reload(int argc, char *argv[], libdm_task_t task) 453{ 454 libdm_cmd_t cmd; 455 libdm_table_t table; 456 457 char *params; 458 char *file_path; 459 char target[128]; 460 int len; 461 uint64_t start, length; 462 463 file_path = NULL; 464 params = NULL; 465 466 cmd = libdm_cmd_create(); 467 468 libdm_task_set_name(dvname, task); 469 470 if (argc != 0) 471 file_path = argv[0]; 472 473 while ((params = parse_stdin(file_path)) != NULL) { 474 table = libdm_table_create(); 475 476 sscanf(params, "%"PRIu64" %"PRIu64" %s %n", &start, &length, target, &len); 477 478 libdm_table_set_start(start, table); 479 libdm_table_set_length(length, table); 480 libdm_table_set_target(target, table); 481 libdm_table_set_params(params + len, table); 482 libdm_cmd_set_table(table, cmd); 483 484 libdm_table_destroy(table); 485 486 free(params); 487 } 488 489 libdm_task_set_cmd(cmd, task); 490 491 if (libdm_task_run(task) != 0) 492 err(EXIT_FAILURE, "libdm_task_run: from dmctl_table_reload failed."); 493 494 libdm_cmd_destroy(cmd); 495 libdm_task_destroy(task); 496 free(params); 497 498 return 0; 499} 500 501/* 502 * Get table status from device. 503 */ 504static int 505dmctl_table_status(int argc __unused, char *argv[] __unused, libdm_task_t task) 506{ 507 libdm_cmd_t cmd; 508 libdm_table_t table; 509 libdm_iter_t iter; 510 511 libdm_task_set_name(dvname, task); 512 libdm_task_set_status_flag(task); 513 514 if (libdm_task_run(task) != 0) 515 err(EXIT_FAILURE, "libdm_task_run"); 516 517 if ((cmd = libdm_task_get_cmd(task)) == NULL) 518 return ENOENT; 519 520 iter = libdm_cmd_iter_create(cmd); 521 522 printf("Getting device table for device %s\n", dvname); 523 524 while ((table = libdm_cmd_get_table(iter)) != NULL) { 525 printf("%10"PRIu64" %10"PRIu64" %s\n", 526 libdm_table_get_start(table), 527 libdm_table_get_length(table), 528 libdm_table_get_target(table)); 529 libdm_table_destroy(table); 530 } 531 532 libdm_iter_destroy(iter); 533 libdm_cmd_destroy(cmd); 534 libdm_task_destroy(task); 535 536 return 0; 537} 538 539static void 540usage(void) 541{ 542 int i; 543 544 (void)fprintf(stderr, "usage: %s command [dm device name]\n" 545 "Available commands are:\n ", getprogname()); 546 for (i = 0; commands[i].cmd_name != NULL; i++) 547 (void)fprintf(stderr, "\t%s\t%s\n", commands[i].cmd_name, commands[i].cmd_help); 548 exit(EXIT_FAILURE); 549} 550 551static char * 552parse_stdin(char *file_path) 553{ 554 char *buf, *lbuf; 555 size_t len; 556 FILE *fp; 557 558 lbuf = NULL; 559 560 if (file_path) { 561 if ((fp = fopen(file_path, "r")) == NULL) 562 err(ENOENT, "Cannot open table file\n"); 563 } else 564 fp = stdin; 565 566 if ((buf = fgetln(fp, &len)) == NULL) 567 return NULL; 568 569 if (buf[len - 1] != '\n') 570 len++; 571 572 if ((lbuf = malloc(len)) == NULL) 573 err(EXIT_FAILURE, "malloc"); 574 575 memcpy(lbuf, buf, len); 576 lbuf[len - 1] = '\0'; 577 578 return lbuf; 579} 580