gvinum.c revision 157052
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: head/sbin/gvinum/gvinum.c 157052 2006-03-23 19:58:43Z le $
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
57void	gvinum_create(int, char **);
58void	gvinum_help(void);
59void	gvinum_list(int, char **);
60void	gvinum_move(int, char **);
61void	gvinum_parityop(int, char **, int);
62void	gvinum_printconfig(int, char **);
63void	gvinum_rename(int, char **);
64void	gvinum_resetconfig(void);
65void	gvinum_rm(int, char **);
66void	gvinum_saveconfig(void);
67void	gvinum_setstate(int, char **);
68void	gvinum_start(int, char **);
69void	gvinum_stop(int, char **);
70void	parseline(int, char **);
71void	printconfig(FILE *, char *);
72
73int
74main(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
114void
115gvinum_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
326void
327gvinum_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
371void
372gvinum_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
426void
427gvinum_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 [...]' */
492void
493gvinum_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
546void
547gvinum_printconfig(int argc, char **argv)
548{
549	printconfig(stdout, "");
550}
551
552void
553gvinum_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
635void
636gvinum_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
688void
689gvinum_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
734void
735gvinum_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
770void
771gvinum_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
785void
786gvinum_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
839void
840gvinum_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
858void
859parseline(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 */
915void
916printconfig(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