xref: /freebsd/sbin/gvinum/gvinum.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
1 /*
2  *  Copyright (c) 2004 Lukas Ertl, 2005 Chris Jones
3  *  All rights reserved.
4  *
5  * Portions of this software were developed for the FreeBSD Project
6  * by Chris Jones thanks to the support of Google's Summer of Code
7  * program and mentoring by Lukas Ertl.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32 
33 #include <sys/param.h>
34 #include <sys/linker.h>
35 #include <sys/lock.h>
36 #include <sys/module.h>
37 #include <sys/mutex.h>
38 #include <sys/queue.h>
39 #include <sys/utsname.h>
40 
41 #include <geom/vinum/geom_vinum_var.h>
42 #include <geom/vinum/geom_vinum_share.h>
43 
44 #include <ctype.h>
45 #include <err.h>
46 #include <libgeom.h>
47 #include <stdint.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <paths.h>
51 #include <readline/readline.h>
52 #include <readline/history.h>
53 #include <unistd.h>
54 
55 #include "gvinum.h"
56 
57 void	gvinum_create(int, char **);
58 void	gvinum_help(void);
59 void	gvinum_list(int, char **);
60 void	gvinum_move(int, char **);
61 void	gvinum_parityop(int, char **, int);
62 void	gvinum_printconfig(int, char **);
63 void	gvinum_rename(int, char **);
64 void	gvinum_resetconfig(void);
65 void	gvinum_rm(int, char **);
66 void	gvinum_saveconfig(void);
67 void	gvinum_setstate(int, char **);
68 void	gvinum_start(int, char **);
69 void	gvinum_stop(int, char **);
70 void	parseline(int, char **);
71 void	printconfig(FILE *, char *);
72 
73 int
74 main(int argc, char **argv)
75 {
76 	int line, tokens;
77 	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
78 
79 	/* Load the module if necessary. */
80 	if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
81 		err(1, GVINUMMOD ": Kernel module not available");
82 
83 	/* Arguments given on the command line. */
84 	if (argc > 1) {
85 		argc--;
86 		argv++;
87 		parseline(argc, argv);
88 
89 	/* Interactive mode. */
90 	} else {
91 		for (;;) {
92 			inputline = readline("gvinum -> ");
93 			if (inputline == NULL) {
94 				if (ferror(stdin)) {
95 					err(1, "can't read input");
96 				} else {
97 					printf("\n");
98 					exit(0);
99 				}
100 			} else if (*inputline) {
101 				add_history(inputline);
102 				strcpy(buffer, inputline);
103 				free(inputline);
104 				line++;		    /* count the lines */
105 				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
106 				if (tokens)
107 					parseline(tokens, token);
108 			}
109 		}
110 	}
111 	exit(0);
112 }
113 
114 void
115 gvinum_create(int argc, char **argv)
116 {
117 	struct gctl_req *req;
118 	struct gv_drive *d;
119 	struct gv_plex *p;
120 	struct gv_sd *s;
121 	struct gv_volume *v;
122 	FILE *tmp;
123 	int drives, errors, fd, line, plexes, plex_in_volume;
124 	int sd_in_plex, status, subdisks, tokens, volumes;
125 	const char *errstr;
126 	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed;
127 	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
128 	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
129 
130 	if (argc == 2) {
131 		if ((tmp = fopen(argv[1], "r")) == NULL) {
132 			warn("can't open '%s' for reading", argv[1]);
133 			return;
134 		}
135 	} else {
136 		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
137 
138 		if ((fd = mkstemp(tmpfile)) == -1) {
139 			warn("temporary file not accessible");
140 			return;
141 		}
142 		if ((tmp = fdopen(fd, "w")) == NULL) {
143 			warn("can't open '%s' for writing", tmpfile);
144 			return;
145 		}
146 		printconfig(tmp, "# ");
147 		fclose(tmp);
148 
149 		ed = getenv("EDITOR");
150 		if (ed == NULL)
151 			ed = _PATH_VI;
152 
153 		snprintf(commandline, sizeof(commandline), "%s %s", ed,
154 		    tmpfile);
155 		status = system(commandline);
156 		if (status != 0) {
157 			warn("couldn't exec %s; status: %d", ed, status);
158 			return;
159 		}
160 
161 		if ((tmp = fopen(tmpfile, "r")) == NULL) {
162 			warn("can't open '%s' for reading", tmpfile);
163 			return;
164 		}
165 	}
166 
167 	req = gctl_get_handle();
168 	gctl_ro_param(req, "class", -1, "VINUM");
169 	gctl_ro_param(req, "verb", -1, "create");
170 
171 	drives = volumes = plexes = subdisks = 0;
172 	plex_in_volume = sd_in_plex = 0;
173 	errors = 0;
174 	line = 1;
175 	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
176 
177 		/* Skip empty lines and comments. */
178 		if (*buf == '\0' || *buf == '#') {
179 			line++;
180 			continue;
181 		}
182 
183 		/* Kill off the newline. */
184 		buf[strlen(buf) - 1] = '\0';
185 
186 		/*
187 		 * Copy the original input line in case we need it for error
188 		 * output.
189 		 */
190 		strncpy(original, buf, sizeof(buf));
191 
192 		tokens = gv_tokenize(buf, token, GV_MAXARGS);
193 		if (tokens <= 0) {
194 			line++;
195 			continue;
196 		}
197 
198 		/* Volume definition. */
199 		if (!strcmp(token[0], "volume")) {
200 			v = gv_new_volume(tokens, token);
201 			if (v == NULL) {
202 				warnx("line %d: invalid volume definition",
203 				    line);
204 				warnx("line %d: '%s'", line, original);
205 				errors++;
206 				line++;
207 				continue;
208 			}
209 
210 			/* Reset plex count for this volume. */
211 			plex_in_volume = 0;
212 
213 			/*
214 			 * Set default volume name for following plex
215 			 * definitions.
216 			 */
217 			strncpy(volume, v->name, sizeof(volume));
218 
219 			snprintf(buf1, sizeof(buf1), "volume%d", volumes);
220 			gctl_ro_param(req, buf1, sizeof(*v), v);
221 			volumes++;
222 
223 		/* Plex definition. */
224 		} else if (!strcmp(token[0], "plex")) {
225 			p = gv_new_plex(tokens, token);
226 			if (p == NULL) {
227 				warnx("line %d: invalid plex definition", line);
228 				warnx("line %d: '%s'", line, original);
229 				errors++;
230 				line++;
231 				continue;
232 			}
233 
234 			/* Reset subdisk count for this plex. */
235 			sd_in_plex = 0;
236 
237 			/* Default name. */
238 			if (strlen(p->name) == 0) {
239 				snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d",
240 				    volume, plex_in_volume++);
241 			}
242 
243 			/* Default volume. */
244 			if (strlen(p->volume) == 0) {
245 				snprintf(p->volume, GV_MAXVOLNAME, "%s",
246 				    volume);
247 			}
248 
249 			/*
250 			 * Set default plex name for following subdisk
251 			 * definitions.
252 			 */
253 			strncpy(plex, p->name, GV_MAXPLEXNAME);
254 
255 			snprintf(buf1, sizeof(buf1), "plex%d", plexes);
256 			gctl_ro_param(req, buf1, sizeof(*p), p);
257 			plexes++;
258 
259 		/* Subdisk definition. */
260 		} else if (!strcmp(token[0], "sd")) {
261 			s = gv_new_sd(tokens, token);
262 			if (s == NULL) {
263 				warnx("line %d: invalid subdisk "
264 				    "definition:", line);
265 				warnx("line %d: '%s'", line, original);
266 				errors++;
267 				line++;
268 				continue;
269 			}
270 
271 			/* Default name. */
272 			if (strlen(s->name) == 0) {
273 				snprintf(s->name, GV_MAXSDNAME, "%s.s%d",
274 				    plex, sd_in_plex++);
275 			}
276 
277 			/* Default plex. */
278 			if (strlen(s->plex) == 0)
279 				snprintf(s->plex, GV_MAXPLEXNAME, "%s", plex);
280 
281 			snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
282 			gctl_ro_param(req, buf1, sizeof(*s), s);
283 			subdisks++;
284 
285 		/* Subdisk definition. */
286 		} else if (!strcmp(token[0], "drive")) {
287 			d = gv_new_drive(tokens, token);
288 			if (d == NULL) {
289 				warnx("line %d: invalid drive definition:",
290 				    line);
291 				warnx("line %d: '%s'", line, original);
292 				errors++;
293 				line++;
294 				continue;
295 			}
296 
297 			snprintf(buf1, sizeof(buf1), "drive%d", drives);
298 			gctl_ro_param(req, buf1, sizeof(*d), d);
299 			drives++;
300 
301 		/* Everything else is bogus. */
302 		} else {
303 			warnx("line %d: invalid definition:", line);
304 			warnx("line %d: '%s'", line, original);
305 			errors++;
306 		}
307 		line++;
308 	}
309 
310 	fclose(tmp);
311 	unlink(tmpfile);
312 
313 	if (!errors && (volumes || plexes || subdisks || drives)) {
314 		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
315 		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
316 		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
317 		gctl_ro_param(req, "drives", sizeof(int), &drives);
318 		errstr = gctl_issue(req);
319 		if (errstr != NULL)
320 			warnx("create failed: %s", errstr);
321 	}
322 	gctl_free(req);
323 	gvinum_list(0, NULL);
324 }
325 
326 void
327 gvinum_help(void)
328 {
329 	printf("COMMANDS\n"
330 	    "checkparity [-f] plex\n"
331 	    "        Check the parity blocks of a RAID-5 plex.\n"
332 	    "create description-file\n"
333 	    "        Create as per description-file or open editor.\n"
334 	    "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
335 	    "        List information about specified objects.\n"
336 	    "ld [-r] [-v] [-V] [volume]\n"
337 	    "        List information about drives.\n"
338 	    "ls [-r] [-v] [-V] [subdisk]\n"
339 	    "        List information about subdisks.\n"
340 	    "lp [-r] [-v] [-V] [plex]\n"
341 	    "        List information about plexes.\n"
342 	    "lv [-r] [-v] [-V] [volume]\n"
343 	    "        List information about volumes.\n"
344 	    "move | mv -f drive object ...\n"
345 	    "        Move the object(s) to the specified drive.\n"
346 	    "quit    Exit the vinum program when running in interactive mode."
347 	    "  Nor-\n"
348 	    "        mally this would be done by entering the EOF character.\n"
349 	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
350 	    "        Change the name of the specified object.\n"
351 	    "rebuildparity plex [-f]\n"
352 	    "        Rebuild the parity blocks of a RAID-5 plex.\n"
353 	    "resetconfig\n"
354 	    "        Reset the complete gvinum configuration\n"
355 	    "rm [-r] volume | plex | subdisk | drive\n"
356 	    "        Remove an object.\n"
357 	    "saveconfig\n"
358 	    "        Save vinum configuration to disk after configuration"
359 	    " failures.\n"
360 	    "setstate [-f] state [volume | plex | subdisk | drive]\n"
361 	    "        Set state without influencing other objects, for"
362 	    " diagnostic pur-\n"
363 	    "        poses only.\n"
364 	    "start [-S size] volume | plex | subdisk\n"
365 	    "        Allow the system to access the objects.\n"
366 	);
367 
368 	return;
369 }
370 
371 void
372 gvinum_setstate(int argc, char **argv)
373 {
374 	struct gctl_req *req;
375 	int flags, i;
376 	const char *errstr;
377 
378 	flags = 0;
379 
380 	optreset = 1;
381 	optind = 1;
382 
383 	while ((i = getopt(argc, argv, "f")) != -1) {
384 		switch (i) {
385 		case 'f':
386 			flags |= GV_FLAG_F;
387 			break;
388 		case '?':
389 		default:
390 			warn("invalid flag: %c", i);
391 			return;
392 		}
393 	}
394 
395 	argc -= optind;
396 	argv += optind;
397 
398 	if (argc != 2) {
399 		warnx("usage: setstate [-f] <state> <obj>");
400 		return;
401 	}
402 
403 	/*
404 	 * XXX: This hack is needed to avoid tripping over (now) invalid
405 	 * 'classic' vinum states and will go away later.
406 	 */
407 	if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
408 	    strcmp(argv[0], "stale")) {
409 		warnx("invalid state '%s'", argv[0]);
410 		return;
411 	}
412 
413 	req = gctl_get_handle();
414 	gctl_ro_param(req, "class", -1, "VINUM");
415 	gctl_ro_param(req, "verb", -1, "setstate");
416 	gctl_ro_param(req, "state", -1, argv[0]);
417 	gctl_ro_param(req, "object", -1, argv[1]);
418 	gctl_ro_param(req, "flags", sizeof(int), &flags);
419 
420 	errstr = gctl_issue(req);
421 	if (errstr != NULL)
422 		warnx("%s", errstr);
423 	gctl_free(req);
424 }
425 
426 void
427 gvinum_list(int argc, char **argv)
428 {
429 	struct gctl_req *req;
430 	int flags, i, j;
431 	const char *errstr;
432 	char buf[20], *cmd, config[GV_CFG_LEN + 1];
433 
434 	flags = 0;
435 	cmd = "list";
436 
437 	if (argc) {
438 		optreset = 1;
439 		optind = 1;
440 		cmd = argv[0];
441 		while ((j = getopt(argc, argv, "rsvV")) != -1) {
442 			switch (j) {
443 			case 'r':
444 				flags |= GV_FLAG_R;
445 				break;
446 			case 's':
447 				flags |= GV_FLAG_S;
448 				break;
449 			case 'v':
450 				flags |= GV_FLAG_V;
451 				break;
452 			case 'V':
453 				flags |= GV_FLAG_V;
454 				flags |= GV_FLAG_VV;
455 				break;
456 			case '?':
457 			default:
458 				return;
459 			}
460 		}
461 		argc -= optind;
462 		argv += optind;
463 
464 	}
465 
466 	req = gctl_get_handle();
467 	gctl_ro_param(req, "class", -1, "VINUM");
468 	gctl_ro_param(req, "verb", -1, "list");
469 	gctl_ro_param(req, "cmd", -1, cmd);
470 	gctl_ro_param(req, "argc", sizeof(int), &argc);
471 	gctl_ro_param(req, "flags", sizeof(int), &flags);
472 	gctl_rw_param(req, "config", sizeof(config), config);
473 	if (argc) {
474 		for (i = 0; i < argc; i++) {
475 			snprintf(buf, sizeof(buf), "argv%d", i);
476 			gctl_ro_param(req, buf, -1, argv[i]);
477 		}
478 	}
479 	errstr = gctl_issue(req);
480 	if (errstr != NULL) {
481 		warnx("can't get configuration: %s", errstr);
482 		gctl_free(req);
483 		return;
484 	}
485 
486 	printf("%s", config);
487 	gctl_free(req);
488 	return;
489 }
490 
491 /* Note that move is currently of form '[-r] target object [...]' */
492 void
493 gvinum_move(int argc, char **argv)
494 {
495 	struct gctl_req *req;
496 	const char *errstr;
497 	char buf[20];
498 	int flags, i, j;
499 
500 	flags = 0;
501 	if (argc) {
502 		optreset = 1;
503 		optind = 1;
504 		while ((j = getopt(argc, argv, "f")) != -1) {
505 			switch (j) {
506 			case 'f':
507 				flags |= GV_FLAG_F;
508 				break;
509 			case '?':
510 			default:
511 				return;
512 			}
513 		}
514 		argc -= optind;
515 		argv += optind;
516 	}
517 
518 	switch (argc) {
519 		case 0:
520 			warnx("no destination or object(s) to move specified");
521 			return;
522 		case 1:
523 			warnx("no object(s) to move specified");
524 			return;
525 		default:
526 			break;
527 	}
528 
529 	req = gctl_get_handle();
530 	gctl_ro_param(req, "class", -1, "VINUM");
531 	gctl_ro_param(req, "verb", -1, "move");
532 	gctl_ro_param(req, "argc", sizeof(int), &argc);
533 	gctl_ro_param(req, "flags", sizeof(int), &flags);
534 	gctl_ro_param(req, "destination", -1, argv[0]);
535 	for (i = 1; i < argc; i++) {
536 		snprintf(buf, sizeof(buf), "argv%d", i);
537 		gctl_ro_param(req, buf, -1, argv[i]);
538 	}
539 	errstr = gctl_issue(req);
540 	if (errstr != NULL)
541 		warnx("can't move object(s):  %s", errstr);
542 	gctl_free(req);
543 	return;
544 }
545 
546 void
547 gvinum_printconfig(int argc, char **argv)
548 {
549 	printconfig(stdout, "");
550 }
551 
552 void
553 gvinum_parityop(int argc, char **argv, int rebuild)
554 {
555 	struct gctl_req *req;
556 	int flags, i, rv;
557 	off_t offset;
558 	const char *errstr;
559 	char *op, *msg;
560 
561 	if (rebuild) {
562 		op = "rebuildparity";
563 		msg = "Rebuilding";
564 	} else {
565 		op = "checkparity";
566 		msg = "Checking";
567 	}
568 
569 	optreset = 1;
570 	optind = 1;
571 	flags = 0;
572 	while ((i = getopt(argc, argv, "fv")) != -1) {
573 		switch (i) {
574 		case 'f':
575 			flags |= GV_FLAG_F;
576 			break;
577 		case 'v':
578 			flags |= GV_FLAG_V;
579 			break;
580 		case '?':
581 		default:
582 			warnx("invalid flag '%c'", i);
583 			return;
584 		}
585 	}
586 	argc -= optind;
587 	argv += optind;
588 
589 	if (argc != 1) {
590 		warn("usage: %s [-f] [-v] <plex>", op);
591 		return;
592 	}
593 
594 	do {
595 		rv = 0;
596 		req = gctl_get_handle();
597 		gctl_ro_param(req, "class", -1, "VINUM");
598 		gctl_ro_param(req, "verb", -1, "parityop");
599 		gctl_ro_param(req, "flags", sizeof(int), &flags);
600 		gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
601 		gctl_rw_param(req, "rv", sizeof(int), &rv);
602 		gctl_rw_param(req, "offset", sizeof(off_t), &offset);
603 		gctl_ro_param(req, "plex", -1, argv[0]);
604 		errstr = gctl_issue(req);
605 		if (errstr) {
606 			warnx("%s\n", errstr);
607 			gctl_free(req);
608 			break;
609 		}
610 		gctl_free(req);
611 		if (flags & GV_FLAG_V) {
612 			printf("\r%s at %s ... ", msg,
613 			    gv_roughlength(offset, 1));
614 		}
615 		if (rv == 1) {
616 			printf("Parity incorrect at offset 0x%jx\n",
617 			    (intmax_t)offset);
618 			if (!rebuild)
619 				break;
620 		}
621 		fflush(stdout);
622 
623 		/* Clear the -f flag. */
624 		flags &= ~GV_FLAG_F;
625 	} while (rv >= 0);
626 
627 	if ((rv == 2) && (flags & GV_FLAG_V)) {
628 		if (rebuild)
629 			printf("Rebuilt parity on %s\n", argv[0]);
630 		else
631 			printf("%s has correct parity\n", argv[0]);
632 	}
633 }
634 
635 void
636 gvinum_rename(int argc, char **argv)
637 {
638 	struct gctl_req *req;
639 	const char *errstr;
640 	int flags, j;
641 
642 	flags = 0;
643 
644 	if (argc) {
645 		optreset = 1;
646 		optind = 1;
647 		while ((j = getopt(argc, argv, "r")) != -1) {
648 			switch (j) {
649 			case 'r':
650 				flags |= GV_FLAG_R;
651 				break;
652 			case '?':
653 			default:
654 				return;
655 			}
656 		}
657 		argc -= optind;
658 		argv += optind;
659 	}
660 
661 	switch (argc) {
662 		case 0:
663 			warnx("no object to rename specified");
664 			return;
665 		case 1:
666 			warnx("no new name specified");
667 			return;
668 		case 2:
669 			break;
670 		default:
671 			warnx("more than one new name specified");
672 			return;
673 	}
674 
675 	req = gctl_get_handle();
676 	gctl_ro_param(req, "class", -1, "VINUM");
677 	gctl_ro_param(req, "verb", -1, "rename");
678 	gctl_ro_param(req, "flags", sizeof(int), &flags);
679 	gctl_ro_param(req, "object", -1, argv[0]);
680 	gctl_ro_param(req, "newname", -1, argv[1]);
681 	errstr = gctl_issue(req);
682 	if (errstr != NULL)
683 		warnx("can't rename object:  %s", errstr);
684 	gctl_free(req);
685 	return;
686 }
687 
688 void
689 gvinum_rm(int argc, char **argv)
690 {
691 	struct gctl_req *req;
692 	int flags, i, j;
693 	const char *errstr;
694 	char buf[20], *cmd;
695 
696 	cmd = argv[0];
697 	flags = 0;
698 	optreset = 1;
699 	optind = 1;
700 	while ((j = getopt(argc, argv, "r")) != -1) {
701 		switch (j) {
702 		case 'r':
703 			flags |= GV_FLAG_R;
704 			break;
705 		case '?':
706 		default:
707 			return;
708 		}
709 	}
710 	argc -= optind;
711 	argv += optind;
712 
713 	req = gctl_get_handle();
714 	gctl_ro_param(req, "class", -1, "VINUM");
715 	gctl_ro_param(req, "verb", -1, "remove");
716 	gctl_ro_param(req, "argc", sizeof(int), &argc);
717 	gctl_ro_param(req, "flags", sizeof(int), &flags);
718 	if (argc) {
719 		for (i = 0; i < argc; i++) {
720 			snprintf(buf, sizeof(buf), "argv%d", i);
721 			gctl_ro_param(req, buf, -1, argv[i]);
722 		}
723 	}
724 	errstr = gctl_issue(req);
725 	if (errstr != NULL) {
726 		warnx("can't remove: %s", errstr);
727 		gctl_free(req);
728 		return;
729 	}
730 	gctl_free(req);
731 	gvinum_list(0, NULL);
732 }
733 
734 void
735 gvinum_resetconfig(void)
736 {
737 	struct gctl_req *req;
738 	const char *errstr;
739 	char reply[32];
740 
741 	if (!isatty(STDIN_FILENO)) {
742 		warn("Please enter this command from a tty device\n");
743 		return;
744 	}
745 	printf(" WARNING!  This command will completely wipe out your gvinum"
746 	    "configuration.\n"
747 	    " All data will be lost.  If you really want to do this,"
748 	    " enter the text\n\n"
749 	    " NO FUTURE\n"
750 	    " Enter text -> ");
751 	fgets(reply, sizeof(reply), stdin);
752 	if (strcmp(reply, "NO FUTURE\n")) {
753 		printf("\n No change\n");
754 		return;
755 	}
756 	req = gctl_get_handle();
757 	gctl_ro_param(req, "class", -1, "VINUM");
758 	gctl_ro_param(req, "verb", -1, "resetconfig");
759 	errstr = gctl_issue(req);
760 	if (errstr != NULL) {
761 		warnx("can't reset config: %s", errstr);
762 		gctl_free(req);
763 		return;
764 	}
765 	gctl_free(req);
766 	gvinum_list(0, NULL);
767 	printf("gvinum configuration obliterated\n");
768 }
769 
770 void
771 gvinum_saveconfig(void)
772 {
773 	struct gctl_req *req;
774 	const char *errstr;
775 
776 	req = gctl_get_handle();
777 	gctl_ro_param(req, "class", -1, "VINUM");
778 	gctl_ro_param(req, "verb", -1, "saveconfig");
779 	errstr = gctl_issue(req);
780 	if (errstr != NULL)
781 		warnx("can't save configuration: %s", errstr);
782 	gctl_free(req);
783 }
784 
785 void
786 gvinum_start(int argc, char **argv)
787 {
788 	struct gctl_req *req;
789 	int i, initsize, j;
790 	const char *errstr;
791 	char buf[20];
792 
793 	/* 'start' with no arguments is a no-op. */
794 	if (argc == 1)
795 		return;
796 
797 	initsize = 0;
798 
799 	optreset = 1;
800 	optind = 1;
801 	while ((j = getopt(argc, argv, "S")) != -1) {
802 		switch (j) {
803 		case 'S':
804 			initsize = atoi(optarg);
805 			break;
806 		case '?':
807 		default:
808 			return;
809 		}
810 	}
811 	argc -= optind;
812 	argv += optind;
813 
814 	if (!initsize)
815 		initsize = 512;
816 
817 	req = gctl_get_handle();
818 	gctl_ro_param(req, "class", -1, "VINUM");
819 	gctl_ro_param(req, "verb", -1, "start");
820 	gctl_ro_param(req, "argc", sizeof(int), &argc);
821 	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
822 	if (argc) {
823 		for (i = 0; i < argc; i++) {
824 			snprintf(buf, sizeof(buf), "argv%d", i);
825 			gctl_ro_param(req, buf, -1, argv[i]);
826 		}
827 	}
828 	errstr = gctl_issue(req);
829 	if (errstr != NULL) {
830 		warnx("can't start: %s", errstr);
831 		gctl_free(req);
832 		return;
833 	}
834 
835 	gctl_free(req);
836 	gvinum_list(0, NULL);
837 }
838 
839 void
840 gvinum_stop(int argc, char **argv)
841 {
842 	int fileid;
843 
844 	fileid = kldfind(GVINUMMOD);
845 	if (fileid == -1) {
846 		warn("cannot find " GVINUMMOD);
847 		return;
848 	}
849 	if (kldunload(fileid) != 0) {
850 		warn("cannot unload " GVINUMMOD);
851 		return;
852 	}
853 
854 	warnx(GVINUMMOD " unloaded");
855 	exit(0);
856 }
857 
858 void
859 parseline(int argc, char **argv)
860 {
861 	if (argc <= 0)
862 		return;
863 
864 	if (!strcmp(argv[0], "create"))
865 		gvinum_create(argc, argv);
866 	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
867 		exit(0);
868 	else if (!strcmp(argv[0], "help"))
869 		gvinum_help();
870 	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
871 		gvinum_list(argc, argv);
872 	else if (!strcmp(argv[0], "ld"))
873 		gvinum_list(argc, argv);
874 	else if (!strcmp(argv[0], "lp"))
875 		gvinum_list(argc, argv);
876 	else if (!strcmp(argv[0], "ls"))
877 		gvinum_list(argc, argv);
878 	else if (!strcmp(argv[0], "lv"))
879 		gvinum_list(argc, argv);
880 	else if (!strcmp(argv[0], "move"))
881 		gvinum_move(argc, argv);
882 	else if (!strcmp(argv[0], "mv"))
883 		gvinum_move(argc, argv);
884 	else if (!strcmp(argv[0], "printconfig"))
885 		gvinum_printconfig(argc, argv);
886 	else if (!strcmp(argv[0], "rename"))
887 		gvinum_rename(argc, argv);
888 	else if (!strcmp(argv[0], "resetconfig"))
889 		gvinum_resetconfig();
890 	else if (!strcmp(argv[0], "rm"))
891 		gvinum_rm(argc, argv);
892 	else if (!strcmp(argv[0], "saveconfig"))
893 		gvinum_saveconfig();
894 	else if (!strcmp(argv[0], "setstate"))
895 		gvinum_setstate(argc, argv);
896 	else if (!strcmp(argv[0], "start"))
897 		gvinum_start(argc, argv);
898 	else if (!strcmp(argv[0], "stop"))
899 		gvinum_stop(argc, argv);
900 	else if (!strcmp(argv[0], "checkparity"))
901 		gvinum_parityop(argc, argv, 0);
902 	else if (!strcmp(argv[0], "rebuildparity"))
903 		gvinum_parityop(argc, argv, 1);
904 	else
905 		printf("unknown command '%s'\n", argv[0]);
906 
907 	return;
908 }
909 
910 /*
911  * The guts of printconfig.  This is called from gvinum_printconfig and from
912  * gvinum_create when called without an argument, in order to give the user
913  * something to edit.
914  */
915 void
916 printconfig(FILE *of, char *comment)
917 {
918 	struct gctl_req *req;
919 	struct utsname uname_s;
920 	const char *errstr;
921 	time_t now;
922 	char buf[GV_CFG_LEN + 1];
923 
924 	uname(&uname_s);
925 	time(&now);
926 
927 	req = gctl_get_handle();
928 	gctl_ro_param(req, "class", -1, "VINUM");
929 	gctl_ro_param(req, "verb", -1, "getconfig");
930 	gctl_ro_param(req, "comment", -1, comment);
931 	gctl_rw_param(req, "config", sizeof(buf), buf);
932 	errstr = gctl_issue(req);
933 	if (errstr != NULL) {
934 		warnx("can't get configuration: %s", errstr);
935 		return;
936 	}
937 	gctl_free(req);
938 
939 	fprintf(of, "# Vinum configuration of %s, saved at %s",
940 	    uname_s.nodename,
941 	    ctime(&now));
942 
943 	if (*comment != '\0')
944 	    fprintf(of, "# Current configuration:\n");
945 
946 	fprintf(of, buf);
947 }
948