gvinum.c revision 152631
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 152631 2005-11-20 10:35:46Z 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 **);
64130391Slevoid	gvinum_rm(int, char **);
65130391Slevoid	gvinum_saveconfig(void);
66138112Slevoid	gvinum_setstate(int, char **);
67130391Slevoid	gvinum_start(int, char **);
68130391Slevoid	gvinum_stop(int, char **);
69130391Slevoid	parseline(int, char **);
70130391Slevoid	printconfig(FILE *, char *);
71130391Sle
72130391Sleint
73130391Slemain(int argc, char **argv)
74130391Sle{
75130391Sle	int line, tokens;
76130391Sle	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
77130391Sle
78130391Sle	/* Load the module if necessary. */
79130391Sle	if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
80130391Sle		err(1, GVINUMMOD ": Kernel module not available");
81130391Sle
82130391Sle	/* Arguments given on the command line. */
83130391Sle	if (argc > 1) {
84130391Sle		argc--;
85130391Sle		argv++;
86130391Sle		parseline(argc, argv);
87130391Sle
88130391Sle	/* Interactive mode. */
89130391Sle	} else {
90130391Sle		for (;;) {
91130391Sle			inputline = readline("gvinum -> ");
92130391Sle			if (inputline == NULL) {
93130391Sle				if (ferror(stdin)) {
94130391Sle					err(1, "can't read input");
95130391Sle				} else {
96130391Sle					printf("\n");
97130391Sle					exit(0);
98130391Sle				}
99130391Sle			} else if (*inputline) {
100130391Sle				add_history(inputline);
101130391Sle				strcpy(buffer, inputline);
102130391Sle				free(inputline);
103130391Sle				line++;		    /* count the lines */
104130391Sle				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
105130391Sle				if (tokens)
106130391Sle					parseline(tokens, token);
107130391Sle			}
108130391Sle		}
109130391Sle	}
110130391Sle	exit(0);
111130391Sle}
112130391Sle
113130391Slevoid
114130391Slegvinum_create(int argc, char **argv)
115130391Sle{
116130391Sle	struct gctl_req *req;
117130391Sle	struct gv_drive *d;
118130391Sle	struct gv_plex *p;
119130391Sle	struct gv_sd *s;
120130391Sle	struct gv_volume *v;
121130391Sle	FILE *tmp;
122130391Sle	int drives, errors, fd, line, plexes, plex_in_volume;
123130391Sle	int sd_in_plex, status, subdisks, tokens, volumes;
124130391Sle	const char *errstr;
125130391Sle	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed;
126130391Sle	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
127130391Sle	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
128130391Sle
129133097Sle	if (argc == 2) {
130133097Sle		if ((tmp = fopen(argv[1], "r")) == NULL) {
131133097Sle			warn("can't open '%s' for reading", argv[1]);
132133097Sle			return;
133133097Sle		}
134133097Sle	} else {
135133097Sle		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
136133097Sle
137133097Sle		if ((fd = mkstemp(tmpfile)) == -1) {
138133097Sle			warn("temporary file not accessible");
139133097Sle			return;
140133097Sle		}
141133097Sle		if ((tmp = fdopen(fd, "w")) == NULL) {
142133097Sle			warn("can't open '%s' for writing", tmpfile);
143133097Sle			return;
144133097Sle		}
145133097Sle		printconfig(tmp, "# ");
146133097Sle		fclose(tmp);
147133097Sle
148133097Sle		ed = getenv("EDITOR");
149133097Sle		if (ed == NULL)
150133097Sle			ed = _PATH_VI;
151133097Sle
152133097Sle		snprintf(commandline, sizeof(commandline), "%s %s", ed,
153133097Sle		    tmpfile);
154133097Sle		status = system(commandline);
155133097Sle		if (status != 0) {
156133097Sle			warn("couldn't exec %s; status: %d", ed, status);
157133097Sle			return;
158133097Sle		}
159133097Sle
160133097Sle		if ((tmp = fopen(tmpfile, "r")) == NULL) {
161133097Sle			warn("can't open '%s' for reading", tmpfile);
162133097Sle			return;
163133097Sle		}
164130391Sle	}
165130391Sle
166130391Sle	req = gctl_get_handle();
167130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
168130391Sle	gctl_ro_param(req, "verb", -1, "create");
169130391Sle
170130391Sle	drives = volumes = plexes = subdisks = 0;
171130391Sle	plex_in_volume = sd_in_plex = 0;
172130391Sle	errors = 0;
173130391Sle	line = 1;
174130391Sle	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
175130391Sle
176130391Sle		/* Skip empty lines and comments. */
177130391Sle		if (*buf == '\0' || *buf == '#') {
178130391Sle			line++;
179130391Sle			continue;
180130391Sle		}
181130391Sle
182130391Sle		/* Kill off the newline. */
183130391Sle		buf[strlen(buf) - 1] = '\0';
184130391Sle
185130391Sle		/*
186130391Sle		 * Copy the original input line in case we need it for error
187130391Sle		 * output.
188130391Sle		 */
189130391Sle		strncpy(original, buf, sizeof(buf));
190130391Sle
191130391Sle		tokens = gv_tokenize(buf, token, GV_MAXARGS);
192150044Sle		if (tokens <= 0) {
193150044Sle			line++;
194150044Sle			continue;
195150044Sle		}
196130391Sle
197150044Sle		/* Volume definition. */
198150044Sle		if (!strcmp(token[0], "volume")) {
199150044Sle			v = gv_new_volume(tokens, token);
200150044Sle			if (v == NULL) {
201150044Sle				warnx("line %d: invalid volume definition",
202150044Sle				    line);
203150044Sle				warnx("line %d: '%s'", line, original);
204150044Sle				errors++;
205150044Sle				line++;
206150044Sle				continue;
207150044Sle			}
208130391Sle
209150044Sle			/* Reset plex count for this volume. */
210150044Sle			plex_in_volume = 0;
211130391Sle
212150044Sle			/*
213150044Sle			 * Set default volume name for following plex
214150044Sle			 * definitions.
215150044Sle			 */
216150044Sle			strncpy(volume, v->name, sizeof(volume));
217130391Sle
218150044Sle			snprintf(buf1, sizeof(buf1), "volume%d", volumes);
219150044Sle			gctl_ro_param(req, buf1, sizeof(*v), v);
220150044Sle			volumes++;
221130391Sle
222150044Sle		/* Plex definition. */
223150044Sle		} else if (!strcmp(token[0], "plex")) {
224150044Sle			p = gv_new_plex(tokens, token);
225150044Sle			if (p == NULL) {
226150044Sle				warnx("line %d: invalid plex definition", line);
227150044Sle				warnx("line %d: '%s'", line, original);
228150044Sle				errors++;
229150044Sle				line++;
230150044Sle				continue;
231150044Sle			}
232130391Sle
233150044Sle			/* Reset subdisk count for this plex. */
234150044Sle			sd_in_plex = 0;
235130391Sle
236150044Sle			/* Default name. */
237150044Sle			if (strlen(p->name) == 0) {
238150044Sle				snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d",
239150044Sle				    volume, plex_in_volume++);
240150044Sle			}
241130391Sle
242150044Sle			/* Default volume. */
243150044Sle			if (strlen(p->volume) == 0) {
244150044Sle				snprintf(p->volume, GV_MAXVOLNAME, "%s",
245150044Sle				    volume);
246150044Sle			}
247130391Sle
248150044Sle			/*
249150044Sle			 * Set default plex name for following subdisk
250150044Sle			 * definitions.
251150044Sle			 */
252150044Sle			strncpy(plex, p->name, GV_MAXPLEXNAME);
253130391Sle
254150044Sle			snprintf(buf1, sizeof(buf1), "plex%d", plexes);
255150044Sle			gctl_ro_param(req, buf1, sizeof(*p), p);
256150044Sle			plexes++;
257130391Sle
258150044Sle		/* Subdisk definition. */
259150044Sle		} else if (!strcmp(token[0], "sd")) {
260150044Sle			s = gv_new_sd(tokens, token);
261150044Sle			if (s == NULL) {
262150044Sle				warnx("line %d: invalid subdisk "
263150044Sle				    "definition:", line);
264150044Sle				warnx("line %d: '%s'", line, original);
265150044Sle				errors++;
266150044Sle				line++;
267150044Sle				continue;
268150044Sle			}
269130391Sle
270150044Sle			/* Default name. */
271150044Sle			if (strlen(s->name) == 0) {
272150044Sle				snprintf(s->name, GV_MAXSDNAME, "%s.s%d",
273150044Sle				    plex, sd_in_plex++);
274150044Sle			}
275150044Sle
276150044Sle			/* Default plex. */
277150044Sle			if (strlen(s->plex) == 0)
278150044Sle				snprintf(s->plex, GV_MAXPLEXNAME, "%s", plex);
279150044Sle
280150044Sle			snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
281150044Sle			gctl_ro_param(req, buf1, sizeof(*s), s);
282150044Sle			subdisks++;
283150044Sle
284150044Sle		/* Subdisk definition. */
285150044Sle		} else if (!strcmp(token[0], "drive")) {
286150044Sle			d = gv_new_drive(tokens, token);
287150044Sle			if (d == NULL) {
288150044Sle				warnx("line %d: invalid drive definition:",
289150044Sle				    line);
290130391Sle				warnx("line %d: '%s'", line, original);
291130391Sle				errors++;
292150044Sle				line++;
293150044Sle				continue;
294130391Sle			}
295150044Sle
296150044Sle			snprintf(buf1, sizeof(buf1), "drive%d", drives);
297150044Sle			gctl_ro_param(req, buf1, sizeof(*d), d);
298150044Sle			drives++;
299150044Sle
300150044Sle		/* Everything else is bogus. */
301150044Sle		} else {
302150044Sle			warnx("line %d: invalid definition:", line);
303150044Sle			warnx("line %d: '%s'", line, original);
304150044Sle			errors++;
305130391Sle		}
306130391Sle		line++;
307130391Sle	}
308130391Sle
309130391Sle	fclose(tmp);
310130391Sle	unlink(tmpfile);
311130391Sle
312130391Sle	if (!errors && (volumes || plexes || subdisks || drives)) {
313130391Sle		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
314130391Sle		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
315130391Sle		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
316130391Sle		gctl_ro_param(req, "drives", sizeof(int), &drives);
317130391Sle		errstr = gctl_issue(req);
318130391Sle		if (errstr != NULL)
319130391Sle			warnx("create failed: %s", errstr);
320130391Sle	}
321130391Sle	gctl_free(req);
322130391Sle	gvinum_list(0, NULL);
323130391Sle}
324130391Sle
325130391Slevoid
326130391Slegvinum_help(void)
327130391Sle{
328130391Sle	printf("COMMANDS\n"
329152616Sle	    "checkparity [-f] plex\n"
330152616Sle	    "        Check the parity blocks of a RAID-5 plex.\n"
331152616Sle	    "create description-file\n"
332152616Sle	    "        Create as per description-file or open editor.\n"
333152616Sle	    "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
334130391Sle	    "        List information about specified objects.\n"
335152616Sle	    "ld [-r] [-v] [-V] [volume]\n"
336130391Sle	    "        List information about drives.\n"
337152616Sle	    "ls [-r] [-v] [-V] [subdisk]\n"
338130391Sle	    "        List information about subdisks.\n"
339152616Sle	    "lp [-r] [-v] [-V] [plex]\n"
340130391Sle	    "        List information about plexes.\n"
341152616Sle	    "lv [-r] [-v] [-V] [volume]\n"
342130391Sle	    "        List information about volumes.\n"
343130391Sle	    "move | mv -f drive object ...\n"
344130391Sle	    "        Move the object(s) to the specified drive.\n"
345130391Sle	    "quit    Exit the vinum program when running in interactive mode."
346130391Sle	    "  Nor-\n"
347130391Sle	    "        mally this would be done by entering the EOF character.\n"
348130391Sle	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
349130391Sle	    "        Change the name of the specified object.\n"
350152616Sle	    "rebuildparity plex [-f]\n"
351152616Sle	    "        Rebuild the parity blocks of a RAID-5 plex.\n"
352152616Sle	    "rm [-r] volume | plex | subdisk | drive\n"
353130391Sle	    "        Remove an object.\n"
354130391Sle	    "saveconfig\n"
355130391Sle	    "        Save vinum configuration to disk after configuration"
356130391Sle	    " failures.\n"
357152616Sle	    "setstate [-f] state [volume | plex | subdisk | drive]\n"
358130391Sle	    "        Set state without influencing other objects, for"
359130391Sle	    " diagnostic pur-\n"
360130391Sle	    "        poses only.\n"
361152616Sle	    "start [-S size] volume | plex | subdisk\n"
362130391Sle	    "        Allow the system to access the objects.\n"
363130391Sle	);
364130391Sle
365130391Sle	return;
366130391Sle}
367130391Sle
368130391Slevoid
369138112Slegvinum_setstate(int argc, char **argv)
370138112Sle{
371138112Sle	struct gctl_req *req;
372138112Sle	int flags, i;
373138112Sle	const char *errstr;
374138112Sle
375138112Sle	flags = 0;
376138112Sle
377138112Sle	optreset = 1;
378138112Sle	optind = 1;
379138112Sle
380138112Sle	while ((i = getopt(argc, argv, "f")) != -1) {
381138112Sle		switch (i) {
382138112Sle		case 'f':
383138112Sle			flags |= GV_FLAG_F;
384138112Sle			break;
385138112Sle		case '?':
386138112Sle		default:
387138112Sle			warn("invalid flag: %c", i);
388138112Sle			return;
389138112Sle		}
390138112Sle	}
391138112Sle
392138112Sle	argc -= optind;
393138112Sle	argv += optind;
394138112Sle
395138112Sle	if (argc != 2) {
396138112Sle		warnx("usage: setstate [-f] <state> <obj>");
397138112Sle		return;
398138112Sle	}
399138112Sle
400138112Sle	/*
401138112Sle	 * XXX: This hack is needed to avoid tripping over (now) invalid
402138112Sle	 * 'classic' vinum states and will go away later.
403138112Sle	 */
404138112Sle	if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
405138112Sle	    strcmp(argv[0], "stale")) {
406138112Sle		warnx("invalid state '%s'", argv[0]);
407138112Sle		return;
408138112Sle	}
409138112Sle
410138112Sle	req = gctl_get_handle();
411138112Sle	gctl_ro_param(req, "class", -1, "VINUM");
412138112Sle	gctl_ro_param(req, "verb", -1, "setstate");
413138112Sle	gctl_ro_param(req, "state", -1, argv[0]);
414138112Sle	gctl_ro_param(req, "object", -1, argv[1]);
415138112Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
416138112Sle
417138112Sle	errstr = gctl_issue(req);
418138112Sle	if (errstr != NULL)
419138112Sle		warnx("%s", errstr);
420138112Sle	gctl_free(req);
421138112Sle}
422138112Sle
423138112Slevoid
424130391Slegvinum_list(int argc, char **argv)
425130391Sle{
426130391Sle	struct gctl_req *req;
427130391Sle	int flags, i, j;
428130391Sle	const char *errstr;
429130391Sle	char buf[20], *cmd, config[GV_CFG_LEN + 1];
430130391Sle
431130391Sle	flags = 0;
432130391Sle	cmd = "list";
433130391Sle
434130391Sle	if (argc) {
435130391Sle		optreset = 1;
436130391Sle		optind = 1;
437130391Sle		cmd = argv[0];
438130391Sle		while ((j = getopt(argc, argv, "rsvV")) != -1) {
439130391Sle			switch (j) {
440130391Sle			case 'r':
441130391Sle				flags |= GV_FLAG_R;
442130391Sle				break;
443130391Sle			case 's':
444130391Sle				flags |= GV_FLAG_S;
445130391Sle				break;
446130391Sle			case 'v':
447130391Sle				flags |= GV_FLAG_V;
448130391Sle				break;
449130391Sle			case 'V':
450130391Sle				flags |= GV_FLAG_V;
451130391Sle				flags |= GV_FLAG_VV;
452130391Sle				break;
453130391Sle			case '?':
454130391Sle			default:
455130391Sle				return;
456130391Sle			}
457130391Sle		}
458130391Sle		argc -= optind;
459130391Sle		argv += optind;
460130391Sle
461130391Sle	}
462130391Sle
463130391Sle	req = gctl_get_handle();
464130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
465130391Sle	gctl_ro_param(req, "verb", -1, "list");
466130391Sle	gctl_ro_param(req, "cmd", -1, cmd);
467130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
468130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
469130391Sle	gctl_rw_param(req, "config", sizeof(config), config);
470130391Sle	if (argc) {
471130391Sle		for (i = 0; i < argc; i++) {
472130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
473130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
474130391Sle		}
475130391Sle	}
476130391Sle	errstr = gctl_issue(req);
477130391Sle	if (errstr != NULL) {
478130391Sle		warnx("can't get configuration: %s", errstr);
479130391Sle		gctl_free(req);
480130391Sle		return;
481130391Sle	}
482130391Sle
483130391Sle	printf("%s", config);
484130391Sle	gctl_free(req);
485130391Sle	return;
486130391Sle}
487130391Sle
488152616Sle/* Note that move is currently of form '[-r] target object [...]' */
489130391Slevoid
490152616Slegvinum_move(int argc, char **argv)
491152616Sle{
492152616Sle	struct gctl_req *req;
493152616Sle	const char *errstr;
494152616Sle	char buf[20];
495152616Sle	int flags, i, j;
496152616Sle
497152616Sle	flags = 0;
498152616Sle	if (argc) {
499152616Sle		optreset = 1;
500152616Sle		optind = 1;
501152616Sle		while ((j = getopt(argc, argv, "f")) != -1) {
502152616Sle			switch (j) {
503152616Sle			case 'f':
504152616Sle				flags |= GV_FLAG_F;
505152616Sle				break;
506152616Sle			case '?':
507152616Sle			default:
508152616Sle				return;
509152616Sle			}
510152616Sle		}
511152616Sle		argc -= optind;
512152616Sle		argv += optind;
513152616Sle	}
514152616Sle
515152616Sle	switch (argc) {
516152616Sle		case 0:
517152616Sle			warnx("no destination or object(s) to move specified");
518152616Sle			return;
519152616Sle		case 1:
520152616Sle			warnx("no object(s) to move specified");
521152616Sle			return;
522152616Sle		default:
523152616Sle			break;
524152616Sle	}
525152616Sle
526152616Sle	req = gctl_get_handle();
527152616Sle	gctl_ro_param(req, "class", -1, "VINUM");
528152616Sle	gctl_ro_param(req, "verb", -1, "move");
529152616Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
530152616Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
531152616Sle	gctl_ro_param(req, "destination", -1, argv[0]);
532152616Sle	for (i = 1; i < argc; i++) {
533152616Sle		snprintf(buf, sizeof(buf), "argv%d", i);
534152616Sle		gctl_ro_param(req, buf, -1, argv[i]);
535152616Sle	}
536152631Sle	errstr = gctl_issue(req);
537152616Sle	if (errstr != NULL)
538152616Sle		warnx("can't move object(s):  %s", errstr);
539152616Sle	gctl_free(req);
540152616Sle	return;
541152616Sle}
542152616Sle
543152616Slevoid
544130391Slegvinum_printconfig(int argc, char **argv)
545130391Sle{
546130391Sle	printconfig(stdout, "");
547130391Sle}
548130391Sle
549130391Slevoid
550138110Slegvinum_parityop(int argc, char **argv, int rebuild)
551138110Sle{
552138110Sle	struct gctl_req *req;
553138110Sle	int flags, i, rv;
554138110Sle	off_t offset;
555138110Sle	const char *errstr;
556138110Sle	char *op, *msg;
557138110Sle
558138110Sle	if (rebuild) {
559138110Sle		op = "rebuildparity";
560138110Sle		msg = "Rebuilding";
561138110Sle	} else {
562138110Sle		op = "checkparity";
563138110Sle		msg = "Checking";
564138110Sle	}
565138110Sle
566138110Sle	optreset = 1;
567138110Sle	optind = 1;
568138110Sle	flags = 0;
569138110Sle	while ((i = getopt(argc, argv, "fv")) != -1) {
570138110Sle		switch (i) {
571138110Sle		case 'f':
572138110Sle			flags |= GV_FLAG_F;
573138110Sle			break;
574138110Sle		case 'v':
575138110Sle			flags |= GV_FLAG_V;
576138110Sle			break;
577138110Sle		case '?':
578138110Sle		default:
579138110Sle			warnx("invalid flag '%c'", i);
580138110Sle			return;
581138110Sle		}
582138110Sle	}
583138110Sle	argc -= optind;
584138110Sle	argv += optind;
585138110Sle
586138110Sle	if (argc != 1) {
587138110Sle		warn("usage: %s [-f] [-v] <plex>", op);
588138110Sle		return;
589138110Sle	}
590138110Sle
591138110Sle	do {
592138110Sle		rv = 0;
593138110Sle		req = gctl_get_handle();
594138110Sle		gctl_ro_param(req, "class", -1, "VINUM");
595138110Sle		gctl_ro_param(req, "verb", -1, "parityop");
596138110Sle		gctl_ro_param(req, "flags", sizeof(int), &flags);
597138110Sle		gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
598138110Sle		gctl_rw_param(req, "rv", sizeof(int), &rv);
599138110Sle		gctl_rw_param(req, "offset", sizeof(off_t), &offset);
600138110Sle		gctl_ro_param(req, "plex", -1, argv[0]);
601138110Sle		errstr = gctl_issue(req);
602138110Sle		if (errstr) {
603138110Sle			warnx("%s\n", errstr);
604138110Sle			gctl_free(req);
605138110Sle			break;
606138110Sle		}
607138110Sle		gctl_free(req);
608138110Sle		if (flags & GV_FLAG_V) {
609138110Sle			printf("\r%s at %s ... ", msg,
610138110Sle			    gv_roughlength(offset, 1));
611138110Sle		}
612138110Sle		if (rv == 1) {
613138110Sle			printf("Parity incorrect at offset 0x%jx\n",
614138110Sle			    (intmax_t)offset);
615138110Sle			if (!rebuild)
616138110Sle				break;
617138110Sle		}
618138110Sle		fflush(stdout);
619138110Sle
620138110Sle		/* Clear the -f flag. */
621138110Sle		flags &= ~GV_FLAG_F;
622138110Sle	} while (rv >= 0);
623138110Sle
624138110Sle	if ((rv == 2) && (flags & GV_FLAG_V)) {
625138110Sle		if (rebuild)
626138110Sle			printf("Rebuilt parity on %s\n", argv[0]);
627138110Sle		else
628138110Sle			printf("%s has correct parity\n", argv[0]);
629138110Sle	}
630138110Sle}
631138110Sle
632138110Slevoid
633152616Slegvinum_rename(int argc, char **argv)
634152616Sle{
635152616Sle	struct gctl_req *req;
636152616Sle	const char *errstr;
637152616Sle	int flags, j;
638152616Sle
639152616Sle	flags = 0;
640152616Sle
641152616Sle	if (argc) {
642152616Sle		optreset = 1;
643152616Sle		optind = 1;
644152616Sle		while ((j = getopt(argc, argv, "r")) != -1) {
645152616Sle			switch (j) {
646152616Sle			case 'r':
647152616Sle				flags |= GV_FLAG_R;
648152616Sle				break;
649152616Sle			case '?':
650152616Sle			default:
651152616Sle				return;
652152616Sle			}
653152616Sle		}
654152616Sle		argc -= optind;
655152616Sle		argv += optind;
656152616Sle	}
657152616Sle
658152616Sle	switch (argc) {
659152616Sle		case 0:
660152616Sle			warnx("no object to rename specified");
661152616Sle			return;
662152616Sle		case 1:
663152616Sle			warnx("no new name specified");
664152616Sle			return;
665152616Sle		case 2:
666152616Sle			break;
667152616Sle		default:
668152616Sle			warnx("more than one new name specified");
669152616Sle			return;
670152616Sle	}
671152616Sle
672152616Sle	req = gctl_get_handle();
673152616Sle	gctl_ro_param(req, "class", -1, "VINUM");
674152616Sle	gctl_ro_param(req, "verb", -1, "rename");
675152616Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
676152616Sle	gctl_ro_param(req, "object", -1, argv[0]);
677152616Sle	gctl_ro_param(req, "newname", -1, argv[1]);
678152631Sle	errstr = gctl_issue(req);
679152616Sle	if (errstr != NULL)
680152616Sle		warnx("can't rename object:  %s", errstr);
681152616Sle	gctl_free(req);
682152616Sle	return;
683152616Sle}
684152616Sle
685152616Slevoid
686130391Slegvinum_rm(int argc, char **argv)
687130391Sle{
688130391Sle	struct gctl_req *req;
689130391Sle	int flags, i, j;
690130391Sle	const char *errstr;
691130391Sle	char buf[20], *cmd;
692130391Sle
693130391Sle	cmd = argv[0];
694130391Sle	flags = 0;
695130391Sle	optreset = 1;
696130391Sle	optind = 1;
697130391Sle	while ((j = getopt(argc, argv, "r")) != -1) {
698130391Sle		switch (j) {
699130391Sle		case 'r':
700130391Sle			flags |= GV_FLAG_R;
701130391Sle			break;
702130391Sle		case '?':
703130391Sle		default:
704130391Sle			return;
705130391Sle		}
706130391Sle	}
707130391Sle	argc -= optind;
708130391Sle	argv += optind;
709130391Sle
710130391Sle	req = gctl_get_handle();
711130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
712130391Sle	gctl_ro_param(req, "verb", -1, "remove");
713130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
714130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
715130391Sle	if (argc) {
716130391Sle		for (i = 0; i < argc; i++) {
717130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
718130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
719130391Sle		}
720130391Sle	}
721130391Sle	errstr = gctl_issue(req);
722130391Sle	if (errstr != NULL) {
723130391Sle		warnx("can't remove: %s", errstr);
724130391Sle		gctl_free(req);
725130391Sle		return;
726130391Sle	}
727130391Sle	gctl_free(req);
728130391Sle	gvinum_list(0, NULL);
729130391Sle}
730130391Sle
731130391Slevoid
732130391Slegvinum_saveconfig(void)
733130391Sle{
734130391Sle	struct gctl_req *req;
735130391Sle	const char *errstr;
736130391Sle
737130391Sle	req = gctl_get_handle();
738130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
739130391Sle	gctl_ro_param(req, "verb", -1, "saveconfig");
740130391Sle	errstr = gctl_issue(req);
741130391Sle	if (errstr != NULL)
742130391Sle		warnx("can't save configuration: %s", errstr);
743130391Sle	gctl_free(req);
744130391Sle}
745130391Sle
746130391Slevoid
747130391Slegvinum_start(int argc, char **argv)
748130391Sle{
749130391Sle	struct gctl_req *req;
750130391Sle	int i, initsize, j;
751130391Sle	const char *errstr;
752130391Sle	char buf[20];
753130391Sle
754130391Sle	/* 'start' with no arguments is a no-op. */
755130391Sle	if (argc == 1)
756130391Sle		return;
757130391Sle
758130391Sle	initsize = 0;
759130391Sle
760130391Sle	optreset = 1;
761130391Sle	optind = 1;
762130391Sle	while ((j = getopt(argc, argv, "S")) != -1) {
763130391Sle		switch (j) {
764130391Sle		case 'S':
765130391Sle			initsize = atoi(optarg);
766130391Sle			break;
767130391Sle		case '?':
768130391Sle		default:
769130391Sle			return;
770130391Sle		}
771130391Sle	}
772130391Sle	argc -= optind;
773130391Sle	argv += optind;
774130391Sle
775130391Sle	if (!initsize)
776130391Sle		initsize = 512;
777130391Sle
778130391Sle	req = gctl_get_handle();
779130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
780130391Sle	gctl_ro_param(req, "verb", -1, "start");
781130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
782130391Sle	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
783130391Sle	if (argc) {
784130391Sle		for (i = 0; i < argc; i++) {
785130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
786130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
787130391Sle		}
788130391Sle	}
789130391Sle	errstr = gctl_issue(req);
790130391Sle	if (errstr != NULL) {
791130391Sle		warnx("can't start: %s", errstr);
792130391Sle		gctl_free(req);
793130391Sle		return;
794130391Sle	}
795130391Sle
796130391Sle	gctl_free(req);
797130391Sle	gvinum_list(0, NULL);
798130391Sle}
799130391Sle
800130391Slevoid
801130391Slegvinum_stop(int argc, char **argv)
802130391Sle{
803130391Sle	int fileid;
804130391Sle
805130391Sle	fileid = kldfind(GVINUMMOD);
806130391Sle	if (fileid == -1) {
807130391Sle		warn("cannot find " GVINUMMOD);
808130391Sle		return;
809130391Sle	}
810130391Sle	if (kldunload(fileid) != 0) {
811130391Sle		warn("cannot unload " GVINUMMOD);
812130391Sle		return;
813130391Sle	}
814130391Sle
815130391Sle	warnx(GVINUMMOD " unloaded");
816130391Sle	exit(0);
817130391Sle}
818130391Sle
819130391Slevoid
820130391Sleparseline(int argc, char **argv)
821130391Sle{
822130391Sle	if (argc <= 0)
823130391Sle		return;
824130391Sle
825150044Sle	if (!strcmp(argv[0], "create"))
826130391Sle		gvinum_create(argc, argv);
827130391Sle	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
828130391Sle		exit(0);
829130391Sle	else if (!strcmp(argv[0], "help"))
830130391Sle		gvinum_help();
831130391Sle	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
832130391Sle		gvinum_list(argc, argv);
833130391Sle	else if (!strcmp(argv[0], "ld"))
834130391Sle		gvinum_list(argc, argv);
835130391Sle	else if (!strcmp(argv[0], "lp"))
836130391Sle		gvinum_list(argc, argv);
837130391Sle	else if (!strcmp(argv[0], "ls"))
838130391Sle		gvinum_list(argc, argv);
839130391Sle	else if (!strcmp(argv[0], "lv"))
840130391Sle		gvinum_list(argc, argv);
841152616Sle	else if (!strcmp(argv[0], "move"))
842152616Sle		gvinum_move(argc, argv);
843152616Sle	else if (!strcmp(argv[0], "mv"))
844152616Sle		gvinum_move(argc, argv);
845130391Sle	else if (!strcmp(argv[0], "printconfig"))
846130391Sle		gvinum_printconfig(argc, argv);
847152616Sle	else if (!strcmp(argv[0], "rename"))
848152616Sle		gvinum_rename(argc, argv);
849130391Sle	else if (!strcmp(argv[0], "rm"))
850130391Sle		gvinum_rm(argc, argv);
851130391Sle	else if (!strcmp(argv[0], "saveconfig"))
852130391Sle		gvinum_saveconfig();
853138112Sle	else if (!strcmp(argv[0], "setstate"))
854138112Sle		gvinum_setstate(argc, argv);
855130391Sle	else if (!strcmp(argv[0], "start"))
856130391Sle		gvinum_start(argc, argv);
857130391Sle	else if (!strcmp(argv[0], "stop"))
858130391Sle		gvinum_stop(argc, argv);
859138110Sle	else if (!strcmp(argv[0], "checkparity"))
860138110Sle		gvinum_parityop(argc, argv, 0);
861138110Sle	else if (!strcmp(argv[0], "rebuildparity"))
862138110Sle		gvinum_parityop(argc, argv, 1);
863130391Sle	else
864130391Sle		printf("unknown command '%s'\n", argv[0]);
865130391Sle
866130391Sle	return;
867130391Sle}
868130391Sle
869130391Sle/*
870130391Sle * The guts of printconfig.  This is called from gvinum_printconfig and from
871130391Sle * gvinum_create when called without an argument, in order to give the user
872130391Sle * something to edit.
873130391Sle */
874130391Slevoid
875130391Sleprintconfig(FILE *of, char *comment)
876130391Sle{
877130391Sle	struct gctl_req *req;
878130391Sle	struct utsname uname_s;
879130391Sle	const char *errstr;
880130391Sle	time_t now;
881130391Sle	char buf[GV_CFG_LEN + 1];
882130391Sle
883130391Sle	uname(&uname_s);
884130391Sle	time(&now);
885130391Sle
886130391Sle	req = gctl_get_handle();
887130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
888130391Sle	gctl_ro_param(req, "verb", -1, "getconfig");
889130391Sle	gctl_ro_param(req, "comment", -1, comment);
890130391Sle	gctl_rw_param(req, "config", sizeof(buf), buf);
891130391Sle	errstr = gctl_issue(req);
892130391Sle	if (errstr != NULL) {
893130391Sle		warnx("can't get configuration: %s", errstr);
894130391Sle		return;
895130391Sle	}
896130391Sle	gctl_free(req);
897130391Sle
898130391Sle	fprintf(of, "# Vinum configuration of %s, saved at %s",
899130391Sle	    uname_s.nodename,
900130391Sle	    ctime(&now));
901130391Sle
902130391Sle	if (*comment != '\0')
903130391Sle	    fprintf(of, "# Current configuration:\n");
904130391Sle
905130391Sle	fprintf(of, buf);
906130391Sle}
907