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 */ 61 struct 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 69 int fd; /* file descriptor for device */ 70 const char *dvname; /* device name */ 71 const char *cmdname; /* command user issued */ 72 73 static char * parse_stdin(char *); 74 75 static int dmctl_get_version(int, char *[], libdm_task_t); 76 static int dmctl_get_targets(int, char *[], libdm_task_t); 77 static int dmctl_get_device_info(int, char *[], libdm_task_t); 78 static int dmctl_create_dev(int, char *[], libdm_task_t); 79 static int dmctl_dev_rename(int, char *[], libdm_task_t); 80 static int dmctl_dev_remove(int, char *[], libdm_task_t); 81 static int dmctl_dev_resume(int, char *[], libdm_task_t); 82 static int dmctl_dev_suspend(int, char *[], libdm_task_t); 83 static int dmctl_dev_deps(int, char *[], libdm_task_t); 84 static int dmctl_list_devices(int, char *[], libdm_task_t); 85 static int dmctl_table_reload(int, char *[], libdm_task_t); 86 static int dmctl_table_status(int, char *[], libdm_task_t); 87 void usage(void); 88 89 struct command commands[] = { 90 { "version", 91 "Print driver and lib version.", 92 NULL, DMCTL_CMD_REQ_NODEVNAME, 93 dmctl_get_version }, 94 { "targets", 95 "List available kernel targets.", 96 NULL, DMCTL_CMD_REQ_NODEVNAME, 97 dmctl_get_targets }, 98 { "create", 99 "Create device with [dm device name].", 100 NULL, DMCTL_CMD_REQ_DEVNAME, 101 dmctl_create_dev }, 102 { "ls", 103 "List existing dm devices.", 104 "names", DMCTL_CMD_REQ_NODEVNAME, 105 dmctl_list_devices }, 106 { "info", 107 "Get info about device with [dm device name].", 108 NULL, DMCTL_CMD_REQ_DEVNAME, 109 dmctl_get_device_info }, 110 { "rename", 111 "Rename device with [dm device name] to [dm device new name].", 112 NULL, DMCTL_CMD_REQ_NEWNAME, 113 dmctl_dev_rename }, 114 { "remove", 115 "Remove device with [dm device name].", 116 NULL, DMCTL_CMD_REQ_DEVNAME, 117 dmctl_dev_remove }, 118 { "resume", 119 "Resume IO on dm device [dm device name].", 120 NULL, DMCTL_CMD_REQ_DEVNAME, 121 dmctl_dev_resume }, 122 { "suspend", 123 "Suspend IO on dm device [dm device name].", 124 NULL, DMCTL_CMD_REQ_DEVNAME, 125 dmctl_dev_suspend }, 126 { "deps", 127 "Print physical dependiences for dm device [dm device name].", 128 NULL, DMCTL_CMD_REQ_DEVNAME, 129 dmctl_dev_deps }, 130 { "reload", 131 "Switch active and passive tables for device with [dm device name].", 132 NULL, DMCTL_CMD_REQ_DEVNAME, 133 dmctl_table_reload }, 134 { "status", 135 "Print status for device with [dm device name].", 136 "table", DMCTL_CMD_REQ_DEVNAME, 137 dmctl_table_status }, 138 { "table", 139 "Print active table for device with [dm device name].", 140 NULL, DMCTL_CMD_REQ_DEVNAME, 141 dmctl_table_status }, 142 { NULL, 143 NULL, 144 NULL, 0, 145 NULL }, 146 }; 147 148 int 149 main(int argc, char *argv[]) 150 { 151 int i; 152 int oargc; 153 libdm_task_t task; 154 155 oargc = 0; 156 157 #ifdef RUMP_ACTION 158 if (rumpclient_init() == -1) 159 err(EXIT_FAILURE, "rump client init failed"); 160 #endif 161 162 /* Must have at least: device command */ 163 if (argc < 2) 164 usage(); 165 166 /* Skip program name, get and skip device name and command. */ 167 cmdname = argv[1]; 168 if (argc > 2) { 169 oargc = 1; 170 dvname = argv[2]; 171 } 172 173 if (argc > 3) { 174 argv += 3; 175 argc -= 3; 176 oargc = 2; 177 } else { 178 argv = 0; 179 argc = 0; 180 } 181 182 for (i = 0; commands[i].cmd_name != NULL; i++) 183 if (strcmp(cmdname, commands[i].cmd_name) == 0) 184 break; 185 186 if (commands[i].cmd_name == NULL) 187 errx(EXIT_FAILURE, "unknown command: %s", cmdname); 188 189 if (commands[i].ioctl_cmd_name != NULL) 190 cmdname = commands[i].ioctl_cmd_name; 191 192 if (oargc != commands[i].min_args) { 193 (void)fprintf(stderr, "Insufficient number of arguments for " 194 "command: %s specified\n", commands[i].cmd_name); 195 usage(); 196 } 197 198 /* 199 * Create libdm task, and pass it to command handler later. 200 * Don't release it here because it will be replaced by different 201 * dictionary received from kernel after libdm_task_run. 202 */ 203 task = libdm_task_create(cmdname); 204 205 (*commands[i].cmd_func)(argc, argv, task); 206 207 return 0; 208 } 209 210 /* 211 * Print library and kernel driver versions if command can be used only when 212 * major, minor number of library version is <= kernel. 213 */ 214 static int 215 dmctl_get_version(int argc __unused, char *argv[] __unused, libdm_task_t task) 216 { 217 uint32_t ver[3], size; 218 219 size = libdm_task_get_cmd_version(task, ver, sizeof(ver)); 220 221 printf("Library protocol version %d:%d:%d\n", ver[0], ver[1], ver[2]); 222 223 if (libdm_task_run(task) != 0) 224 err(EXIT_FAILURE, "dmctl_get_version: libdm_task_run failed."); 225 226 size = libdm_task_get_cmd_version(task, ver, 3); 227 printf("Kernel protocol version %d:%d:%d\n",ver[0], ver[1], ver[2]); 228 229 libdm_task_destroy(task); 230 return 0; 231 } 232 233 /* 234 * Get list of available targets from kernel and print them. 235 */ 236 static int 237 dmctl_get_targets(int argc __unused, char *argv[] __unused, libdm_task_t task) 238 { 239 libdm_cmd_t cmd; 240 libdm_iter_t iter; 241 libdm_target_t target; 242 uint32_t ver[3]; 243 244 if (libdm_task_run(task) != 0) 245 err(EXIT_FAILURE, "dmctl_get_targets: libdm_task_run failed."); 246 247 if ((cmd = libdm_task_get_cmd(task)) == NULL) 248 return ENOENT; 249 250 iter = libdm_cmd_iter_create(cmd); 251 252 while((target = libdm_cmd_get_target(iter)) != NULL){ 253 printf("Target name: %s\n", libdm_target_get_name(target)); 254 255 libdm_target_get_version(target, ver, sizeof(ver)); 256 printf("Target version %d.%d.%d\n\n", ver[0], ver[1], ver[2]); 257 258 libdm_target_destroy(target); 259 } 260 261 libdm_iter_destroy(iter); 262 libdm_cmd_destroy(cmd); 263 libdm_task_destroy(task); 264 265 return 0; 266 } 267 268 /* 269 * Create device with name used as second parameter. 270 * TODO: Support for UUIDs here. 271 */ 272 static int 273 dmctl_create_dev(int argc __unused, char *argv[] __unused, libdm_task_t task) 274 { 275 276 libdm_task_set_name(dvname, task); 277 278 if (libdm_task_run(task) != 0) 279 err(EXIT_FAILURE, "dmctl_create_dev: libdm_task_run failed."); 280 281 libdm_task_destroy(task); 282 return 0; 283 } 284 285 /* 286 * Get basic device info from device-mapper driver. 287 */ 288 static int 289 dmctl_get_device_info(int argc __unused, char *argv[] __unused, libdm_task_t task) 290 { 291 292 libdm_task_set_name(dvname, task); 293 294 if (libdm_task_run(task) != 0) 295 err(EXIT_FAILURE, "dmctl_get_device_info: libdm_task_run failed.\n"); 296 297 printf("Printing Device info for:\n"); 298 printf("Device name: \t\t%s\n", libdm_task_get_name(task)); 299 printf("Device uuid: \t\t%s\n", libdm_task_get_uuid(task)); 300 printf("Device minor: \t\t%d\n", libdm_task_get_minor(task)); 301 printf("Device target number: \t%d\n", libdm_task_get_target_num(task)); 302 printf("Device flags: \t\t%d\n", libdm_task_get_flags(task)); 303 304 libdm_task_destroy(task); 305 return 0; 306 } 307 308 /* 309 * List all device in device-mapper driver. 310 */ 311 static int 312 dmctl_list_devices(int argc __unused, char *argv[] __unused, libdm_task_t task) 313 { 314 libdm_cmd_t cmd; 315 libdm_iter_t iter; 316 libdm_dev_t dev; 317 318 if (libdm_task_run(task) != 0) 319 err(EXIT_FAILURE, "dmctl_list_devices: libdm_task_run failed."); 320 321 if ((cmd = libdm_task_get_cmd(task)) == NULL) 322 return ENOENT; 323 324 iter = libdm_cmd_iter_create(cmd); 325 326 while((dev = libdm_cmd_get_dev(iter)) != NULL){ 327 printf("Device name: %s, device minor: %d \n", 328 libdm_dev_get_name(dev), libdm_dev_get_minor(dev)); 329 libdm_dev_destroy(dev); 330 } 331 332 libdm_iter_destroy(iter); 333 libdm_cmd_destroy(cmd); 334 libdm_task_destroy(task); 335 336 return 0; 337 } 338 339 /* 340 * Rename device to new name 341 */ 342 static int 343 dmctl_dev_rename(int argc __unused, char *argv[], libdm_task_t task) 344 { 345 libdm_cmd_t cmd; 346 347 libdm_task_set_name(dvname, task); 348 349 cmd = libdm_cmd_create(); 350 libdm_dev_set_newname(argv[0], cmd); 351 libdm_task_set_cmd(cmd, task); 352 353 if (libdm_task_run(task) != 0) 354 err(EXIT_FAILURE, "dmctl_dev_rename: libdm_task_run failed."); 355 356 libdm_cmd_destroy(cmd); 357 libdm_task_destroy(task); 358 359 return 0; 360 } 361 362 /* 363 * Remove device from dm device list. 364 */ 365 static int 366 dmctl_dev_remove(int argc __unused, char *argv[] __unused, libdm_task_t task) 367 { 368 369 if (dvname == NULL) 370 return (ENOENT); 371 372 libdm_task_set_name(dvname, task); 373 374 if (libdm_task_run(task) != 0) 375 err(EXIT_FAILURE, "dmctl_dev_remove: libdm_task_run failed."); 376 377 libdm_task_destroy(task); 378 return 0; 379 } 380 381 /* 382 * Resume device which was suspended or created right now. 383 * Replace table in "active slot" witg table in "inactive slot". 384 */ 385 static int 386 dmctl_dev_resume(int argc __unused, char *argv[] __unused, libdm_task_t task) 387 { 388 389 libdm_task_set_name(dvname, task); 390 391 if (libdm_task_run(task) != 0) 392 err(EXIT_FAILURE, "dmctl_dev_resume: libdm_task_run failed."); 393 394 libdm_task_destroy(task); 395 return 0; 396 } 397 398 /* 399 * Resume device which was suspended or created right now. 400 * Replace table in "active slot" with table in "inactive slot". 401 */ 402 static int 403 dmctl_dev_suspend(int argc __unused, char *argv[] __unused, libdm_task_t task) 404 { 405 406 libdm_task_set_name(dvname, task); 407 libdm_task_set_suspend_flag(task); 408 409 if (libdm_task_run(task) != 0) 410 err(EXIT_FAILURE, "dmctl_dev_suspend: libdm_task_run failed."); 411 412 libdm_task_destroy(task); 413 return 0; 414 } 415 416 /* 417 * Get device dependiences from device-mapper. Device dependency is physical 418 * device on which dm device depends. 419 */ 420 static int 421 dmctl_dev_deps(int argc __unused, char *argv[] __unused, libdm_task_t task) 422 { 423 libdm_cmd_t cmd; 424 libdm_iter_t iter; 425 dev_t dev_deps; 426 427 libdm_task_set_name(dvname, task); 428 429 if (libdm_task_run(task) != 0) 430 err(EXIT_FAILURE, "dmctl_dev_deps: libdm_task_run failed."); 431 432 if ((cmd = libdm_task_get_cmd(task)) == NULL) 433 return ENOENT; 434 435 iter = libdm_cmd_iter_create(cmd); 436 437 printf("Device %s dependiences \n", dvname); 438 439 while((dev_deps = libdm_cmd_get_deps(iter)) != 0) 440 printf("major: %d minor: %d\n", major(dev_deps), minor(dev_deps)); 441 442 libdm_iter_destroy(iter); 443 libdm_cmd_destroy(cmd); 444 libdm_task_destroy(task); 445 446 return 0; 447 } 448 449 /* 450 * Reload device table to get new one to use. 451 */ 452 static int 453 dmctl_table_reload(int argc, char *argv[], libdm_task_t task) 454 { 455 libdm_cmd_t cmd; 456 libdm_table_t table; 457 458 char *params; 459 char *file_path; 460 char target[128]; 461 int len; 462 uint64_t start, length; 463 464 file_path = NULL; 465 params = NULL; 466 467 cmd = libdm_cmd_create(); 468 469 libdm_task_set_name(dvname, task); 470 471 if (argc != 0) 472 file_path = argv[0]; 473 474 while ((params = parse_stdin(file_path)) != NULL) { 475 table = libdm_table_create(); 476 477 sscanf(params, "%"PRIu64" %"PRIu64" %s %n", &start, &length, target, &len); 478 479 libdm_table_set_start(start, table); 480 libdm_table_set_length(length, table); 481 libdm_table_set_target(target, table); 482 libdm_table_set_params(params + len, table); 483 libdm_cmd_set_table(table, cmd); 484 485 libdm_table_destroy(table); 486 487 free(params); 488 } 489 490 libdm_task_set_cmd(cmd, task); 491 492 if (libdm_task_run(task) != 0) 493 err(EXIT_FAILURE, "libdm_task_run: from dmctl_table_reload failed."); 494 495 libdm_cmd_destroy(cmd); 496 libdm_task_destroy(task); 497 free(params); 498 499 return 0; 500 } 501 502 /* 503 * Get table status from device. 504 */ 505 static int 506 dmctl_table_status(int argc __unused, char *argv[] __unused, libdm_task_t task) 507 { 508 libdm_cmd_t cmd; 509 libdm_table_t table; 510 libdm_iter_t iter; 511 512 libdm_task_set_name(dvname, task); 513 libdm_task_set_status_flag(task); 514 515 if (libdm_task_run(task) != 0) 516 err(EXIT_FAILURE, "libdm_task_run"); 517 518 if ((cmd = libdm_task_get_cmd(task)) == NULL) 519 return ENOENT; 520 521 iter = libdm_cmd_iter_create(cmd); 522 523 printf("Getting device table for device %s\n", dvname); 524 525 while ((table = libdm_cmd_get_table(iter)) != NULL) { 526 printf("%10"PRIu64" %10"PRIu64" %s\n", 527 libdm_table_get_start(table), 528 libdm_table_get_length(table), 529 libdm_table_get_target(table)); 530 libdm_table_destroy(table); 531 } 532 533 libdm_iter_destroy(iter); 534 libdm_cmd_destroy(cmd); 535 libdm_task_destroy(task); 536 537 return 0; 538 } 539 540 void 541 usage(void) 542 { 543 int i; 544 545 (void)fprintf(stderr, "usage: %s command [dm device name]\n" 546 "Available commands are:\n ", getprogname()); 547 for (i = 0; commands[i].cmd_name != NULL; i++) 548 (void)fprintf(stderr, "\t%s\t%s\n", commands[i].cmd_name, commands[i].cmd_help); 549 exit(EXIT_FAILURE); 550 } 551 552 static char * 553 parse_stdin(char *file_path) 554 { 555 char *buf, *lbuf; 556 size_t len; 557 FILE *fp; 558 559 lbuf = NULL; 560 561 if (file_path) { 562 if ((fp = fopen(file_path, "r")) == NULL) 563 err(ENOENT, "Cannot open table file\n"); 564 } else 565 fp = stdin; 566 567 if ((buf = fgetln(fp, &len)) == NULL) 568 return NULL; 569 570 if (buf[len - 1] != '\n') 571 len++; 572 573 if ((lbuf = malloc(len)) == NULL) 574 err(EXIT_FAILURE, "malloc"); 575 576 memcpy(lbuf, buf, len); 577 lbuf[len - 1] = '\0'; 578 579 return lbuf; 580 } 581