gvinum.c revision 157052
1130391Sle/*
2152616Sle *  Copyright (c) 2004 Lukas Ertl, 2005 Chris Jones
3130391Sle *  All rights reserved.
4152631Sle *
5152631Sle * Portions of this software were developed for the FreeBSD Project
6152631Sle * by Chris Jones thanks to the support of Google's Summer of Code
7152631Sle * program and mentoring by Lukas Ertl.
8152631Sle *
9130391Sle * Redistribution and use in source and binary forms, with or without
10130391Sle * modification, are permitted provided that the following conditions
11130391Sle * are met:
12130391Sle * 1. Redistributions of source code must retain the above copyright
13130391Sle *    notice, this list of conditions and the following disclaimer.
14130391Sle * 2. Redistributions in binary form must reproduce the above copyright
15130391Sle *    notice, this list of conditions and the following disclaimer in the
16130391Sle *    documentation and/or other materials provided with the distribution.
17152631Sle *
18130391Sle * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19130391Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20130391Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21130391Sle * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22130391Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23130391Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24130391Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25130391Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26130391Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27130391Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28130391Sle * SUCH DAMAGE.
29130391Sle *
30130391Sle * $FreeBSD: head/sbin/gvinum/gvinum.c 157052 2006-03-23 19:58:43Z le $
31130391Sle */
32130391Sle
33130391Sle#include <sys/param.h>
34130391Sle#include <sys/linker.h>
35130391Sle#include <sys/lock.h>
36130391Sle#include <sys/module.h>
37130391Sle#include <sys/mutex.h>
38130391Sle#include <sys/queue.h>
39130391Sle#include <sys/utsname.h>
40130391Sle
41130391Sle#include <geom/vinum/geom_vinum_var.h>
42130391Sle#include <geom/vinum/geom_vinum_share.h>
43130391Sle
44130391Sle#include <ctype.h>
45130391Sle#include <err.h>
46130391Sle#include <libgeom.h>
47130391Sle#include <stdint.h>
48130391Sle#include <stdio.h>
49130391Sle#include <stdlib.h>
50130391Sle#include <paths.h>
51130391Sle#include <readline/readline.h>
52130391Sle#include <readline/history.h>
53130391Sle#include <unistd.h>
54130391Sle
55130391Sle#include "gvinum.h"
56130391Sle
57130391Slevoid	gvinum_create(int, char **);
58130391Slevoid	gvinum_help(void);
59130391Slevoid	gvinum_list(int, char **);
60152616Slevoid	gvinum_move(int, char **);
61138110Slevoid	gvinum_parityop(int, char **, int);
62130391Slevoid	gvinum_printconfig(int, char **);
63152616Slevoid	gvinum_rename(int, char **);
64157052Slevoid	gvinum_resetconfig(void);
65130391Slevoid	gvinum_rm(int, char **);
66130391Slevoid	gvinum_saveconfig(void);
67138112Slevoid	gvinum_setstate(int, char **);
68130391Slevoid	gvinum_start(int, char **);
69130391Slevoid	gvinum_stop(int, char **);
70130391Slevoid	parseline(int, char **);
71130391Slevoid	printconfig(FILE *, char *);
72130391Sle
73130391Sleint
74130391Slemain(int argc, char **argv)
75130391Sle{
76130391Sle	int line, tokens;
77130391Sle	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
78130391Sle
79130391Sle	/* Load the module if necessary. */
80130391Sle	if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
81130391Sle		err(1, GVINUMMOD ": Kernel module not available");
82130391Sle
83130391Sle	/* Arguments given on the command line. */
84130391Sle	if (argc > 1) {
85130391Sle		argc--;
86130391Sle		argv++;
87130391Sle		parseline(argc, argv);
88130391Sle
89130391Sle	/* Interactive mode. */
90130391Sle	} else {
91130391Sle		for (;;) {
92130391Sle			inputline = readline("gvinum -> ");
93130391Sle			if (inputline == NULL) {
94130391Sle				if (ferror(stdin)) {
95130391Sle					err(1, "can't read input");
96130391Sle				} else {
97130391Sle					printf("\n");
98130391Sle					exit(0);
99130391Sle				}
100130391Sle			} else if (*inputline) {
101130391Sle				add_history(inputline);
102130391Sle				strcpy(buffer, inputline);
103130391Sle				free(inputline);
104130391Sle				line++;		    /* count the lines */
105130391Sle				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
106130391Sle				if (tokens)
107130391Sle					parseline(tokens, token);
108130391Sle			}
109130391Sle		}
110130391Sle	}
111130391Sle	exit(0);
112130391Sle}
113130391Sle
114130391Slevoid
115130391Slegvinum_create(int argc, char **argv)
116130391Sle{
117130391Sle	struct gctl_req *req;
118130391Sle	struct gv_drive *d;
119130391Sle	struct gv_plex *p;
120130391Sle	struct gv_sd *s;
121130391Sle	struct gv_volume *v;
122130391Sle	FILE *tmp;
123130391Sle	int drives, errors, fd, line, plexes, plex_in_volume;
124130391Sle	int sd_in_plex, status, subdisks, tokens, volumes;
125130391Sle	const char *errstr;
126130391Sle	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed;
127130391Sle	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
128130391Sle	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
129130391Sle
130133097Sle	if (argc == 2) {
131133097Sle		if ((tmp = fopen(argv[1], "r")) == NULL) {
132133097Sle			warn("can't open '%s' for reading", argv[1]);
133133097Sle			return;
134133097Sle		}
135133097Sle	} else {
136133097Sle		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
137133097Sle
138133097Sle		if ((fd = mkstemp(tmpfile)) == -1) {
139133097Sle			warn("temporary file not accessible");
140133097Sle			return;
141133097Sle		}
142133097Sle		if ((tmp = fdopen(fd, "w")) == NULL) {
143133097Sle			warn("can't open '%s' for writing", tmpfile);
144133097Sle			return;
145133097Sle		}
146133097Sle		printconfig(tmp, "# ");
147133097Sle		fclose(tmp);
148133097Sle
149133097Sle		ed = getenv("EDITOR");
150133097Sle		if (ed == NULL)
151133097Sle			ed = _PATH_VI;
152133097Sle
153133097Sle		snprintf(commandline, sizeof(commandline), "%s %s", ed,
154133097Sle		    tmpfile);
155133097Sle		status = system(commandline);
156133097Sle		if (status != 0) {
157133097Sle			warn("couldn't exec %s; status: %d", ed, status);
158133097Sle			return;
159133097Sle		}
160133097Sle
161133097Sle		if ((tmp = fopen(tmpfile, "r")) == NULL) {
162133097Sle			warn("can't open '%s' for reading", tmpfile);
163133097Sle			return;
164133097Sle		}
165130391Sle	}
166130391Sle
167130391Sle	req = gctl_get_handle();
168130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
169130391Sle	gctl_ro_param(req, "verb", -1, "create");
170130391Sle
171130391Sle	drives = volumes = plexes = subdisks = 0;
172130391Sle	plex_in_volume = sd_in_plex = 0;
173130391Sle	errors = 0;
174130391Sle	line = 1;
175130391Sle	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
176130391Sle
177130391Sle		/* Skip empty lines and comments. */
178130391Sle		if (*buf == '\0' || *buf == '#') {
179130391Sle			line++;
180130391Sle			continue;
181130391Sle		}
182130391Sle
183130391Sle		/* Kill off the newline. */
184130391Sle		buf[strlen(buf) - 1] = '\0';
185130391Sle
186130391Sle		/*
187130391Sle		 * Copy the original input line in case we need it for error
188130391Sle		 * output.
189130391Sle		 */
190130391Sle		strncpy(original, buf, sizeof(buf));
191130391Sle
192130391Sle		tokens = gv_tokenize(buf, token, GV_MAXARGS);
193150044Sle		if (tokens <= 0) {
194150044Sle			line++;
195150044Sle			continue;
196150044Sle		}
197130391Sle
198150044Sle		/* Volume definition. */
199150044Sle		if (!strcmp(token[0], "volume")) {
200150044Sle			v = gv_new_volume(tokens, token);
201150044Sle			if (v == NULL) {
202150044Sle				warnx("line %d: invalid volume definition",
203150044Sle				    line);
204150044Sle				warnx("line %d: '%s'", line, original);
205150044Sle				errors++;
206150044Sle				line++;
207150044Sle				continue;
208150044Sle			}
209130391Sle
210150044Sle			/* Reset plex count for this volume. */
211150044Sle			plex_in_volume = 0;
212130391Sle
213150044Sle			/*
214150044Sle			 * Set default volume name for following plex
215150044Sle			 * definitions.
216150044Sle			 */
217150044Sle			strncpy(volume, v->name, sizeof(volume));
218130391Sle
219150044Sle			snprintf(buf1, sizeof(buf1), "volume%d", volumes);
220150044Sle			gctl_ro_param(req, buf1, sizeof(*v), v);
221150044Sle			volumes++;
222130391Sle
223150044Sle		/* Plex definition. */
224150044Sle		} else if (!strcmp(token[0], "plex")) {
225150044Sle			p = gv_new_plex(tokens, token);
226150044Sle			if (p == NULL) {
227150044Sle				warnx("line %d: invalid plex definition", line);
228150044Sle				warnx("line %d: '%s'", line, original);
229150044Sle				errors++;
230150044Sle				line++;
231150044Sle				continue;
232150044Sle			}
233130391Sle
234150044Sle			/* Reset subdisk count for this plex. */
235150044Sle			sd_in_plex = 0;
236130391Sle
237150044Sle			/* Default name. */
238150044Sle			if (strlen(p->name) == 0) {
239150044Sle				snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d",
240150044Sle				    volume, plex_in_volume++);
241150044Sle			}
242130391Sle
243150044Sle			/* Default volume. */
244150044Sle			if (strlen(p->volume) == 0) {
245150044Sle				snprintf(p->volume, GV_MAXVOLNAME, "%s",
246150044Sle				    volume);
247150044Sle			}
248130391Sle
249150044Sle			/*
250150044Sle			 * Set default plex name for following subdisk
251150044Sle			 * definitions.
252150044Sle			 */
253150044Sle			strncpy(plex, p->name, GV_MAXPLEXNAME);
254130391Sle
255150044Sle			snprintf(buf1, sizeof(buf1), "plex%d", plexes);
256150044Sle			gctl_ro_param(req, buf1, sizeof(*p), p);
257150044Sle			plexes++;
258130391Sle
259150044Sle		/* Subdisk definition. */
260150044Sle		} else if (!strcmp(token[0], "sd")) {
261150044Sle			s = gv_new_sd(tokens, token);
262150044Sle			if (s == NULL) {
263150044Sle				warnx("line %d: invalid subdisk "
264150044Sle				    "definition:", line);
265150044Sle				warnx("line %d: '%s'", line, original);
266150044Sle				errors++;
267150044Sle				line++;
268150044Sle				continue;
269150044Sle			}
270130391Sle
271150044Sle			/* Default name. */
272150044Sle			if (strlen(s->name) == 0) {
273150044Sle				snprintf(s->name, GV_MAXSDNAME, "%s.s%d",
274150044Sle				    plex, sd_in_plex++);
275150044Sle			}
276150044Sle
277150044Sle			/* Default plex. */
278150044Sle			if (strlen(s->plex) == 0)
279150044Sle				snprintf(s->plex, GV_MAXPLEXNAME, "%s", plex);
280150044Sle
281150044Sle			snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
282150044Sle			gctl_ro_param(req, buf1, sizeof(*s), s);
283150044Sle			subdisks++;
284150044Sle
285150044Sle		/* Subdisk definition. */
286150044Sle		} else if (!strcmp(token[0], "drive")) {
287150044Sle			d = gv_new_drive(tokens, token);
288150044Sle			if (d == NULL) {
289150044Sle				warnx("line %d: invalid drive definition:",
290150044Sle				    line);
291130391Sle				warnx("line %d: '%s'", line, original);
292130391Sle				errors++;
293150044Sle				line++;
294150044Sle				continue;
295130391Sle			}
296150044Sle
297150044Sle			snprintf(buf1, sizeof(buf1), "drive%d", drives);
298150044Sle			gctl_ro_param(req, buf1, sizeof(*d), d);
299150044Sle			drives++;
300150044Sle
301150044Sle		/* Everything else is bogus. */
302150044Sle		} else {
303150044Sle			warnx("line %d: invalid definition:", line);
304150044Sle			warnx("line %d: '%s'", line, original);
305150044Sle			errors++;
306130391Sle		}
307130391Sle		line++;
308130391Sle	}
309130391Sle
310130391Sle	fclose(tmp);
311130391Sle	unlink(tmpfile);
312130391Sle
313130391Sle	if (!errors && (volumes || plexes || subdisks || drives)) {
314130391Sle		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
315130391Sle		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
316130391Sle		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
317130391Sle		gctl_ro_param(req, "drives", sizeof(int), &drives);
318130391Sle		errstr = gctl_issue(req);
319130391Sle		if (errstr != NULL)
320130391Sle			warnx("create failed: %s", errstr);
321130391Sle	}
322130391Sle	gctl_free(req);
323130391Sle	gvinum_list(0, NULL);
324130391Sle}
325130391Sle
326130391Slevoid
327130391Slegvinum_help(void)
328130391Sle{
329130391Sle	printf("COMMANDS\n"
330152616Sle	    "checkparity [-f] plex\n"
331152616Sle	    "        Check the parity blocks of a RAID-5 plex.\n"
332152616Sle	    "create description-file\n"
333152616Sle	    "        Create as per description-file or open editor.\n"
334152616Sle	    "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
335130391Sle	    "        List information about specified objects.\n"
336152616Sle	    "ld [-r] [-v] [-V] [volume]\n"
337130391Sle	    "        List information about drives.\n"
338152616Sle	    "ls [-r] [-v] [-V] [subdisk]\n"
339130391Sle	    "        List information about subdisks.\n"
340152616Sle	    "lp [-r] [-v] [-V] [plex]\n"
341130391Sle	    "        List information about plexes.\n"
342152616Sle	    "lv [-r] [-v] [-V] [volume]\n"
343130391Sle	    "        List information about volumes.\n"
344130391Sle	    "move | mv -f drive object ...\n"
345130391Sle	    "        Move the object(s) to the specified drive.\n"
346130391Sle	    "quit    Exit the vinum program when running in interactive mode."
347130391Sle	    "  Nor-\n"
348130391Sle	    "        mally this would be done by entering the EOF character.\n"
349130391Sle	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
350130391Sle	    "        Change the name of the specified object.\n"
351152616Sle	    "rebuildparity plex [-f]\n"
352152616Sle	    "        Rebuild the parity blocks of a RAID-5 plex.\n"
353157052Sle	    "resetconfig\n"
354157052Sle	    "        Reset the complete gvinum configuration\n"
355152616Sle	    "rm [-r] volume | plex | subdisk | drive\n"
356130391Sle	    "        Remove an object.\n"
357130391Sle	    "saveconfig\n"
358130391Sle	    "        Save vinum configuration to disk after configuration"
359130391Sle	    " failures.\n"
360152616Sle	    "setstate [-f] state [volume | plex | subdisk | drive]\n"
361130391Sle	    "        Set state without influencing other objects, for"
362130391Sle	    " diagnostic pur-\n"
363130391Sle	    "        poses only.\n"
364152616Sle	    "start [-S size] volume | plex | subdisk\n"
365130391Sle	    "        Allow the system to access the objects.\n"
366130391Sle	);
367130391Sle
368130391Sle	return;
369130391Sle}
370130391Sle
371130391Slevoid
372138112Slegvinum_setstate(int argc, char **argv)
373138112Sle{
374138112Sle	struct gctl_req *req;
375138112Sle	int flags, i;
376138112Sle	const char *errstr;
377138112Sle
378138112Sle	flags = 0;
379138112Sle
380138112Sle	optreset = 1;
381138112Sle	optind = 1;
382138112Sle
383138112Sle	while ((i = getopt(argc, argv, "f")) != -1) {
384138112Sle		switch (i) {
385138112Sle		case 'f':
386138112Sle			flags |= GV_FLAG_F;
387138112Sle			break;
388138112Sle		case '?':
389138112Sle		default:
390138112Sle			warn("invalid flag: %c", i);
391138112Sle			return;
392138112Sle		}
393138112Sle	}
394138112Sle
395138112Sle	argc -= optind;
396138112Sle	argv += optind;
397138112Sle
398138112Sle	if (argc != 2) {
399138112Sle		warnx("usage: setstate [-f] <state> <obj>");
400138112Sle		return;
401138112Sle	}
402138112Sle
403138112Sle	/*
404138112Sle	 * XXX: This hack is needed to avoid tripping over (now) invalid
405138112Sle	 * 'classic' vinum states and will go away later.
406138112Sle	 */
407138112Sle	if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
408138112Sle	    strcmp(argv[0], "stale")) {
409138112Sle		warnx("invalid state '%s'", argv[0]);
410138112Sle		return;
411138112Sle	}
412138112Sle
413138112Sle	req = gctl_get_handle();
414138112Sle	gctl_ro_param(req, "class", -1, "VINUM");
415138112Sle	gctl_ro_param(req, "verb", -1, "setstate");
416138112Sle	gctl_ro_param(req, "state", -1, argv[0]);
417138112Sle	gctl_ro_param(req, "object", -1, argv[1]);
418138112Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
419138112Sle
420138112Sle	errstr = gctl_issue(req);
421138112Sle	if (errstr != NULL)
422138112Sle		warnx("%s", errstr);
423138112Sle	gctl_free(req);
424138112Sle}
425138112Sle
426138112Slevoid
427130391Slegvinum_list(int argc, char **argv)
428130391Sle{
429130391Sle	struct gctl_req *req;
430130391Sle	int flags, i, j;
431130391Sle	const char *errstr;
432130391Sle	char buf[20], *cmd, config[GV_CFG_LEN + 1];
433130391Sle
434130391Sle	flags = 0;
435130391Sle	cmd = "list";
436130391Sle
437130391Sle	if (argc) {
438130391Sle		optreset = 1;
439130391Sle		optind = 1;
440130391Sle		cmd = argv[0];
441130391Sle		while ((j = getopt(argc, argv, "rsvV")) != -1) {
442130391Sle			switch (j) {
443130391Sle			case 'r':
444130391Sle				flags |= GV_FLAG_R;
445130391Sle				break;
446130391Sle			case 's':
447130391Sle				flags |= GV_FLAG_S;
448130391Sle				break;
449130391Sle			case 'v':
450130391Sle				flags |= GV_FLAG_V;
451130391Sle				break;
452130391Sle			case 'V':
453130391Sle				flags |= GV_FLAG_V;
454130391Sle				flags |= GV_FLAG_VV;
455130391Sle				break;
456130391Sle			case '?':
457130391Sle			default:
458130391Sle				return;
459130391Sle			}
460130391Sle		}
461130391Sle		argc -= optind;
462130391Sle		argv += optind;
463130391Sle
464130391Sle	}
465130391Sle
466130391Sle	req = gctl_get_handle();
467130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
468130391Sle	gctl_ro_param(req, "verb", -1, "list");
469130391Sle	gctl_ro_param(req, "cmd", -1, cmd);
470130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
471130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
472130391Sle	gctl_rw_param(req, "config", sizeof(config), config);
473130391Sle	if (argc) {
474130391Sle		for (i = 0; i < argc; i++) {
475130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
476130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
477130391Sle		}
478130391Sle	}
479130391Sle	errstr = gctl_issue(req);
480130391Sle	if (errstr != NULL) {
481130391Sle		warnx("can't get configuration: %s", errstr);
482130391Sle		gctl_free(req);
483130391Sle		return;
484130391Sle	}
485130391Sle
486130391Sle	printf("%s", config);
487130391Sle	gctl_free(req);
488130391Sle	return;
489130391Sle}
490130391Sle
491152616Sle/* Note that move is currently of form '[-r] target object [...]' */
492130391Slevoid
493152616Slegvinum_move(int argc, char **argv)
494152616Sle{
495152616Sle	struct gctl_req *req;
496152616Sle	const char *errstr;
497152616Sle	char buf[20];
498152616Sle	int flags, i, j;
499152616Sle
500152616Sle	flags = 0;
501152616Sle	if (argc) {
502152616Sle		optreset = 1;
503152616Sle		optind = 1;
504152616Sle		while ((j = getopt(argc, argv, "f")) != -1) {
505152616Sle			switch (j) {
506152616Sle			case 'f':
507152616Sle				flags |= GV_FLAG_F;
508152616Sle				break;
509152616Sle			case '?':
510152616Sle			default:
511152616Sle				return;
512152616Sle			}
513152616Sle		}
514152616Sle		argc -= optind;
515152616Sle		argv += optind;
516152616Sle	}
517152616Sle
518152616Sle	switch (argc) {
519152616Sle		case 0:
520152616Sle			warnx("no destination or object(s) to move specified");
521152616Sle			return;
522152616Sle		case 1:
523152616Sle			warnx("no object(s) to move specified");
524152616Sle			return;
525152616Sle		default:
526152616Sle			break;
527152616Sle	}
528152616Sle
529152616Sle	req = gctl_get_handle();
530152616Sle	gctl_ro_param(req, "class", -1, "VINUM");
531152616Sle	gctl_ro_param(req, "verb", -1, "move");
532152616Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
533152616Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
534152616Sle	gctl_ro_param(req, "destination", -1, argv[0]);
535152616Sle	for (i = 1; i < argc; i++) {
536152616Sle		snprintf(buf, sizeof(buf), "argv%d", i);
537152616Sle		gctl_ro_param(req, buf, -1, argv[i]);
538152616Sle	}
539152631Sle	errstr = gctl_issue(req);
540152616Sle	if (errstr != NULL)
541152616Sle		warnx("can't move object(s):  %s", errstr);
542152616Sle	gctl_free(req);
543152616Sle	return;
544152616Sle}
545152616Sle
546152616Slevoid
547130391Slegvinum_printconfig(int argc, char **argv)
548130391Sle{
549130391Sle	printconfig(stdout, "");
550130391Sle}
551130391Sle
552130391Slevoid
553138110Slegvinum_parityop(int argc, char **argv, int rebuild)
554138110Sle{
555138110Sle	struct gctl_req *req;
556138110Sle	int flags, i, rv;
557138110Sle	off_t offset;
558138110Sle	const char *errstr;
559138110Sle	char *op, *msg;
560138110Sle
561138110Sle	if (rebuild) {
562138110Sle		op = "rebuildparity";
563138110Sle		msg = "Rebuilding";
564138110Sle	} else {
565138110Sle		op = "checkparity";
566138110Sle		msg = "Checking";
567138110Sle	}
568138110Sle
569138110Sle	optreset = 1;
570138110Sle	optind = 1;
571138110Sle	flags = 0;
572138110Sle	while ((i = getopt(argc, argv, "fv")) != -1) {
573138110Sle		switch (i) {
574138110Sle		case 'f':
575138110Sle			flags |= GV_FLAG_F;
576138110Sle			break;
577138110Sle		case 'v':
578138110Sle			flags |= GV_FLAG_V;
579138110Sle			break;
580138110Sle		case '?':
581138110Sle		default:
582138110Sle			warnx("invalid flag '%c'", i);
583138110Sle			return;
584138110Sle		}
585138110Sle	}
586138110Sle	argc -= optind;
587138110Sle	argv += optind;
588138110Sle
589138110Sle	if (argc != 1) {
590138110Sle		warn("usage: %s [-f] [-v] <plex>", op);
591138110Sle		return;
592138110Sle	}
593138110Sle
594138110Sle	do {
595138110Sle		rv = 0;
596138110Sle		req = gctl_get_handle();
597138110Sle		gctl_ro_param(req, "class", -1, "VINUM");
598138110Sle		gctl_ro_param(req, "verb", -1, "parityop");
599138110Sle		gctl_ro_param(req, "flags", sizeof(int), &flags);
600138110Sle		gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
601138110Sle		gctl_rw_param(req, "rv", sizeof(int), &rv);
602138110Sle		gctl_rw_param(req, "offset", sizeof(off_t), &offset);
603138110Sle		gctl_ro_param(req, "plex", -1, argv[0]);
604138110Sle		errstr = gctl_issue(req);
605138110Sle		if (errstr) {
606138110Sle			warnx("%s\n", errstr);
607138110Sle			gctl_free(req);
608138110Sle			break;
609138110Sle		}
610138110Sle		gctl_free(req);
611138110Sle		if (flags & GV_FLAG_V) {
612138110Sle			printf("\r%s at %s ... ", msg,
613138110Sle			    gv_roughlength(offset, 1));
614138110Sle		}
615138110Sle		if (rv == 1) {
616138110Sle			printf("Parity incorrect at offset 0x%jx\n",
617138110Sle			    (intmax_t)offset);
618138110Sle			if (!rebuild)
619138110Sle				break;
620138110Sle		}
621138110Sle		fflush(stdout);
622138110Sle
623138110Sle		/* Clear the -f flag. */
624138110Sle		flags &= ~GV_FLAG_F;
625138110Sle	} while (rv >= 0);
626138110Sle
627138110Sle	if ((rv == 2) && (flags & GV_FLAG_V)) {
628138110Sle		if (rebuild)
629138110Sle			printf("Rebuilt parity on %s\n", argv[0]);
630138110Sle		else
631138110Sle			printf("%s has correct parity\n", argv[0]);
632138110Sle	}
633138110Sle}
634138110Sle
635138110Slevoid
636152616Slegvinum_rename(int argc, char **argv)
637152616Sle{
638152616Sle	struct gctl_req *req;
639152616Sle	const char *errstr;
640152616Sle	int flags, j;
641152616Sle
642152616Sle	flags = 0;
643152616Sle
644152616Sle	if (argc) {
645152616Sle		optreset = 1;
646152616Sle		optind = 1;
647152616Sle		while ((j = getopt(argc, argv, "r")) != -1) {
648152616Sle			switch (j) {
649152616Sle			case 'r':
650152616Sle				flags |= GV_FLAG_R;
651152616Sle				break;
652152616Sle			case '?':
653152616Sle			default:
654152616Sle				return;
655152616Sle			}
656152616Sle		}
657152616Sle		argc -= optind;
658152616Sle		argv += optind;
659152616Sle	}
660152616Sle
661152616Sle	switch (argc) {
662152616Sle		case 0:
663152616Sle			warnx("no object to rename specified");
664152616Sle			return;
665152616Sle		case 1:
666152616Sle			warnx("no new name specified");
667152616Sle			return;
668152616Sle		case 2:
669152616Sle			break;
670152616Sle		default:
671152616Sle			warnx("more than one new name specified");
672152616Sle			return;
673152616Sle	}
674152616Sle
675152616Sle	req = gctl_get_handle();
676152616Sle	gctl_ro_param(req, "class", -1, "VINUM");
677152616Sle	gctl_ro_param(req, "verb", -1, "rename");
678152616Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
679152616Sle	gctl_ro_param(req, "object", -1, argv[0]);
680152616Sle	gctl_ro_param(req, "newname", -1, argv[1]);
681152631Sle	errstr = gctl_issue(req);
682152616Sle	if (errstr != NULL)
683152616Sle		warnx("can't rename object:  %s", errstr);
684152616Sle	gctl_free(req);
685152616Sle	return;
686152616Sle}
687152616Sle
688152616Slevoid
689130391Slegvinum_rm(int argc, char **argv)
690130391Sle{
691130391Sle	struct gctl_req *req;
692130391Sle	int flags, i, j;
693130391Sle	const char *errstr;
694130391Sle	char buf[20], *cmd;
695130391Sle
696130391Sle	cmd = argv[0];
697130391Sle	flags = 0;
698130391Sle	optreset = 1;
699130391Sle	optind = 1;
700130391Sle	while ((j = getopt(argc, argv, "r")) != -1) {
701130391Sle		switch (j) {
702130391Sle		case 'r':
703130391Sle			flags |= GV_FLAG_R;
704130391Sle			break;
705130391Sle		case '?':
706130391Sle		default:
707130391Sle			return;
708130391Sle		}
709130391Sle	}
710130391Sle	argc -= optind;
711130391Sle	argv += optind;
712130391Sle
713130391Sle	req = gctl_get_handle();
714130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
715130391Sle	gctl_ro_param(req, "verb", -1, "remove");
716130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
717130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
718130391Sle	if (argc) {
719130391Sle		for (i = 0; i < argc; i++) {
720130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
721130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
722130391Sle		}
723130391Sle	}
724130391Sle	errstr = gctl_issue(req);
725130391Sle	if (errstr != NULL) {
726130391Sle		warnx("can't remove: %s", errstr);
727130391Sle		gctl_free(req);
728130391Sle		return;
729130391Sle	}
730130391Sle	gctl_free(req);
731130391Sle	gvinum_list(0, NULL);
732130391Sle}
733130391Sle
734130391Slevoid
735157052Slegvinum_resetconfig(void)
736157052Sle{
737157052Sle	struct gctl_req *req;
738157052Sle	const char *errstr;
739157052Sle	char reply[32];
740157052Sle
741157052Sle	if (!isatty(STDIN_FILENO)) {
742157052Sle		warn("Please enter this command from a tty device\n");
743157052Sle		return;
744157052Sle	}
745157052Sle	printf(" WARNING!  This command will completely wipe out your gvinum"
746157052Sle	    "configuration.\n"
747157052Sle	    " All data will be lost.  If you really want to do this,"
748157052Sle	    " enter the text\n\n"
749157052Sle	    " NO FUTURE\n"
750157052Sle	    " Enter text -> ");
751157052Sle	fgets(reply, sizeof(reply), stdin);
752157052Sle	if (strcmp(reply, "NO FUTURE\n")) {
753157052Sle		printf("\n No change\n");
754157052Sle		return;
755157052Sle	}
756157052Sle	req = gctl_get_handle();
757157052Sle	gctl_ro_param(req, "class", -1, "VINUM");
758157052Sle	gctl_ro_param(req, "verb", -1, "resetconfig");
759157052Sle	errstr = gctl_issue(req);
760157052Sle	if (errstr != NULL) {
761157052Sle		warnx("can't reset config: %s", errstr);
762157052Sle		gctl_free(req);
763157052Sle		return;
764157052Sle	}
765157052Sle	gctl_free(req);
766157052Sle	gvinum_list(0, NULL);
767157052Sle	printf("gvinum configuration obliterated\n");
768157052Sle}
769157052Sle
770157052Slevoid
771130391Slegvinum_saveconfig(void)
772130391Sle{
773130391Sle	struct gctl_req *req;
774130391Sle	const char *errstr;
775130391Sle
776130391Sle	req = gctl_get_handle();
777130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
778130391Sle	gctl_ro_param(req, "verb", -1, "saveconfig");
779130391Sle	errstr = gctl_issue(req);
780130391Sle	if (errstr != NULL)
781130391Sle		warnx("can't save configuration: %s", errstr);
782130391Sle	gctl_free(req);
783130391Sle}
784130391Sle
785130391Slevoid
786130391Slegvinum_start(int argc, char **argv)
787130391Sle{
788130391Sle	struct gctl_req *req;
789130391Sle	int i, initsize, j;
790130391Sle	const char *errstr;
791130391Sle	char buf[20];
792130391Sle
793130391Sle	/* 'start' with no arguments is a no-op. */
794130391Sle	if (argc == 1)
795130391Sle		return;
796130391Sle
797130391Sle	initsize = 0;
798130391Sle
799130391Sle	optreset = 1;
800130391Sle	optind = 1;
801130391Sle	while ((j = getopt(argc, argv, "S")) != -1) {
802130391Sle		switch (j) {
803130391Sle		case 'S':
804130391Sle			initsize = atoi(optarg);
805130391Sle			break;
806130391Sle		case '?':
807130391Sle		default:
808130391Sle			return;
809130391Sle		}
810130391Sle	}
811130391Sle	argc -= optind;
812130391Sle	argv += optind;
813130391Sle
814130391Sle	if (!initsize)
815130391Sle		initsize = 512;
816130391Sle
817130391Sle	req = gctl_get_handle();
818130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
819130391Sle	gctl_ro_param(req, "verb", -1, "start");
820130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
821130391Sle	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
822130391Sle	if (argc) {
823130391Sle		for (i = 0; i < argc; i++) {
824130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
825130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
826130391Sle		}
827130391Sle	}
828130391Sle	errstr = gctl_issue(req);
829130391Sle	if (errstr != NULL) {
830130391Sle		warnx("can't start: %s", errstr);
831130391Sle		gctl_free(req);
832130391Sle		return;
833130391Sle	}
834130391Sle
835130391Sle	gctl_free(req);
836130391Sle	gvinum_list(0, NULL);
837130391Sle}
838130391Sle
839130391Slevoid
840130391Slegvinum_stop(int argc, char **argv)
841130391Sle{
842130391Sle	int fileid;
843130391Sle
844130391Sle	fileid = kldfind(GVINUMMOD);
845130391Sle	if (fileid == -1) {
846130391Sle		warn("cannot find " GVINUMMOD);
847130391Sle		return;
848130391Sle	}
849130391Sle	if (kldunload(fileid) != 0) {
850130391Sle		warn("cannot unload " GVINUMMOD);
851130391Sle		return;
852130391Sle	}
853130391Sle
854130391Sle	warnx(GVINUMMOD " unloaded");
855130391Sle	exit(0);
856130391Sle}
857130391Sle
858130391Slevoid
859130391Sleparseline(int argc, char **argv)
860130391Sle{
861130391Sle	if (argc <= 0)
862130391Sle		return;
863130391Sle
864150044Sle	if (!strcmp(argv[0], "create"))
865130391Sle		gvinum_create(argc, argv);
866130391Sle	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
867130391Sle		exit(0);
868130391Sle	else if (!strcmp(argv[0], "help"))
869130391Sle		gvinum_help();
870130391Sle	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
871130391Sle		gvinum_list(argc, argv);
872130391Sle	else if (!strcmp(argv[0], "ld"))
873130391Sle		gvinum_list(argc, argv);
874130391Sle	else if (!strcmp(argv[0], "lp"))
875130391Sle		gvinum_list(argc, argv);
876130391Sle	else if (!strcmp(argv[0], "ls"))
877130391Sle		gvinum_list(argc, argv);
878130391Sle	else if (!strcmp(argv[0], "lv"))
879130391Sle		gvinum_list(argc, argv);
880152616Sle	else if (!strcmp(argv[0], "move"))
881152616Sle		gvinum_move(argc, argv);
882152616Sle	else if (!strcmp(argv[0], "mv"))
883152616Sle		gvinum_move(argc, argv);
884130391Sle	else if (!strcmp(argv[0], "printconfig"))
885130391Sle		gvinum_printconfig(argc, argv);
886152616Sle	else if (!strcmp(argv[0], "rename"))
887152616Sle		gvinum_rename(argc, argv);
888157052Sle	else if (!strcmp(argv[0], "resetconfig"))
889157052Sle		gvinum_resetconfig();
890130391Sle	else if (!strcmp(argv[0], "rm"))
891130391Sle		gvinum_rm(argc, argv);
892130391Sle	else if (!strcmp(argv[0], "saveconfig"))
893130391Sle		gvinum_saveconfig();
894138112Sle	else if (!strcmp(argv[0], "setstate"))
895138112Sle		gvinum_setstate(argc, argv);
896130391Sle	else if (!strcmp(argv[0], "start"))
897130391Sle		gvinum_start(argc, argv);
898130391Sle	else if (!strcmp(argv[0], "stop"))
899130391Sle		gvinum_stop(argc, argv);
900138110Sle	else if (!strcmp(argv[0], "checkparity"))
901138110Sle		gvinum_parityop(argc, argv, 0);
902138110Sle	else if (!strcmp(argv[0], "rebuildparity"))
903138110Sle		gvinum_parityop(argc, argv, 1);
904130391Sle	else
905130391Sle		printf("unknown command '%s'\n", argv[0]);
906130391Sle
907130391Sle	return;
908130391Sle}
909130391Sle
910130391Sle/*
911130391Sle * The guts of printconfig.  This is called from gvinum_printconfig and from
912130391Sle * gvinum_create when called without an argument, in order to give the user
913130391Sle * something to edit.
914130391Sle */
915130391Slevoid
916130391Sleprintconfig(FILE *of, char *comment)
917130391Sle{
918130391Sle	struct gctl_req *req;
919130391Sle	struct utsname uname_s;
920130391Sle	const char *errstr;
921130391Sle	time_t now;
922130391Sle	char buf[GV_CFG_LEN + 1];
923130391Sle
924130391Sle	uname(&uname_s);
925130391Sle	time(&now);
926130391Sle
927130391Sle	req = gctl_get_handle();
928130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
929130391Sle	gctl_ro_param(req, "verb", -1, "getconfig");
930130391Sle	gctl_ro_param(req, "comment", -1, comment);
931130391Sle	gctl_rw_param(req, "config", sizeof(buf), buf);
932130391Sle	errstr = gctl_issue(req);
933130391Sle	if (errstr != NULL) {
934130391Sle		warnx("can't get configuration: %s", errstr);
935130391Sle		return;
936130391Sle	}
937130391Sle	gctl_free(req);
938130391Sle
939130391Sle	fprintf(of, "# Vinum configuration of %s, saved at %s",
940130391Sle	    uname_s.nodename,
941130391Sle	    ctime(&now));
942130391Sle
943130391Sle	if (*comment != '\0')
944130391Sle	    fprintf(of, "# Current configuration:\n");
945130391Sle
946130391Sle	fprintf(of, buf);
947130391Sle}
948