gvinum.c revision 138112
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 138112 2004-11-26 12:31:36Z 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 **);
58138110Slevoid	gvinum_parityop(int, char **, int);
59130391Slevoid	gvinum_printconfig(int, char **);
60130391Slevoid	gvinum_rm(int, char **);
61130391Slevoid	gvinum_saveconfig(void);
62138112Slevoid	gvinum_setstate(int, char **);
63130391Slevoid	gvinum_start(int, char **);
64130391Slevoid	gvinum_stop(int, char **);
65130391Slevoid	parseline(int, char **);
66130391Slevoid	printconfig(FILE *, char *);
67130391Sle
68130391Sleint
69130391Slemain(int argc, char **argv)
70130391Sle{
71130391Sle	int line, tokens;
72130391Sle	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
73130391Sle
74130391Sle	/* Load the module if necessary. */
75130391Sle	if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
76130391Sle		err(1, GVINUMMOD ": Kernel module not available");
77130391Sle
78130391Sle	/* Arguments given on the command line. */
79130391Sle	if (argc > 1) {
80130391Sle		argc--;
81130391Sle		argv++;
82130391Sle		parseline(argc, argv);
83130391Sle
84130391Sle	/* Interactive mode. */
85130391Sle	} else {
86130391Sle		for (;;) {
87130391Sle			inputline = readline("gvinum -> ");
88130391Sle			if (inputline == NULL) {
89130391Sle				if (ferror(stdin)) {
90130391Sle					err(1, "can't read input");
91130391Sle				} else {
92130391Sle					printf("\n");
93130391Sle					exit(0);
94130391Sle				}
95130391Sle			} else if (*inputline) {
96130391Sle				add_history(inputline);
97130391Sle				strcpy(buffer, inputline);
98130391Sle				free(inputline);
99130391Sle				line++;		    /* count the lines */
100130391Sle				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
101130391Sle				if (tokens)
102130391Sle					parseline(tokens, token);
103130391Sle			}
104130391Sle		}
105130391Sle	}
106130391Sle	exit(0);
107130391Sle}
108130391Sle
109130391Slevoid
110130391Slegvinum_cancelinit(int argc, char **argv)
111130391Sle{
112130391Sle	struct gctl_req *req;
113130391Sle	int i;
114130391Sle	const char *errstr;
115130391Sle	char buf[20];
116130391Sle
117130391Sle	if (argc == 1)
118130391Sle		return;
119130391Sle
120130391Sle	argc--;
121130391Sle	argv++;
122130391Sle
123130391Sle	req = gctl_get_handle();
124130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
125130391Sle	gctl_ro_param(req, "verb", -1, "cancelinit");
126130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
127130391Sle	if (argc) {
128130391Sle		for (i = 0; i < argc; i++) {
129130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
130130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
131130391Sle		}
132130391Sle	}
133130391Sle	errstr = gctl_issue(req);
134130391Sle	if (errstr != NULL) {
135130391Sle		warnx("can't init: %s", errstr);
136130391Sle		gctl_free(req);
137130391Sle		return;
138130391Sle	}
139130391Sle
140130391Sle	gctl_free(req);
141130391Sle	gvinum_list(0, NULL);
142130391Sle}
143130391Sle
144130391Slevoid
145130391Slegvinum_create(int argc, char **argv)
146130391Sle{
147130391Sle	struct gctl_req *req;
148130391Sle	struct gv_drive *d;
149130391Sle	struct gv_plex *p;
150130391Sle	struct gv_sd *s;
151130391Sle	struct gv_volume *v;
152130391Sle	FILE *tmp;
153130391Sle	int drives, errors, fd, line, plexes, plex_in_volume;
154130391Sle	int sd_in_plex, status, subdisks, tokens, volumes;
155130391Sle	const char *errstr;
156130391Sle	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed;
157130391Sle	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
158130391Sle	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
159130391Sle
160133097Sle	if (argc == 2) {
161133097Sle		if ((tmp = fopen(argv[1], "r")) == NULL) {
162133097Sle			warn("can't open '%s' for reading", argv[1]);
163133097Sle			return;
164133097Sle		}
165133097Sle	} else {
166133097Sle		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
167133097Sle
168133097Sle		if ((fd = mkstemp(tmpfile)) == -1) {
169133097Sle			warn("temporary file not accessible");
170133097Sle			return;
171133097Sle		}
172133097Sle		if ((tmp = fdopen(fd, "w")) == NULL) {
173133097Sle			warn("can't open '%s' for writing", tmpfile);
174133097Sle			return;
175133097Sle		}
176133097Sle		printconfig(tmp, "# ");
177133097Sle		fclose(tmp);
178133097Sle
179133097Sle		ed = getenv("EDITOR");
180133097Sle		if (ed == NULL)
181133097Sle			ed = _PATH_VI;
182133097Sle
183133097Sle		snprintf(commandline, sizeof(commandline), "%s %s", ed,
184133097Sle		    tmpfile);
185133097Sle		status = system(commandline);
186133097Sle		if (status != 0) {
187133097Sle			warn("couldn't exec %s; status: %d", ed, status);
188133097Sle			return;
189133097Sle		}
190133097Sle
191133097Sle		if ((tmp = fopen(tmpfile, "r")) == NULL) {
192133097Sle			warn("can't open '%s' for reading", tmpfile);
193133097Sle			return;
194133097Sle		}
195130391Sle	}
196130391Sle
197130391Sle	req = gctl_get_handle();
198130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
199130391Sle	gctl_ro_param(req, "verb", -1, "create");
200130391Sle
201130391Sle	drives = volumes = plexes = subdisks = 0;
202130391Sle	plex_in_volume = sd_in_plex = 0;
203130391Sle	errors = 0;
204130391Sle	line = 1;
205130391Sle	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
206130391Sle
207130391Sle		/* Skip empty lines and comments. */
208130391Sle		if (*buf == '\0' || *buf == '#') {
209130391Sle			line++;
210130391Sle			continue;
211130391Sle		}
212130391Sle
213130391Sle		/* Kill off the newline. */
214130391Sle		buf[strlen(buf) - 1] = '\0';
215130391Sle
216130391Sle		/*
217130391Sle		 * Copy the original input line in case we need it for error
218130391Sle		 * output.
219130391Sle		 */
220130391Sle		strncpy(original, buf, sizeof(buf));
221130391Sle
222130391Sle		tokens = gv_tokenize(buf, token, GV_MAXARGS);
223130391Sle
224130391Sle		if (tokens > 0) {
225130391Sle			/* Volume definition. */
226130391Sle			if (!strcmp(token[0], "volume")) {
227130391Sle				v = gv_new_volume(tokens, token);
228130391Sle				if (v == NULL) {
229130391Sle					warnx("line %d: invalid volume "
230130391Sle					    "definition", line);
231130391Sle					warnx("line %d: '%s'", line, original);
232130391Sle					errors++;
233130391Sle				} else {
234130391Sle					/* Reset plex count for this volume. */
235130391Sle					plex_in_volume = 0;
236130391Sle
237130391Sle					/*
238130391Sle					 * Set default volume name for
239130391Sle					 * following plex definitions.
240130391Sle					 */
241130391Sle					strncpy(volume, v->name,
242130391Sle					    sizeof(volume));
243130391Sle
244130391Sle					snprintf(buf1, sizeof(buf1), "volume%d",
245130391Sle					    volumes);
246130391Sle					gctl_ro_param(req, buf1, sizeof(*v), v);
247130391Sle					volumes++;
248130391Sle				}
249130391Sle
250130391Sle			/* Plex definition. */
251130391Sle			} else if (!strcmp(token[0], "plex")) {
252130391Sle				p = gv_new_plex(tokens, token);
253130391Sle				if (p == NULL) {
254130391Sle					warnx("line %d: invalid plex "
255130391Sle					    "definition", line);
256130391Sle					warnx("line %d: '%s'", line, original);
257130391Sle					errors++;
258130391Sle				} else {
259130391Sle					/* Reset subdisk count for this plex. */
260130391Sle					sd_in_plex = 0;
261130391Sle
262130391Sle					/* Default name. */
263130391Sle					if (strlen(p->name) == 0) {
264130391Sle						snprintf(p->name,
265130391Sle						    GV_MAXPLEXNAME,
266130391Sle						    "%s.p%d", volume,
267130391Sle						    plex_in_volume++);
268130391Sle					}
269130391Sle
270130391Sle					/* Default volume. */
271130391Sle					if (strlen(p->volume) == 0) {
272130391Sle						snprintf(p->volume,
273130391Sle						    GV_MAXVOLNAME, "%s",
274130391Sle						    volume);
275130391Sle					}
276130391Sle
277130391Sle					/*
278130391Sle					 * Set default plex name for following
279130391Sle					 * subdisk definitions.
280130391Sle					 */
281130391Sle					strncpy(plex, p->name, GV_MAXPLEXNAME);
282130391Sle
283130391Sle					snprintf(buf1, sizeof(buf1), "plex%d",
284130391Sle					    plexes);
285130391Sle					gctl_ro_param(req, buf1, sizeof(*p), p);
286130391Sle					plexes++;
287130391Sle				}
288130391Sle
289130391Sle			/* Subdisk definition. */
290130391Sle			} else if (!strcmp(token[0], "sd")) {
291130391Sle				s = gv_new_sd(tokens, token);
292130391Sle				if (s == NULL) {
293130391Sle					warnx("line %d: invalid subdisk "
294130391Sle					    "definition:", line);
295130391Sle					warnx("line %d: '%s'", line, original);
296130391Sle					errors++;
297130391Sle				} else {
298130391Sle					/* Default name. */
299130391Sle					if (strlen(s->name) == 0) {
300130391Sle						snprintf(s->name, GV_MAXSDNAME,
301130391Sle						    "%s.s%d", plex,
302130391Sle						    sd_in_plex++);
303130391Sle					}
304130391Sle
305130391Sle					/* Default plex. */
306130391Sle					if (strlen(s->plex) == 0) {
307130391Sle						snprintf(s->plex,
308130391Sle						    GV_MAXPLEXNAME, "%s", plex);
309130391Sle					}
310130391Sle
311130391Sle					snprintf(buf1, sizeof(buf1), "sd%d",
312130391Sle					    subdisks);
313130391Sle					gctl_ro_param(req, buf1, sizeof(*s), s);
314130391Sle					subdisks++;
315130391Sle				}
316130391Sle
317130391Sle			/* Subdisk definition. */
318130391Sle			} else if (!strcmp(token[0], "drive")) {
319130391Sle				d = gv_new_drive(tokens, token);
320130391Sle				if (d == NULL) {
321130391Sle					warnx("line %d: invalid drive "
322130391Sle					    "definition:", line);
323130391Sle					warnx("line %d: '%s'", line, original);
324130391Sle					errors++;
325130391Sle				} else {
326130391Sle					snprintf(buf1, sizeof(buf1), "drive%d",
327130391Sle					    drives);
328130391Sle					gctl_ro_param(req, buf1, sizeof(*d), d);
329130391Sle					drives++;
330130391Sle				}
331130391Sle
332130391Sle			/* Everything else is bogus. */
333130391Sle			} else {
334130391Sle				warnx("line %d: invalid definition:", line);
335130391Sle				warnx("line %d: '%s'", line, original);
336130391Sle				errors++;
337130391Sle			}
338130391Sle		}
339130391Sle		line++;
340130391Sle	}
341130391Sle
342130391Sle	fclose(tmp);
343130391Sle	unlink(tmpfile);
344130391Sle
345130391Sle	if (!errors && (volumes || plexes || subdisks || drives)) {
346130391Sle		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
347130391Sle		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
348130391Sle		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
349130391Sle		gctl_ro_param(req, "drives", sizeof(int), &drives);
350130391Sle		errstr = gctl_issue(req);
351130391Sle		if (errstr != NULL)
352130391Sle			warnx("create failed: %s", errstr);
353130391Sle	}
354130391Sle	gctl_free(req);
355130391Sle	gvinum_list(0, NULL);
356130391Sle}
357130391Sle
358130391Slevoid
359130391Slegvinum_help(void)
360130391Sle{
361130391Sle	printf("COMMANDS\n"
362130391Sle	    "attach plex volume [rename]\n"
363130391Sle	    "attach subdisk plex [offset] [rename]\n"
364130391Sle	    "        Attach a plex to a volume, or a subdisk to a plex.\n"
365130391Sle	    "checkparity plex [-f] [-v]\n"
366130391Sle	    "        Check the parity blocks of a RAID-4 or RAID-5 plex.\n"
367130391Sle	    "concat [-f] [-n name] [-v] drives\n"
368130391Sle	    "        Create a concatenated volume from the specified drives.\n"
369130391Sle	    "create [-f] description-file\n"
370130391Sle	    "        Create a volume as described in description-file.\n"
371130391Sle	    "detach [-f] [plex | subdisk]\n"
372130391Sle	    "        Detach a plex or subdisk from the volume or plex to"
373130391Sle	    "which it is\n"
374130391Sle	    "        attached.\n"
375130391Sle	    "dumpconfig [drive ...]\n"
376130391Sle	    "        List the configuration information stored on the"
377130391Sle	    " specified\n"
378130391Sle	    "        drives, or all drives in the system if no drive names"
379130391Sle	    " are speci-\n"
380130391Sle	    "        fied.\n"
381130391Sle	    "info [-v] [-V]\n"
382130391Sle	    "        List information about volume manager state.\n"
383130391Sle	    "init [-S size] [-w] plex | subdisk\n"
384130391Sle	    "        Initialize the contents of a subdisk or all the subdisks"
385130391Sle	    " of a\n"
386130391Sle	    "        plex to all zeros.\n"
387130391Sle	    "label volume\n"
388130391Sle	    "        Create a volume label.\n"
389130391Sle	    "l | list [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n"
390130391Sle	    "        List information about specified objects.\n"
391130391Sle	    "ld [-r] [-s] [-v] [-V] [volume]\n"
392130391Sle	    "        List information about drives.\n"
393130391Sle	    "ls [-r] [-s] [-v] [-V] [subdisk]\n"
394130391Sle	    "        List information about subdisks.\n"
395130391Sle	    "lp [-r] [-s] [-v] [-V] [plex]\n"
396130391Sle	    "        List information about plexes.\n"
397130391Sle	    "lv [-r] [-s] [-v] [-V] [volume]\n"
398130391Sle	    "        List information about volumes.\n"
399130391Sle	    "mirror [-f] [-n name] [-s] [-v] drives\n"
400130391Sle	    "        Create a mirrored volume from the specified drives.\n"
401130391Sle	    "move | mv -f drive object ...\n"
402130391Sle	    "        Move the object(s) to the specified drive.\n"
403130391Sle	    "printconfig [file]\n"
404130391Sle	    "        Write a copy of the current configuration to file.\n"
405130391Sle	    "quit    Exit the vinum program when running in interactive mode."
406130391Sle	    "  Nor-\n"
407130391Sle	    "        mally this would be done by entering the EOF character.\n"
408130391Sle	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
409130391Sle	    "        Change the name of the specified object.\n"
410130391Sle	    "rebuildparity plex [-f] [-v] [-V]\n"
411130391Sle	    "        Rebuild the parity blocks of a RAID-4 or RAID-5 plex.\n"
412130391Sle	    "resetconfig\n"
413130391Sle	    "        Reset the complete vinum configuration.\n"
414130391Sle	    "rm [-f] [-r] volume | plex | subdisk\n"
415130391Sle	    "        Remove an object.\n"
416130391Sle	    "saveconfig\n"
417130391Sle	    "        Save vinum configuration to disk after configuration"
418130391Sle	    " failures.\n"
419130391Sle	    "setstate state [volume | plex | subdisk | drive]\n"
420130391Sle	    "        Set state without influencing other objects, for"
421130391Sle	    " diagnostic pur-\n"
422130391Sle	    "        poses only.\n"
423130391Sle	    "start [-i interval] [-S size] [-w] volume | plex | subdisk\n"
424130391Sle	    "        Allow the system to access the objects.\n"
425130391Sle	    "stop [-f] [volume | plex | subdisk]\n"
426130391Sle	    "        Terminate access to the objects, or stop vinum if no"
427130391Sle	    " parameters\n"
428130391Sle	    "        are specified.\n"
429130391Sle	    "stripe [-f] [-n name] [-v] drives\n"
430130391Sle	    "        Create a striped volume from the specified drives.\n"
431130391Sle	);
432130391Sle
433130391Sle	return;
434130391Sle}
435130391Sle
436130391Slevoid
437130391Slegvinum_init(int argc, char **argv)
438130391Sle{
439130391Sle	struct gctl_req *req;
440130391Sle	int i, initsize, j;
441130391Sle	const char *errstr;
442130391Sle	char buf[20];
443130391Sle
444130391Sle	initsize = 0;
445130391Sle	optreset = 1;
446130391Sle	optind = 1;
447130391Sle	while ((j = getopt(argc, argv, "S")) != -1) {
448130391Sle		switch (j) {
449130391Sle		case 'S':
450130391Sle			initsize = atoi(optarg);
451130391Sle			break;
452130391Sle		case '?':
453130391Sle		default:
454130391Sle			return;
455130391Sle		}
456130391Sle	}
457130391Sle	argc -= optind;
458130391Sle	argv += optind;
459130391Sle
460130391Sle	if (!initsize)
461130391Sle		initsize = 512;
462130391Sle
463130391Sle	req = gctl_get_handle();
464130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
465130391Sle	gctl_ro_param(req, "verb", -1, "init");
466130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
467130391Sle	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
468130391Sle	if (argc) {
469130391Sle		for (i = 0; i < argc; i++) {
470130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
471130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
472130391Sle		}
473130391Sle	}
474130391Sle	errstr = gctl_issue(req);
475130391Sle	if (errstr != NULL) {
476130391Sle		warnx("can't init: %s", errstr);
477130391Sle		gctl_free(req);
478130391Sle		return;
479130391Sle	}
480130391Sle
481130391Sle	gctl_free(req);
482130391Sle	gvinum_list(0, NULL);
483130391Sle}
484130391Sle
485130391Slevoid
486138112Slegvinum_setstate(int argc, char **argv)
487138112Sle{
488138112Sle	struct gctl_req *req;
489138112Sle	int flags, i;
490138112Sle	const char *errstr;
491138112Sle
492138112Sle	flags = 0;
493138112Sle
494138112Sle	optreset = 1;
495138112Sle	optind = 1;
496138112Sle
497138112Sle	while ((i = getopt(argc, argv, "f")) != -1) {
498138112Sle		switch (i) {
499138112Sle		case 'f':
500138112Sle			flags |= GV_FLAG_F;
501138112Sle			break;
502138112Sle		case '?':
503138112Sle		default:
504138112Sle			warn("invalid flag: %c", i);
505138112Sle			return;
506138112Sle		}
507138112Sle	}
508138112Sle
509138112Sle	argc -= optind;
510138112Sle	argv += optind;
511138112Sle
512138112Sle	if (argc != 2) {
513138112Sle		warnx("usage: setstate [-f] <state> <obj>");
514138112Sle		return;
515138112Sle	}
516138112Sle
517138112Sle	/*
518138112Sle	 * XXX: This hack is needed to avoid tripping over (now) invalid
519138112Sle	 * 'classic' vinum states and will go away later.
520138112Sle	 */
521138112Sle	if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
522138112Sle	    strcmp(argv[0], "stale")) {
523138112Sle		warnx("invalid state '%s'", argv[0]);
524138112Sle		return;
525138112Sle	}
526138112Sle
527138112Sle	req = gctl_get_handle();
528138112Sle	gctl_ro_param(req, "class", -1, "VINUM");
529138112Sle	gctl_ro_param(req, "verb", -1, "setstate");
530138112Sle	gctl_ro_param(req, "state", -1, argv[0]);
531138112Sle	gctl_ro_param(req, "object", -1, argv[1]);
532138112Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
533138112Sle
534138112Sle	errstr = gctl_issue(req);
535138112Sle	if (errstr != NULL)
536138112Sle		warnx("%s", errstr);
537138112Sle	gctl_free(req);
538138112Sle}
539138112Sle
540138112Slevoid
541130391Slegvinum_list(int argc, char **argv)
542130391Sle{
543130391Sle	struct gctl_req *req;
544130391Sle	int flags, i, j;
545130391Sle	const char *errstr;
546130391Sle	char buf[20], *cmd, config[GV_CFG_LEN + 1];
547130391Sle
548130391Sle	flags = 0;
549130391Sle	cmd = "list";
550130391Sle
551130391Sle	if (argc) {
552130391Sle		optreset = 1;
553130391Sle		optind = 1;
554130391Sle		cmd = argv[0];
555130391Sle		while ((j = getopt(argc, argv, "rsvV")) != -1) {
556130391Sle			switch (j) {
557130391Sle			case 'r':
558130391Sle				flags |= GV_FLAG_R;
559130391Sle				break;
560130391Sle			case 's':
561130391Sle				flags |= GV_FLAG_S;
562130391Sle				break;
563130391Sle			case 'v':
564130391Sle				flags |= GV_FLAG_V;
565130391Sle				break;
566130391Sle			case 'V':
567130391Sle				flags |= GV_FLAG_V;
568130391Sle				flags |= GV_FLAG_VV;
569130391Sle				break;
570130391Sle			case '?':
571130391Sle			default:
572130391Sle				return;
573130391Sle			}
574130391Sle		}
575130391Sle		argc -= optind;
576130391Sle		argv += optind;
577130391Sle
578130391Sle	}
579130391Sle
580130391Sle	req = gctl_get_handle();
581130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
582130391Sle	gctl_ro_param(req, "verb", -1, "list");
583130391Sle	gctl_ro_param(req, "cmd", -1, cmd);
584130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
585130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
586130391Sle	gctl_rw_param(req, "config", sizeof(config), config);
587130391Sle	if (argc) {
588130391Sle		for (i = 0; i < argc; i++) {
589130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
590130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
591130391Sle		}
592130391Sle	}
593130391Sle	errstr = gctl_issue(req);
594130391Sle	if (errstr != NULL) {
595130391Sle		warnx("can't get configuration: %s", errstr);
596130391Sle		gctl_free(req);
597130391Sle		return;
598130391Sle	}
599130391Sle
600130391Sle	printf("%s", config);
601130391Sle	gctl_free(req);
602130391Sle	return;
603130391Sle}
604130391Sle
605130391Slevoid
606130391Slegvinum_printconfig(int argc, char **argv)
607130391Sle{
608130391Sle	printconfig(stdout, "");
609130391Sle}
610130391Sle
611130391Slevoid
612138110Slegvinum_parityop(int argc, char **argv, int rebuild)
613138110Sle{
614138110Sle	struct gctl_req *req;
615138110Sle	int flags, i, rv;
616138110Sle	off_t offset;
617138110Sle	const char *errstr;
618138110Sle	char *op, *msg;
619138110Sle
620138110Sle	if (rebuild) {
621138110Sle		op = "rebuildparity";
622138110Sle		msg = "Rebuilding";
623138110Sle	} else {
624138110Sle		op = "checkparity";
625138110Sle		msg = "Checking";
626138110Sle	}
627138110Sle
628138110Sle	optreset = 1;
629138110Sle	optind = 1;
630138110Sle	flags = 0;
631138110Sle	while ((i = getopt(argc, argv, "fv")) != -1) {
632138110Sle		switch (i) {
633138110Sle		case 'f':
634138110Sle			flags |= GV_FLAG_F;
635138110Sle			break;
636138110Sle		case 'v':
637138110Sle			flags |= GV_FLAG_V;
638138110Sle			break;
639138110Sle		case '?':
640138110Sle		default:
641138110Sle			warnx("invalid flag '%c'", i);
642138110Sle			return;
643138110Sle		}
644138110Sle	}
645138110Sle	argc -= optind;
646138110Sle	argv += optind;
647138110Sle
648138110Sle	if (argc != 1) {
649138110Sle		warn("usage: %s [-f] [-v] <plex>", op);
650138110Sle		return;
651138110Sle	}
652138110Sle
653138110Sle	do {
654138110Sle		rv = 0;
655138110Sle		req = gctl_get_handle();
656138110Sle		gctl_ro_param(req, "class", -1, "VINUM");
657138110Sle		gctl_ro_param(req, "verb", -1, "parityop");
658138110Sle		gctl_ro_param(req, "flags", sizeof(int), &flags);
659138110Sle		gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
660138110Sle		gctl_rw_param(req, "rv", sizeof(int), &rv);
661138110Sle		gctl_rw_param(req, "offset", sizeof(off_t), &offset);
662138110Sle		gctl_ro_param(req, "plex", -1, argv[0]);
663138110Sle		errstr = gctl_issue(req);
664138110Sle		if (errstr) {
665138110Sle			warnx("%s\n", errstr);
666138110Sle			gctl_free(req);
667138110Sle			break;
668138110Sle		}
669138110Sle		gctl_free(req);
670138110Sle		if (flags & GV_FLAG_V) {
671138110Sle			printf("\r%s at %s ... ", msg,
672138110Sle			    gv_roughlength(offset, 1));
673138110Sle		}
674138110Sle		if (rv == 1) {
675138110Sle			printf("Parity incorrect at offset 0x%jx\n",
676138110Sle			    (intmax_t)offset);
677138110Sle			if (!rebuild)
678138110Sle				break;
679138110Sle		}
680138110Sle		fflush(stdout);
681138110Sle
682138110Sle		/* Clear the -f flag. */
683138110Sle		flags &= ~GV_FLAG_F;
684138110Sle	} while (rv >= 0);
685138110Sle
686138110Sle	if ((rv == 2) && (flags & GV_FLAG_V)) {
687138110Sle		if (rebuild)
688138110Sle			printf("Rebuilt parity on %s\n", argv[0]);
689138110Sle		else
690138110Sle			printf("%s has correct parity\n", argv[0]);
691138110Sle	}
692138110Sle}
693138110Sle
694138110Slevoid
695130391Slegvinum_rm(int argc, char **argv)
696130391Sle{
697130391Sle	struct gctl_req *req;
698130391Sle	int flags, i, j;
699130391Sle	const char *errstr;
700130391Sle	char buf[20], *cmd;
701130391Sle
702130391Sle	cmd = argv[0];
703130391Sle	flags = 0;
704130391Sle	optreset = 1;
705130391Sle	optind = 1;
706130391Sle	while ((j = getopt(argc, argv, "r")) != -1) {
707130391Sle		switch (j) {
708130391Sle		case 'r':
709130391Sle			flags |= GV_FLAG_R;
710130391Sle			break;
711130391Sle		case '?':
712130391Sle		default:
713130391Sle			return;
714130391Sle		}
715130391Sle	}
716130391Sle	argc -= optind;
717130391Sle	argv += optind;
718130391Sle
719130391Sle	req = gctl_get_handle();
720130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
721130391Sle	gctl_ro_param(req, "verb", -1, "remove");
722130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
723130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
724130391Sle	if (argc) {
725130391Sle		for (i = 0; i < argc; i++) {
726130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
727130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
728130391Sle		}
729130391Sle	}
730130391Sle	errstr = gctl_issue(req);
731130391Sle	if (errstr != NULL) {
732130391Sle		warnx("can't remove: %s", errstr);
733130391Sle		gctl_free(req);
734130391Sle		return;
735130391Sle	}
736130391Sle	gctl_free(req);
737130391Sle	gvinum_list(0, NULL);
738130391Sle}
739130391Sle
740130391Slevoid
741130391Slegvinum_saveconfig(void)
742130391Sle{
743130391Sle	struct gctl_req *req;
744130391Sle	const char *errstr;
745130391Sle
746130391Sle	req = gctl_get_handle();
747130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
748130391Sle	gctl_ro_param(req, "verb", -1, "saveconfig");
749130391Sle	errstr = gctl_issue(req);
750130391Sle	if (errstr != NULL)
751130391Sle		warnx("can't save configuration: %s", errstr);
752130391Sle	gctl_free(req);
753130391Sle}
754130391Sle
755130391Slevoid
756130391Slegvinum_start(int argc, char **argv)
757130391Sle{
758130391Sle	struct gctl_req *req;
759130391Sle	int i, initsize, j;
760130391Sle	const char *errstr;
761130391Sle	char buf[20];
762130391Sle
763130391Sle	/* 'start' with no arguments is a no-op. */
764130391Sle	if (argc == 1)
765130391Sle		return;
766130391Sle
767130391Sle	initsize = 0;
768130391Sle
769130391Sle	optreset = 1;
770130391Sle	optind = 1;
771130391Sle	while ((j = getopt(argc, argv, "S")) != -1) {
772130391Sle		switch (j) {
773130391Sle		case 'S':
774130391Sle			initsize = atoi(optarg);
775130391Sle			break;
776130391Sle		case '?':
777130391Sle		default:
778130391Sle			return;
779130391Sle		}
780130391Sle	}
781130391Sle	argc -= optind;
782130391Sle	argv += optind;
783130391Sle
784130391Sle	if (!initsize)
785130391Sle		initsize = 512;
786130391Sle
787130391Sle	req = gctl_get_handle();
788130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
789130391Sle	gctl_ro_param(req, "verb", -1, "start");
790130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
791130391Sle	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
792130391Sle	if (argc) {
793130391Sle		for (i = 0; i < argc; i++) {
794130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
795130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
796130391Sle		}
797130391Sle	}
798130391Sle	errstr = gctl_issue(req);
799130391Sle	if (errstr != NULL) {
800130391Sle		warnx("can't start: %s", errstr);
801130391Sle		gctl_free(req);
802130391Sle		return;
803130391Sle	}
804130391Sle
805130391Sle	gctl_free(req);
806130391Sle	gvinum_list(0, NULL);
807130391Sle}
808130391Sle
809130391Slevoid
810130391Slegvinum_stop(int argc, char **argv)
811130391Sle{
812130391Sle	int fileid;
813130391Sle
814130391Sle	fileid = kldfind(GVINUMMOD);
815130391Sle	if (fileid == -1) {
816130391Sle		warn("cannot find " GVINUMMOD);
817130391Sle		return;
818130391Sle	}
819130391Sle	if (kldunload(fileid) != 0) {
820130391Sle		warn("cannot unload " GVINUMMOD);
821130391Sle		return;
822130391Sle	}
823130391Sle
824130391Sle	warnx(GVINUMMOD " unloaded");
825130391Sle	exit(0);
826130391Sle}
827130391Sle
828130391Slevoid
829130391Sleparseline(int argc, char **argv)
830130391Sle{
831130391Sle	if (argc <= 0)
832130391Sle		return;
833130391Sle
834130391Sle	if (!strcmp(argv[0], "cancelinit"))
835130391Sle		gvinum_cancelinit(argc, argv);
836130391Sle	else if (!strcmp(argv[0], "create"))
837130391Sle		gvinum_create(argc, argv);
838130391Sle	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
839130391Sle		exit(0);
840130391Sle	else if (!strcmp(argv[0], "help"))
841130391Sle		gvinum_help();
842130391Sle	else if (!strcmp(argv[0], "init"))
843130391Sle		gvinum_init(argc, argv);
844130391Sle	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
845130391Sle		gvinum_list(argc, argv);
846130391Sle	else if (!strcmp(argv[0], "ld"))
847130391Sle		gvinum_list(argc, argv);
848130391Sle	else if (!strcmp(argv[0], "lp"))
849130391Sle		gvinum_list(argc, argv);
850130391Sle	else if (!strcmp(argv[0], "ls"))
851130391Sle		gvinum_list(argc, argv);
852130391Sle	else if (!strcmp(argv[0], "lv"))
853130391Sle		gvinum_list(argc, argv);
854130391Sle	else if (!strcmp(argv[0], "printconfig"))
855130391Sle		gvinum_printconfig(argc, argv);
856130391Sle	else if (!strcmp(argv[0], "rm"))
857130391Sle		gvinum_rm(argc, argv);
858130391Sle	else if (!strcmp(argv[0], "saveconfig"))
859130391Sle		gvinum_saveconfig();
860138112Sle	else if (!strcmp(argv[0], "setstate"))
861138112Sle		gvinum_setstate(argc, argv);
862130391Sle	else if (!strcmp(argv[0], "start"))
863130391Sle		gvinum_start(argc, argv);
864130391Sle	else if (!strcmp(argv[0], "stop"))
865130391Sle		gvinum_stop(argc, argv);
866138110Sle	else if (!strcmp(argv[0], "checkparity"))
867138110Sle		gvinum_parityop(argc, argv, 0);
868138110Sle	else if (!strcmp(argv[0], "rebuildparity"))
869138110Sle		gvinum_parityop(argc, argv, 1);
870130391Sle	else
871130391Sle		printf("unknown command '%s'\n", argv[0]);
872130391Sle
873130391Sle	return;
874130391Sle}
875130391Sle
876130391Sle/*
877130391Sle * The guts of printconfig.  This is called from gvinum_printconfig and from
878130391Sle * gvinum_create when called without an argument, in order to give the user
879130391Sle * something to edit.
880130391Sle */
881130391Slevoid
882130391Sleprintconfig(FILE *of, char *comment)
883130391Sle{
884130391Sle	struct gctl_req *req;
885130391Sle	struct utsname uname_s;
886130391Sle	const char *errstr;
887130391Sle	time_t now;
888130391Sle	char buf[GV_CFG_LEN + 1];
889130391Sle
890130391Sle	uname(&uname_s);
891130391Sle	time(&now);
892130391Sle
893130391Sle	req = gctl_get_handle();
894130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
895130391Sle	gctl_ro_param(req, "verb", -1, "getconfig");
896130391Sle	gctl_ro_param(req, "comment", -1, comment);
897130391Sle	gctl_rw_param(req, "config", sizeof(buf), buf);
898130391Sle	errstr = gctl_issue(req);
899130391Sle	if (errstr != NULL) {
900130391Sle		warnx("can't get configuration: %s", errstr);
901130391Sle		return;
902130391Sle	}
903130391Sle	gctl_free(req);
904130391Sle
905130391Sle	fprintf(of, "# Vinum configuration of %s, saved at %s",
906130391Sle	    uname_s.nodename,
907130391Sle	    ctime(&now));
908130391Sle
909130391Sle	if (*comment != '\0')
910130391Sle	    fprintf(of, "# Current configuration:\n");
911130391Sle
912130391Sle	fprintf(of, buf);
913130391Sle}
914