dmctl.c revision 1.1 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, "%llu %llu %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