gvinum.c revision 133097
1130391Sle/*
2130391Sle *  Copyright (c) 2004 Lukas Ertl
3130391Sle *  All rights reserved.
4130391Sle *
5130391Sle * Redistribution and use in source and binary forms, with or without
6130391Sle * modification, are permitted provided that the following conditions
7130391Sle * are met:
8130391Sle * 1. Redistributions of source code must retain the above copyright
9130391Sle *    notice, this list of conditions and the following disclaimer.
10130391Sle * 2. Redistributions in binary form must reproduce the above copyright
11130391Sle *    notice, this list of conditions and the following disclaimer in the
12130391Sle *    documentation and/or other materials provided with the distribution.
13130391Sle *
14130391Sle * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15130391Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16130391Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17130391Sle * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
18130391Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19130391Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20130391Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21130391Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22130391Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23130391Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24130391Sle * SUCH DAMAGE.
25130391Sle *
26130391Sle * $FreeBSD: head/sbin/gvinum/gvinum.c 133097 2004-08-04 00:23:00Z le $
27130391Sle */
28130391Sle
29130391Sle#include <sys/param.h>
30130391Sle#include <sys/linker.h>
31130391Sle#include <sys/lock.h>
32130391Sle#include <sys/module.h>
33130391Sle#include <sys/mutex.h>
34130391Sle#include <sys/queue.h>
35130391Sle#include <sys/utsname.h>
36130391Sle
37130391Sle#include <geom/vinum/geom_vinum_var.h>
38130391Sle#include <geom/vinum/geom_vinum_share.h>
39130391Sle
40130391Sle#include <ctype.h>
41130391Sle#include <err.h>
42130391Sle#include <libgeom.h>
43130391Sle#include <stdint.h>
44130391Sle#include <stdio.h>
45130391Sle#include <stdlib.h>
46130391Sle#include <paths.h>
47130391Sle#include <readline/readline.h>
48130391Sle#include <readline/history.h>
49130391Sle#include <unistd.h>
50130391Sle
51130391Sle#include "gvinum.h"
52130391Sle
53130391Slevoid	gvinum_cancelinit(int, char **);
54130391Slevoid	gvinum_create(int, char **);
55130391Slevoid	gvinum_help(void);
56130391Slevoid	gvinum_init(int, char **);
57130391Slevoid	gvinum_list(int, char **);
58130391Slevoid	gvinum_printconfig(int, char **);
59130391Slevoid	gvinum_rm(int, char **);
60130391Slevoid	gvinum_saveconfig(void);
61130391Slevoid	gvinum_start(int, char **);
62130391Slevoid	gvinum_stop(int, char **);
63130391Slevoid	parseline(int, char **);
64130391Slevoid	printconfig(FILE *, char *);
65130391Sle
66130391Sleint
67130391Slemain(int argc, char **argv)
68130391Sle{
69130391Sle	int line, tokens;
70130391Sle	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
71130391Sle
72130391Sle	/* Load the module if necessary. */
73130391Sle	if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
74130391Sle		err(1, GVINUMMOD ": Kernel module not available");
75130391Sle
76130391Sle	/* Arguments given on the command line. */
77130391Sle	if (argc > 1) {
78130391Sle		argc--;
79130391Sle		argv++;
80130391Sle		parseline(argc, argv);
81130391Sle
82130391Sle	/* Interactive mode. */
83130391Sle	} else {
84130391Sle		for (;;) {
85130391Sle			inputline = readline("gvinum -> ");
86130391Sle			if (inputline == NULL) {
87130391Sle				if (ferror(stdin)) {
88130391Sle					err(1, "can't read input");
89130391Sle				} else {
90130391Sle					printf("\n");
91130391Sle					exit(0);
92130391Sle				}
93130391Sle			} else if (*inputline) {
94130391Sle				add_history(inputline);
95130391Sle				strcpy(buffer, inputline);
96130391Sle				free(inputline);
97130391Sle				line++;		    /* count the lines */
98130391Sle				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
99130391Sle				if (tokens)
100130391Sle					parseline(tokens, token);
101130391Sle			}
102130391Sle		}
103130391Sle	}
104130391Sle	exit(0);
105130391Sle}
106130391Sle
107130391Slevoid
108130391Slegvinum_cancelinit(int argc, char **argv)
109130391Sle{
110130391Sle	struct gctl_req *req;
111130391Sle	int i;
112130391Sle	const char *errstr;
113130391Sle	char buf[20];
114130391Sle
115130391Sle	if (argc == 1)
116130391Sle		return;
117130391Sle
118130391Sle	argc--;
119130391Sle	argv++;
120130391Sle
121130391Sle	req = gctl_get_handle();
122130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
123130391Sle	gctl_ro_param(req, "verb", -1, "cancelinit");
124130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
125130391Sle	if (argc) {
126130391Sle		for (i = 0; i < argc; i++) {
127130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
128130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
129130391Sle		}
130130391Sle	}
131130391Sle	errstr = gctl_issue(req);
132130391Sle	if (errstr != NULL) {
133130391Sle		warnx("can't init: %s", errstr);
134130391Sle		gctl_free(req);
135130391Sle		return;
136130391Sle	}
137130391Sle
138130391Sle	gctl_free(req);
139130391Sle	gvinum_list(0, NULL);
140130391Sle}
141130391Sle
142130391Slevoid
143130391Slegvinum_create(int argc, char **argv)
144130391Sle{
145130391Sle	struct gctl_req *req;
146130391Sle	struct gv_drive *d;
147130391Sle	struct gv_plex *p;
148130391Sle	struct gv_sd *s;
149130391Sle	struct gv_volume *v;
150130391Sle	FILE *tmp;
151130391Sle	int drives, errors, fd, line, plexes, plex_in_volume;
152130391Sle	int sd_in_plex, status, subdisks, tokens, volumes;
153130391Sle	const char *errstr;
154130391Sle	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed;
155130391Sle	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
156130391Sle	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
157130391Sle
158133097Sle	if (argc == 2) {
159133097Sle		if ((tmp = fopen(argv[1], "r")) == NULL) {
160133097Sle			warn("can't open '%s' for reading", argv[1]);
161133097Sle			return;
162133097Sle		}
163133097Sle	} else {
164133097Sle		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
165133097Sle
166133097Sle		if ((fd = mkstemp(tmpfile)) == -1) {
167133097Sle			warn("temporary file not accessible");
168133097Sle			return;
169133097Sle		}
170133097Sle		if ((tmp = fdopen(fd, "w")) == NULL) {
171133097Sle			warn("can't open '%s' for writing", tmpfile);
172133097Sle			return;
173133097Sle		}
174133097Sle		printconfig(tmp, "# ");
175133097Sle		fclose(tmp);
176133097Sle
177133097Sle		ed = getenv("EDITOR");
178133097Sle		if (ed == NULL)
179133097Sle			ed = _PATH_VI;
180133097Sle
181133097Sle		snprintf(commandline, sizeof(commandline), "%s %s", ed,
182133097Sle		    tmpfile);
183133097Sle		status = system(commandline);
184133097Sle		if (status != 0) {
185133097Sle			warn("couldn't exec %s; status: %d", ed, status);
186133097Sle			return;
187133097Sle		}
188133097Sle
189133097Sle		if ((tmp = fopen(tmpfile, "r")) == NULL) {
190133097Sle			warn("can't open '%s' for reading", tmpfile);
191133097Sle			return;
192133097Sle		}
193130391Sle	}
194130391Sle
195130391Sle	req = gctl_get_handle();
196130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
197130391Sle	gctl_ro_param(req, "verb", -1, "create");
198130391Sle
199130391Sle	drives = volumes = plexes = subdisks = 0;
200130391Sle	plex_in_volume = sd_in_plex = 0;
201130391Sle	errors = 0;
202130391Sle	line = 1;
203130391Sle	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
204130391Sle
205130391Sle		/* Skip empty lines and comments. */
206130391Sle		if (*buf == '\0' || *buf == '#') {
207130391Sle			line++;
208130391Sle			continue;
209130391Sle		}
210130391Sle
211130391Sle		/* Kill off the newline. */
212130391Sle		buf[strlen(buf) - 1] = '\0';
213130391Sle
214130391Sle		/*
215130391Sle		 * Copy the original input line in case we need it for error
216130391Sle		 * output.
217130391Sle		 */
218130391Sle		strncpy(original, buf, sizeof(buf));
219130391Sle
220130391Sle		tokens = gv_tokenize(buf, token, GV_MAXARGS);
221130391Sle
222130391Sle		if (tokens > 0) {
223130391Sle			/* Volume definition. */
224130391Sle			if (!strcmp(token[0], "volume")) {
225130391Sle				v = gv_new_volume(tokens, token);
226130391Sle				if (v == NULL) {
227130391Sle					warnx("line %d: invalid volume "
228130391Sle					    "definition", line);
229130391Sle					warnx("line %d: '%s'", line, original);
230130391Sle					errors++;
231130391Sle				} else {
232130391Sle					/* Reset plex count for this volume. */
233130391Sle					plex_in_volume = 0;
234130391Sle
235130391Sle					/*
236130391Sle					 * Set default volume name for
237130391Sle					 * following plex definitions.
238130391Sle					 */
239130391Sle					strncpy(volume, v->name,
240130391Sle					    sizeof(volume));
241130391Sle
242130391Sle					snprintf(buf1, sizeof(buf1), "volume%d",
243130391Sle					    volumes);
244130391Sle					gctl_ro_param(req, buf1, sizeof(*v), v);
245130391Sle					volumes++;
246130391Sle				}
247130391Sle
248130391Sle			/* Plex definition. */
249130391Sle			} else if (!strcmp(token[0], "plex")) {
250130391Sle				p = gv_new_plex(tokens, token);
251130391Sle				if (p == NULL) {
252130391Sle					warnx("line %d: invalid plex "
253130391Sle					    "definition", line);
254130391Sle					warnx("line %d: '%s'", line, original);
255130391Sle					errors++;
256130391Sle				} else {
257130391Sle					/* Reset subdisk count for this plex. */
258130391Sle					sd_in_plex = 0;
259130391Sle
260130391Sle					/* Default name. */
261130391Sle					if (strlen(p->name) == 0) {
262130391Sle						snprintf(p->name,
263130391Sle						    GV_MAXPLEXNAME,
264130391Sle						    "%s.p%d", volume,
265130391Sle						    plex_in_volume++);
266130391Sle					}
267130391Sle
268130391Sle					/* Default volume. */
269130391Sle					if (strlen(p->volume) == 0) {
270130391Sle						snprintf(p->volume,
271130391Sle						    GV_MAXVOLNAME, "%s",
272130391Sle						    volume);
273130391Sle					}
274130391Sle
275130391Sle					/*
276130391Sle					 * Set default plex name for following
277130391Sle					 * subdisk definitions.
278130391Sle					 */
279130391Sle					strncpy(plex, p->name, GV_MAXPLEXNAME);
280130391Sle
281130391Sle					snprintf(buf1, sizeof(buf1), "plex%d",
282130391Sle					    plexes);
283130391Sle					gctl_ro_param(req, buf1, sizeof(*p), p);
284130391Sle					plexes++;
285130391Sle				}
286130391Sle
287130391Sle			/* Subdisk definition. */
288130391Sle			} else if (!strcmp(token[0], "sd")) {
289130391Sle				s = gv_new_sd(tokens, token);
290130391Sle				if (s == NULL) {
291130391Sle					warnx("line %d: invalid subdisk "
292130391Sle					    "definition:", line);
293130391Sle					warnx("line %d: '%s'", line, original);
294130391Sle					errors++;
295130391Sle				} else {
296130391Sle					/* Default name. */
297130391Sle					if (strlen(s->name) == 0) {
298130391Sle						snprintf(s->name, GV_MAXSDNAME,
299130391Sle						    "%s.s%d", plex,
300130391Sle						    sd_in_plex++);
301130391Sle					}
302130391Sle
303130391Sle					/* Default plex. */
304130391Sle					if (strlen(s->plex) == 0) {
305130391Sle						snprintf(s->plex,
306130391Sle						    GV_MAXPLEXNAME, "%s", plex);
307130391Sle					}
308130391Sle
309130391Sle					snprintf(buf1, sizeof(buf1), "sd%d",
310130391Sle					    subdisks);
311130391Sle					gctl_ro_param(req, buf1, sizeof(*s), s);
312130391Sle					subdisks++;
313130391Sle				}
314130391Sle
315130391Sle			/* Subdisk definition. */
316130391Sle			} else if (!strcmp(token[0], "drive")) {
317130391Sle				d = gv_new_drive(tokens, token);
318130391Sle				if (d == NULL) {
319130391Sle					warnx("line %d: invalid drive "
320130391Sle					    "definition:", line);
321130391Sle					warnx("line %d: '%s'", line, original);
322130391Sle					errors++;
323130391Sle				} else {
324130391Sle					snprintf(buf1, sizeof(buf1), "drive%d",
325130391Sle					    drives);
326130391Sle					gctl_ro_param(req, buf1, sizeof(*d), d);
327130391Sle					drives++;
328130391Sle				}
329130391Sle
330130391Sle			/* Everything else is bogus. */
331130391Sle			} else {
332130391Sle				warnx("line %d: invalid definition:", line);
333130391Sle				warnx("line %d: '%s'", line, original);
334130391Sle				errors++;
335130391Sle			}
336130391Sle		}
337130391Sle		line++;
338130391Sle	}
339130391Sle
340130391Sle	fclose(tmp);
341130391Sle	unlink(tmpfile);
342130391Sle
343130391Sle	if (!errors && (volumes || plexes || subdisks || drives)) {
344130391Sle		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
345130391Sle		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
346130391Sle		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
347130391Sle		gctl_ro_param(req, "drives", sizeof(int), &drives);
348130391Sle		errstr = gctl_issue(req);
349130391Sle		if (errstr != NULL)
350130391Sle			warnx("create failed: %s", errstr);
351130391Sle	}
352130391Sle	gctl_free(req);
353130391Sle	gvinum_list(0, NULL);
354130391Sle}
355130391Sle
356130391Slevoid
357130391Slegvinum_help(void)
358130391Sle{
359130391Sle	printf("COMMANDS\n"
360130391Sle	    "attach plex volume [rename]\n"
361130391Sle	    "attach subdisk plex [offset] [rename]\n"
362130391Sle	    "        Attach a plex to a volume, or a subdisk to a plex.\n"
363130391Sle	    "checkparity plex [-f] [-v]\n"
364130391Sle	    "        Check the parity blocks of a RAID-4 or RAID-5 plex.\n"
365130391Sle	    "concat [-f] [-n name] [-v] drives\n"
366130391Sle	    "        Create a concatenated volume from the specified drives.\n"
367130391Sle	    "create [-f] description-file\n"
368130391Sle	    "        Create a volume as described in description-file.\n"
369130391Sle	    "detach [-f] [plex | subdisk]\n"
370130391Sle	    "        Detach a plex or subdisk from the volume or plex to"
371130391Sle	    "which it is\n"
372130391Sle	    "        attached.\n"
373130391Sle	    "dumpconfig [drive ...]\n"
374130391Sle	    "        List the configuration information stored on the"
375130391Sle	    " specified\n"
376130391Sle	    "        drives, or all drives in the system if no drive names"
377130391Sle	    " are speci-\n"
378130391Sle	    "        fied.\n"
379130391Sle	    "info [-v] [-V]\n"
380130391Sle	    "        List information about volume manager state.\n"
381130391Sle	    "init [-S size] [-w] plex | subdisk\n"
382130391Sle	    "        Initialize the contents of a subdisk or all the subdisks"
383130391Sle	    " of a\n"
384130391Sle	    "        plex to all zeros.\n"
385130391Sle	    "label volume\n"
386130391Sle	    "        Create a volume label.\n"
387130391Sle	    "l | list [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n"
388130391Sle	    "        List information about specified objects.\n"
389130391Sle	    "ld [-r] [-s] [-v] [-V] [volume]\n"
390130391Sle	    "        List information about drives.\n"
391130391Sle	    "ls [-r] [-s] [-v] [-V] [subdisk]\n"
392130391Sle	    "        List information about subdisks.\n"
393130391Sle	    "lp [-r] [-s] [-v] [-V] [plex]\n"
394130391Sle	    "        List information about plexes.\n"
395130391Sle	    "lv [-r] [-s] [-v] [-V] [volume]\n"
396130391Sle	    "        List information about volumes.\n"
397130391Sle	    "mirror [-f] [-n name] [-s] [-v] drives\n"
398130391Sle	    "        Create a mirrored volume from the specified drives.\n"
399130391Sle	    "move | mv -f drive object ...\n"
400130391Sle	    "        Move the object(s) to the specified drive.\n"
401130391Sle	    "printconfig [file]\n"
402130391Sle	    "        Write a copy of the current configuration to file.\n"
403130391Sle	    "quit    Exit the vinum program when running in interactive mode."
404130391Sle	    "  Nor-\n"
405130391Sle	    "        mally this would be done by entering the EOF character.\n"
406130391Sle	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
407130391Sle	    "        Change the name of the specified object.\n"
408130391Sle	    "rebuildparity plex [-f] [-v] [-V]\n"
409130391Sle	    "        Rebuild the parity blocks of a RAID-4 or RAID-5 plex.\n"
410130391Sle	    "resetconfig\n"
411130391Sle	    "        Reset the complete vinum configuration.\n"
412130391Sle	    "rm [-f] [-r] volume | plex | subdisk\n"
413130391Sle	    "        Remove an object.\n"
414130391Sle	    "saveconfig\n"
415130391Sle	    "        Save vinum configuration to disk after configuration"
416130391Sle	    " failures.\n"
417130391Sle	    "setstate state [volume | plex | subdisk | drive]\n"
418130391Sle	    "        Set state without influencing other objects, for"
419130391Sle	    " diagnostic pur-\n"
420130391Sle	    "        poses only.\n"
421130391Sle	    "start [-i interval] [-S size] [-w] volume | plex | subdisk\n"
422130391Sle	    "        Allow the system to access the objects.\n"
423130391Sle	    "stop [-f] [volume | plex | subdisk]\n"
424130391Sle	    "        Terminate access to the objects, or stop vinum if no"
425130391Sle	    " parameters\n"
426130391Sle	    "        are specified.\n"
427130391Sle	    "stripe [-f] [-n name] [-v] drives\n"
428130391Sle	    "        Create a striped volume from the specified drives.\n"
429130391Sle	);
430130391Sle
431130391Sle	return;
432130391Sle}
433130391Sle
434130391Slevoid
435130391Slegvinum_init(int argc, char **argv)
436130391Sle{
437130391Sle	struct gctl_req *req;
438130391Sle	int i, initsize, j;
439130391Sle	const char *errstr;
440130391Sle	char buf[20];
441130391Sle
442130391Sle	initsize = 0;
443130391Sle	optreset = 1;
444130391Sle	optind = 1;
445130391Sle	while ((j = getopt(argc, argv, "S")) != -1) {
446130391Sle		switch (j) {
447130391Sle		case 'S':
448130391Sle			initsize = atoi(optarg);
449130391Sle			break;
450130391Sle		case '?':
451130391Sle		default:
452130391Sle			return;
453130391Sle		}
454130391Sle	}
455130391Sle	argc -= optind;
456130391Sle	argv += optind;
457130391Sle
458130391Sle	if (!initsize)
459130391Sle		initsize = 512;
460130391Sle
461130391Sle	req = gctl_get_handle();
462130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
463130391Sle	gctl_ro_param(req, "verb", -1, "init");
464130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
465130391Sle	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
466130391Sle	if (argc) {
467130391Sle		for (i = 0; i < argc; i++) {
468130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
469130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
470130391Sle		}
471130391Sle	}
472130391Sle	errstr = gctl_issue(req);
473130391Sle	if (errstr != NULL) {
474130391Sle		warnx("can't init: %s", errstr);
475130391Sle		gctl_free(req);
476130391Sle		return;
477130391Sle	}
478130391Sle
479130391Sle	gctl_free(req);
480130391Sle	gvinum_list(0, NULL);
481130391Sle}
482130391Sle
483130391Slevoid
484130391Slegvinum_list(int argc, char **argv)
485130391Sle{
486130391Sle	struct gctl_req *req;
487130391Sle	int flags, i, j;
488130391Sle	const char *errstr;
489130391Sle	char buf[20], *cmd, config[GV_CFG_LEN + 1];
490130391Sle
491130391Sle	flags = 0;
492130391Sle	cmd = "list";
493130391Sle
494130391Sle	if (argc) {
495130391Sle		optreset = 1;
496130391Sle		optind = 1;
497130391Sle		cmd = argv[0];
498130391Sle		while ((j = getopt(argc, argv, "rsvV")) != -1) {
499130391Sle			switch (j) {
500130391Sle			case 'r':
501130391Sle				flags |= GV_FLAG_R;
502130391Sle				break;
503130391Sle			case 's':
504130391Sle				flags |= GV_FLAG_S;
505130391Sle				break;
506130391Sle			case 'v':
507130391Sle				flags |= GV_FLAG_V;
508130391Sle				break;
509130391Sle			case 'V':
510130391Sle				flags |= GV_FLAG_V;
511130391Sle				flags |= GV_FLAG_VV;
512130391Sle				break;
513130391Sle			case '?':
514130391Sle			default:
515130391Sle				return;
516130391Sle			}
517130391Sle		}
518130391Sle		argc -= optind;
519130391Sle		argv += optind;
520130391Sle
521130391Sle	}
522130391Sle
523130391Sle	req = gctl_get_handle();
524130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
525130391Sle	gctl_ro_param(req, "verb", -1, "list");
526130391Sle	gctl_ro_param(req, "cmd", -1, cmd);
527130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
528130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
529130391Sle	gctl_rw_param(req, "config", sizeof(config), config);
530130391Sle	if (argc) {
531130391Sle		for (i = 0; i < argc; i++) {
532130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
533130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
534130391Sle		}
535130391Sle	}
536130391Sle	errstr = gctl_issue(req);
537130391Sle	if (errstr != NULL) {
538130391Sle		warnx("can't get configuration: %s", errstr);
539130391Sle		gctl_free(req);
540130391Sle		return;
541130391Sle	}
542130391Sle
543130391Sle	printf("%s", config);
544130391Sle	gctl_free(req);
545130391Sle	return;
546130391Sle}
547130391Sle
548130391Slevoid
549130391Slegvinum_printconfig(int argc, char **argv)
550130391Sle{
551130391Sle	printconfig(stdout, "");
552130391Sle}
553130391Sle
554130391Slevoid
555130391Slegvinum_rm(int argc, char **argv)
556130391Sle{
557130391Sle	struct gctl_req *req;
558130391Sle	int flags, i, j;
559130391Sle	const char *errstr;
560130391Sle	char buf[20], *cmd;
561130391Sle
562130391Sle	cmd = argv[0];
563130391Sle	flags = 0;
564130391Sle	optreset = 1;
565130391Sle	optind = 1;
566130391Sle	while ((j = getopt(argc, argv, "r")) != -1) {
567130391Sle		switch (j) {
568130391Sle		case 'r':
569130391Sle			flags |= GV_FLAG_R;
570130391Sle			break;
571130391Sle		case '?':
572130391Sle		default:
573130391Sle			return;
574130391Sle		}
575130391Sle	}
576130391Sle	argc -= optind;
577130391Sle	argv += optind;
578130391Sle
579130391Sle	req = gctl_get_handle();
580130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
581130391Sle	gctl_ro_param(req, "verb", -1, "remove");
582130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
583130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
584130391Sle	if (argc) {
585130391Sle		for (i = 0; i < argc; i++) {
586130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
587130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
588130391Sle		}
589130391Sle	}
590130391Sle	errstr = gctl_issue(req);
591130391Sle	if (errstr != NULL) {
592130391Sle		warnx("can't remove: %s", errstr);
593130391Sle		gctl_free(req);
594130391Sle		return;
595130391Sle	}
596130391Sle	gctl_free(req);
597130391Sle	gvinum_list(0, NULL);
598130391Sle}
599130391Sle
600130391Slevoid
601130391Slegvinum_saveconfig(void)
602130391Sle{
603130391Sle	struct gctl_req *req;
604130391Sle	const char *errstr;
605130391Sle
606130391Sle	req = gctl_get_handle();
607130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
608130391Sle	gctl_ro_param(req, "verb", -1, "saveconfig");
609130391Sle	errstr = gctl_issue(req);
610130391Sle	if (errstr != NULL)
611130391Sle		warnx("can't save configuration: %s", errstr);
612130391Sle	gctl_free(req);
613130391Sle}
614130391Sle
615130391Slevoid
616130391Slegvinum_start(int argc, char **argv)
617130391Sle{
618130391Sle	struct gctl_req *req;
619130391Sle	int i, initsize, j;
620130391Sle	const char *errstr;
621130391Sle	char buf[20];
622130391Sle
623130391Sle	/* 'start' with no arguments is a no-op. */
624130391Sle	if (argc == 1)
625130391Sle		return;
626130391Sle
627130391Sle	initsize = 0;
628130391Sle
629130391Sle	optreset = 1;
630130391Sle	optind = 1;
631130391Sle	while ((j = getopt(argc, argv, "S")) != -1) {
632130391Sle		switch (j) {
633130391Sle		case 'S':
634130391Sle			initsize = atoi(optarg);
635130391Sle			break;
636130391Sle		case '?':
637130391Sle		default:
638130391Sle			return;
639130391Sle		}
640130391Sle	}
641130391Sle	argc -= optind;
642130391Sle	argv += optind;
643130391Sle
644130391Sle	if (!initsize)
645130391Sle		initsize = 512;
646130391Sle
647130391Sle	req = gctl_get_handle();
648130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
649130391Sle	gctl_ro_param(req, "verb", -1, "start");
650130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
651130391Sle	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
652130391Sle	if (argc) {
653130391Sle		for (i = 0; i < argc; i++) {
654130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
655130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
656130391Sle		}
657130391Sle	}
658130391Sle	errstr = gctl_issue(req);
659130391Sle	if (errstr != NULL) {
660130391Sle		warnx("can't start: %s", errstr);
661130391Sle		gctl_free(req);
662130391Sle		return;
663130391Sle	}
664130391Sle
665130391Sle	gctl_free(req);
666130391Sle	gvinum_list(0, NULL);
667130391Sle}
668130391Sle
669130391Slevoid
670130391Slegvinum_stop(int argc, char **argv)
671130391Sle{
672130391Sle	int fileid;
673130391Sle
674130391Sle	fileid = kldfind(GVINUMMOD);
675130391Sle	if (fileid == -1) {
676130391Sle		warn("cannot find " GVINUMMOD);
677130391Sle		return;
678130391Sle	}
679130391Sle	if (kldunload(fileid) != 0) {
680130391Sle		warn("cannot unload " GVINUMMOD);
681130391Sle		return;
682130391Sle	}
683130391Sle
684130391Sle	warnx(GVINUMMOD " unloaded");
685130391Sle	exit(0);
686130391Sle}
687130391Sle
688130391Slevoid
689130391Sleparseline(int argc, char **argv)
690130391Sle{
691130391Sle	if (argc <= 0)
692130391Sle		return;
693130391Sle
694130391Sle	if (!strcmp(argv[0], "cancelinit"))
695130391Sle		gvinum_cancelinit(argc, argv);
696130391Sle	else if (!strcmp(argv[0], "create"))
697130391Sle		gvinum_create(argc, argv);
698130391Sle	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
699130391Sle		exit(0);
700130391Sle	else if (!strcmp(argv[0], "help"))
701130391Sle		gvinum_help();
702130391Sle	else if (!strcmp(argv[0], "init"))
703130391Sle		gvinum_init(argc, argv);
704130391Sle	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
705130391Sle		gvinum_list(argc, argv);
706130391Sle	else if (!strcmp(argv[0], "ld"))
707130391Sle		gvinum_list(argc, argv);
708130391Sle	else if (!strcmp(argv[0], "lp"))
709130391Sle		gvinum_list(argc, argv);
710130391Sle	else if (!strcmp(argv[0], "ls"))
711130391Sle		gvinum_list(argc, argv);
712130391Sle	else if (!strcmp(argv[0], "lv"))
713130391Sle		gvinum_list(argc, argv);
714130391Sle	else if (!strcmp(argv[0], "printconfig"))
715130391Sle		gvinum_printconfig(argc, argv);
716130391Sle	else if (!strcmp(argv[0], "rm"))
717130391Sle		gvinum_rm(argc, argv);
718130391Sle	else if (!strcmp(argv[0], "saveconfig"))
719130391Sle		gvinum_saveconfig();
720130391Sle	else if (!strcmp(argv[0], "start"))
721130391Sle		gvinum_start(argc, argv);
722130391Sle	else if (!strcmp(argv[0], "stop"))
723130391Sle		gvinum_stop(argc, argv);
724130391Sle	else
725130391Sle		printf("unknown command '%s'\n", argv[0]);
726130391Sle
727130391Sle	return;
728130391Sle}
729130391Sle
730130391Sle/*
731130391Sle * The guts of printconfig.  This is called from gvinum_printconfig and from
732130391Sle * gvinum_create when called without an argument, in order to give the user
733130391Sle * something to edit.
734130391Sle */
735130391Slevoid
736130391Sleprintconfig(FILE *of, char *comment)
737130391Sle{
738130391Sle	struct gctl_req *req;
739130391Sle	struct utsname uname_s;
740130391Sle	const char *errstr;
741130391Sle	time_t now;
742130391Sle	char buf[GV_CFG_LEN + 1];
743130391Sle
744130391Sle	uname(&uname_s);
745130391Sle	time(&now);
746130391Sle
747130391Sle	req = gctl_get_handle();
748130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
749130391Sle	gctl_ro_param(req, "verb", -1, "getconfig");
750130391Sle	gctl_ro_param(req, "comment", -1, comment);
751130391Sle	gctl_rw_param(req, "config", sizeof(buf), buf);
752130391Sle	errstr = gctl_issue(req);
753130391Sle	if (errstr != NULL) {
754130391Sle		warnx("can't get configuration: %s", errstr);
755130391Sle		return;
756130391Sle	}
757130391Sle	gctl_free(req);
758130391Sle
759130391Sle	fprintf(of, "# Vinum configuration of %s, saved at %s",
760130391Sle	    uname_s.nodename,
761130391Sle	    ctime(&now));
762130391Sle
763130391Sle	if (*comment != '\0')
764130391Sle	    fprintf(of, "# Current configuration:\n");
765130391Sle
766130391Sle	fprintf(of, buf);
767130391Sle}
768