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