xref: /netbsd-src/sbin/dmctl/dmctl.c (revision 1b9578b8c2c1f848eeb16dabbfd7d1f0d9fdefbd)
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