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 possibility 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 static const char *dvname; /* device name */
70 static const char *cmdname; /* command user issued */
71
72 static char * parse_stdin(char *);
73
74 static int dmctl_get_version(int, char *[], libdm_task_t);
75 static int dmctl_get_targets(int, char *[], libdm_task_t);
76 static int dmctl_get_device_info(int, char *[], libdm_task_t);
77 static int dmctl_create_dev(int, char *[], libdm_task_t);
78 static int dmctl_dev_rename(int, char *[], libdm_task_t);
79 static int dmctl_dev_remove(int, char *[], libdm_task_t);
80 static int dmctl_dev_resume(int, char *[], libdm_task_t);
81 static int dmctl_dev_suspend(int, char *[], libdm_task_t);
82 static int dmctl_dev_deps(int, char *[], libdm_task_t);
83 static int dmctl_list_devices(int, char *[], libdm_task_t);
84 static int dmctl_table_reload(int, char *[], libdm_task_t);
85 static int dmctl_table_status(int, char *[], libdm_task_t);
86 __dead static void usage(void);
87
88 static 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 dependencies 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
147 int
main(int argc,char * argv[])148 main(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 */
213 static int
dmctl_get_version(int argc __unused,char * argv[]__unused,libdm_task_t task)214 dmctl_get_version(int argc __unused, char *argv[] __unused, libdm_task_t task)
215 {
216 uint32_t ver[3];
217
218 (void)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 (void)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 */
235 static int
dmctl_get_targets(int argc __unused,char * argv[]__unused,libdm_task_t task)236 dmctl_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 */
271 static int
dmctl_create_dev(int argc __unused,char * argv[]__unused,libdm_task_t task)272 dmctl_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 */
287 static int
dmctl_get_device_info(int argc __unused,char * argv[]__unused,libdm_task_t task)288 dmctl_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, "%s: libdm_task_run failed", __func__);
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 */
310 static int
dmctl_list_devices(int argc __unused,char * argv[]__unused,libdm_task_t task)311 dmctl_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 */
341 static int
dmctl_dev_rename(int argc __unused,char * argv[],libdm_task_t task)342 dmctl_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 */
364 static int
dmctl_dev_remove(int argc __unused,char * argv[]__unused,libdm_task_t task)365 dmctl_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" with table in "inactive slot".
383 */
384 static int
dmctl_dev_resume(int argc __unused,char * argv[]__unused,libdm_task_t task)385 dmctl_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 */
401 static int
dmctl_dev_suspend(int argc __unused,char * argv[]__unused,libdm_task_t task)402 dmctl_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 dependencies from device-mapper. Device dependency is physical
417 * device on which dm device depends.
418 */
419 static int
dmctl_dev_deps(int argc __unused,char * argv[]__unused,libdm_task_t task)420 dmctl_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 dependencies \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 */
451 static int
dmctl_table_reload(int argc,char * argv[],libdm_task_t task)452 dmctl_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 */
504 static int
dmctl_table_status(int argc __unused,char * argv[]__unused,libdm_task_t task)505 dmctl_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
539 static void
usage(void)540 usage(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
551 static char *
parse_stdin(char * file_path)552 parse_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");
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