gvinum.c revision 190881
174462Salfred/*
274462Salfred *  Copyright (c) 2004 Lukas Ertl
3261046Smav *  Copyright (c) 2005 Chris Jones
4261046Smav *  Copyright (c) 2007 Ulf Lilleengen
5261046Smav *  All rights reserved.
68870Srgrimes *
7261046Smav * Portions of this software were developed for the FreeBSD Project
8261046Smav * by Chris Jones thanks to the support of Google's Summer of Code
9261046Smav * program and mentoring by Lukas Ertl.
10261046Smav *
11261046Smav * Redistribution and use in source and binary forms, with or without
12261046Smav * modification, are permitted provided that the following conditions
13261046Smav * are met:
14261046Smav * 1. Redistributions of source code must retain the above copyright
15261046Smav *    notice, this list of conditions and the following disclaimer.
16261046Smav * 2. Redistributions in binary form must reproduce the above copyright
17261046Smav *    notice, this list of conditions and the following disclaimer in the
18261046Smav *    documentation and/or other materials provided with the distribution.
19261046Smav *
20261046Smav * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21261046Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22261046Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23261046Smav * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24261046Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25261046Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26261046Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27261046Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28261046Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291901Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3074462Salfred * SUCH DAMAGE.
3174462Salfred *
3274462Salfred * $FreeBSD: head/sbin/gvinum/gvinum.c 190881 2009-04-10 08:50:14Z lulf $
331901Swollman */
341901Swollman
35136581Sobrien#include <sys/param.h>
3692990Sobrien#include <sys/linker.h>
371901Swollman#include <sys/lock.h>
3892990Sobrien#include <sys/module.h>
3992990Sobrien#include <sys/mutex.h>
401901Swollman#include <sys/queue.h>
418870Srgrimes#include <sys/utsname.h>
421901Swollman
4374462Salfred#include <geom/vinum/geom_vinum_var.h>
441901Swollman#include <geom/vinum/geom_vinum_share.h>
451901Swollman
461901Swollman#include <ctype.h>
4775094Siedowse#include <err.h>
4874462Salfred#include <errno.h>
4921070Speter#include <libgeom.h>
501901Swollman#include <stdint.h>
5174462Salfred#include <stdio.h>
5274462Salfred#include <stdlib.h>
5374462Salfred#include <paths.h>
541901Swollman#include <readline/readline.h>
5574462Salfred#include <readline/history.h>
5611666Sphk#include <unistd.h>
5771579Sdeischen
58156090Sdeischen#include "gvinum.h"
591901Swollman
6074462Salfredvoid	gvinum_attach(int, char **);
6174462Salfredvoid	gvinum_concat(int, char **);
6274462Salfredvoid	gvinum_create(int, char **);
631901Swollmanvoid	gvinum_detach(int, char **);
6474462Salfredvoid	gvinum_help(void);
6574462Salfredvoid	gvinum_list(int, char **);
6674462Salfredvoid	gvinum_move(int, char **);
6774462Salfredvoid	gvinum_mirror(int, char **);
6874462Salfredvoid	gvinum_parityop(int, char **, int);
6974462Salfredvoid	gvinum_printconfig(int, char **);
7074462Salfredvoid	gvinum_raid5(int, char **);
7174462Salfredvoid	gvinum_rename(int, char **);
7274462Salfredvoid	gvinum_resetconfig(void);
7374462Salfredvoid	gvinum_rm(int, char **);
7474462Salfredvoid	gvinum_saveconfig(void);
7574462Salfredvoid	gvinum_setstate(int, char **);
7674462Salfredvoid	gvinum_start(int, char **);
7774462Salfredvoid	gvinum_stop(int, char **);
78204950Sjhbvoid	gvinum_stripe(int, char **);
79204950Sjhbvoid	parseline(int, char **);
80204950Sjhbvoid	printconfig(FILE *, char *);
8174462Salfred
82204950Sjhbchar	*create_drive(char *);
8392905Sobrienvoid	 create_volume(int, char **, char *);
8474462Salfredchar	*find_name(const char *, int, int);
8574462Salfredchar	*find_pattern(char *, char *);
8674462Salfred
871901Swollmanint
8874462Salfredmain(int argc, char **argv)
8974462Salfred{
9074462Salfred	int line, tokens;
9174462Salfred	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
9274462Salfred
9374462Salfred	/* Load the module if necessary. */
9474462Salfred	if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
9574462Salfred		err(1, GVINUMMOD ": Kernel module not available");
9674462Salfred
97204950Sjhb	/* Arguments given on the command line. */
98204950Sjhb	if (argc > 1) {
99204950Sjhb		argc--;
100204950Sjhb		argv++;
101204950Sjhb		parseline(argc, argv);
102204950Sjhb
103204950Sjhb	/* Interactive mode. */
10474462Salfred	} else {
10574462Salfred		for (;;) {
10674462Salfred			inputline = readline("gvinum -> ");
10774462Salfred			if (inputline == NULL) {
10874462Salfred				if (ferror(stdin)) {
10974462Salfred					err(1, "can't read input");
11074462Salfred				} else {
11174462Salfred					printf("\n");
11274462Salfred					exit(0);
11374462Salfred				}
11474462Salfred			} else if (*inputline) {
11574462Salfred				add_history(inputline);
11674462Salfred				strcpy(buffer, inputline);
11774462Salfred				free(inputline);
11874462Salfred				line++;		    /* count the lines */
11974462Salfred				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
12074462Salfred				if (tokens)
12174462Salfred					parseline(tokens, token);
12274462Salfred			}
1231901Swollman		}
1241901Swollman	}
12574462Salfred	exit(0);
1261901Swollman}
12774462Salfred
12874462Salfred/* Attach a plex to a volume or a subdisk to a plex. */
12974462Salfredvoid
130204950Sjhbgvinum_attach(int argc, char **argv)
131204950Sjhb{
132204950Sjhb	struct gctl_req *req;
133204950Sjhb	const char *errstr;
134204950Sjhb	int rename;
13574462Salfred	off_t offset;
13674462Salfred
1371901Swollman	rename = 0;
13874462Salfred	offset = -1;
13974462Salfred	if (argc < 3) {
14074462Salfred		warnx("usage:\tattach <subdisk> <plex> [rename] "
14174462Salfred		    "[<plexoffset>]\n"
14274462Salfred		    "\tattach <plex> <volume> [rename]");
14374462Salfred		return;
14474462Salfred	}
14574462Salfred	if (argc > 3) {
14674462Salfred		if (!strcmp(argv[3], "rename")) {
14774462Salfred			rename = 1;
14874462Salfred			if (argc == 5)
14974462Salfred				offset = strtol(argv[4], NULL, 0);
15074462Salfred		} else
1511901Swollman			offset = strtol(argv[3], NULL, 0);
152121651Smbr	}
15374462Salfred	req = gctl_get_handle();
15474462Salfred	gctl_ro_param(req, "class", -1, "VINUM");
15574462Salfred	gctl_ro_param(req, "verb", -1, "attach");
15674462Salfred	gctl_ro_param(req, "child", -1, argv[1]);
15774462Salfred	gctl_ro_param(req, "parent", -1, argv[2]);
15874462Salfred	gctl_ro_param(req, "offset", sizeof(off_t), &offset);
15974462Salfred	gctl_ro_param(req, "rename", sizeof(int), &rename);
16074462Salfred	errstr = gctl_issue(req);
16174462Salfred	if (errstr != NULL)
16274462Salfred		warnx("attach failed: %s", errstr);
16374462Salfred	gctl_free(req);
16474462Salfred}
16574462Salfred
16674462Salfredvoid
16774462Salfredgvinum_create(int argc, char **argv)
16874462Salfred{
16974462Salfred	struct gctl_req *req;
17074462Salfred	struct gv_drive *d;
1711901Swollman	struct gv_plex *p;
17274462Salfred	struct gv_sd *s;
17374462Salfred	struct gv_volume *v;
17474462Salfred	FILE *tmp;
17574462Salfred	int drives, errors, fd, flags, i, line, plexes, plex_in_volume;
17674462Salfred	int sd_in_plex, status, subdisks, tokens, undeffd, volumes;
1771901Swollman	const char *errstr;
1781901Swollman	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname;
17974462Salfred	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
18074462Salfred	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
18174462Salfred
18274462Salfred	tmp = NULL;
18374462Salfred	flags = 0;
18474462Salfred	for (i = 1; i < argc; i++) {
18574462Salfred		/* Force flag used to ignore already created drives. */
18674462Salfred		if (!strcmp(argv[i], "-f")) {
18774462Salfred			flags |= GV_FLAG_F;
18874462Salfred		/* Else it must be a file. */
18974462Salfred		} else {
19074462Salfred			if ((tmp = fopen(argv[1], "r")) == NULL) {
19174462Salfred				warn("can't open '%s' for reading", argv[1]);
19274462Salfred				return;
19374462Salfred			}
1941901Swollman		}
1951901Swollman	}
19674462Salfred
19774462Salfred	/* We didn't get a file. */
1981901Swollman	if (tmp == NULL) {
1998870Srgrimes		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
2001901Swollman
2011901Swollman		if ((fd = mkstemp(tmpfile)) == -1) {
2021901Swollman			warn("temporary file not accessible");
20374462Salfred			return;
20474462Salfred		}
2051901Swollman		if ((tmp = fdopen(fd, "w")) == NULL) {
206			warn("can't open '%s' for writing", tmpfile);
207			return;
208		}
209		printconfig(tmp, "# ");
210		fclose(tmp);
211
212		ed = getenv("EDITOR");
213		if (ed == NULL)
214			ed = _PATH_VI;
215
216		snprintf(commandline, sizeof(commandline), "%s %s", ed,
217		    tmpfile);
218		status = system(commandline);
219		if (status != 0) {
220			warn("couldn't exec %s; status: %d", ed, status);
221			return;
222		}
223
224		if ((tmp = fopen(tmpfile, "r")) == NULL) {
225			warn("can't open '%s' for reading", tmpfile);
226			return;
227		}
228	}
229
230	req = gctl_get_handle();
231	gctl_ro_param(req, "class", -1, "VINUM");
232	gctl_ro_param(req, "verb", -1, "create");
233	gctl_ro_param(req, "flags", sizeof(int), &flags);
234
235	drives = volumes = plexes = subdisks = 0;
236	plex_in_volume = sd_in_plex = undeffd = 0;
237	plex[0] = '\0';
238	errors = 0;
239	line = 1;
240	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
241
242		/* Skip empty lines and comments. */
243		if (*buf == '\0' || *buf == '#') {
244			line++;
245			continue;
246		}
247
248		/* Kill off the newline. */
249		buf[strlen(buf) - 1] = '\0';
250
251		/*
252		 * Copy the original input line in case we need it for error
253		 * output.
254		 */
255		strlcpy(original, buf, sizeof(original));
256
257		tokens = gv_tokenize(buf, token, GV_MAXARGS);
258		if (tokens <= 0) {
259			line++;
260			continue;
261		}
262
263		/* Volume definition. */
264		if (!strcmp(token[0], "volume")) {
265			v = gv_new_volume(tokens, token);
266			if (v == NULL) {
267				warnx("line %d: invalid volume definition",
268				    line);
269				warnx("line %d: '%s'", line, original);
270				errors++;
271				line++;
272				continue;
273			}
274
275			/* Reset plex count for this volume. */
276			plex_in_volume = 0;
277
278			/*
279			 * Set default volume name for following plex
280			 * definitions.
281			 */
282			strlcpy(volume, v->name, sizeof(volume));
283
284			snprintf(buf1, sizeof(buf1), "volume%d", volumes);
285			gctl_ro_param(req, buf1, sizeof(*v), v);
286			volumes++;
287
288		/* Plex definition. */
289		} else if (!strcmp(token[0], "plex")) {
290			p = gv_new_plex(tokens, token);
291			if (p == NULL) {
292				warnx("line %d: invalid plex definition", line);
293				warnx("line %d: '%s'", line, original);
294				errors++;
295				line++;
296				continue;
297			}
298
299			/* Reset subdisk count for this plex. */
300			sd_in_plex = 0;
301
302			/* Default name. */
303			if (strlen(p->name) == 0) {
304				snprintf(p->name, sizeof(p->name), "%s.p%d",
305				    volume, plex_in_volume++);
306			}
307
308			/* Default volume. */
309			if (strlen(p->volume) == 0) {
310				snprintf(p->volume, sizeof(p->volume), "%s",
311				    volume);
312			}
313
314			/*
315			 * Set default plex name for following subdisk
316			 * definitions.
317			 */
318			strlcpy(plex, p->name, sizeof(plex));
319
320			snprintf(buf1, sizeof(buf1), "plex%d", plexes);
321			gctl_ro_param(req, buf1, sizeof(*p), p);
322			plexes++;
323
324		/* Subdisk definition. */
325		} else if (!strcmp(token[0], "sd")) {
326			s = gv_new_sd(tokens, token);
327			if (s == NULL) {
328				warnx("line %d: invalid subdisk "
329				    "definition:", line);
330				warnx("line %d: '%s'", line, original);
331				errors++;
332				line++;
333				continue;
334			}
335
336			/* Default name. */
337			if (strlen(s->name) == 0) {
338				if (strlen(plex) == 0) {
339					sdname = find_name("gvinumsubdisk.p",
340					    GV_TYPE_SD, GV_MAXSDNAME);
341					snprintf(s->name, sizeof(s->name),
342					    "%s.s%d", sdname, undeffd++);
343					free(sdname);
344				} else {
345					snprintf(s->name, sizeof(s->name),
346					    "%s.s%d",plex, sd_in_plex++);
347				}
348			}
349
350			/* Default plex. */
351			if (strlen(s->plex) == 0)
352				snprintf(s->plex, sizeof(s->plex), "%s", plex);
353
354			snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
355			gctl_ro_param(req, buf1, sizeof(*s), s);
356			subdisks++;
357
358		/* Subdisk definition. */
359		} else if (!strcmp(token[0], "drive")) {
360			d = gv_new_drive(tokens, token);
361			if (d == NULL) {
362				warnx("line %d: invalid drive definition:",
363				    line);
364				warnx("line %d: '%s'", line, original);
365				errors++;
366				line++;
367				continue;
368			}
369
370			snprintf(buf1, sizeof(buf1), "drive%d", drives);
371			gctl_ro_param(req, buf1, sizeof(*d), d);
372			drives++;
373
374		/* Everything else is bogus. */
375		} else {
376			warnx("line %d: invalid definition:", line);
377			warnx("line %d: '%s'", line, original);
378			errors++;
379		}
380		line++;
381	}
382
383	fclose(tmp);
384	unlink(tmpfile);
385
386	if (!errors && (volumes || plexes || subdisks || drives)) {
387		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
388		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
389		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
390		gctl_ro_param(req, "drives", sizeof(int), &drives);
391		errstr = gctl_issue(req);
392		if (errstr != NULL)
393			warnx("create failed: %s", errstr);
394	}
395	gctl_free(req);
396}
397
398/* Create a concatenated volume. */
399void
400gvinum_concat(int argc, char **argv)
401{
402
403	if (argc < 2) {
404		warnx("usage:\tconcat [-fv] [-n name] drives\n");
405		return;
406	}
407	create_volume(argc, argv, "concat");
408}
409
410
411/* Create a drive quick and dirty. */
412char *
413create_drive(char *device)
414{
415	struct gv_drive *d;
416	struct gctl_req *req;
417	const char *errstr;
418	char *drivename, *dname;
419	int drives, i, flags, volumes, subdisks, plexes;
420
421	flags = plexes = subdisks = volumes = 0;
422	drives = 1;
423	dname = NULL;
424
425	/* Strip away eventual /dev/ in front. */
426	if (strncmp(device, "/dev/", 5) == 0)
427		device += 5;
428
429	drivename = find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME);
430	if (drivename == NULL)
431		return (NULL);
432
433	req = gctl_get_handle();
434	gctl_ro_param(req, "class", -1, "VINUM");
435	gctl_ro_param(req, "verb", -1, "create");
436	d = gv_alloc_drive();
437	if (d == NULL)
438		err(1, "unable to allocate for gv_drive object");
439
440	strlcpy(d->name, drivename, sizeof(d->name));
441	strlcpy(d->device, device, sizeof(d->device));
442	gctl_ro_param(req, "drive0", sizeof(*d), d);
443	gctl_ro_param(req, "flags", sizeof(int), &flags);
444	gctl_ro_param(req, "drives", sizeof(int), &drives);
445	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
446	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
447	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
448	errstr = gctl_issue(req);
449	if (errstr != NULL) {
450		warnx("error creating drive: %s", errstr);
451		gctl_free(req);
452		return (NULL);
453	} else {
454		gctl_free(req);
455		/* XXX: This is needed because we have to make sure the drives
456		 * are created before we return. */
457		/* Loop until it's in the config. */
458		for (i = 0; i < 100000; i++) {
459			dname = find_name("gvinumdrive", GV_TYPE_DRIVE,
460			    GV_MAXDRIVENAME);
461			/* If we got a different name, quit. */
462			if (dname == NULL)
463				continue;
464			if (strcmp(dname, drivename)) {
465				free(dname);
466				return (drivename);
467			}
468			free(dname);
469			dname = NULL;
470			usleep(100000); /* Sleep for 0.1s */
471		}
472	}
473	gctl_free(req);
474	return (drivename);
475}
476
477/*
478 * General routine for creating a volume. Mainly for use by concat, mirror,
479 * raid5 and stripe commands.
480 */
481void
482create_volume(int argc, char **argv, char *verb)
483{
484	struct gctl_req *req;
485	const char *errstr;
486	char buf[BUFSIZ], *drivename, *volname;
487	int drives, flags, i;
488	off_t stripesize;
489
490	flags = 0;
491	drives = 0;
492	volname = NULL;
493	stripesize = 262144;
494
495	/* XXX: Should we check for argument length? */
496
497	req = gctl_get_handle();
498	gctl_ro_param(req, "class", -1, "VINUM");
499
500	for (i = 1; i < argc; i++) {
501		if (!strcmp(argv[i], "-f")) {
502			flags |= GV_FLAG_F;
503		} else if (!strcmp(argv[i], "-n")) {
504			volname = argv[++i];
505		} else if (!strcmp(argv[i], "-v")) {
506			flags |= GV_FLAG_V;
507		} else if (!strcmp(argv[i], "-s")) {
508			flags |= GV_FLAG_S;
509			if (!strcmp(verb, "raid5"))
510				stripesize = gv_sizespec(argv[++i]);
511		} else {
512			/* Assume it's a drive. */
513			snprintf(buf, sizeof(buf), "drive%d", drives++);
514
515			/* First we create the drive. */
516			drivename = create_drive(argv[i]);
517			if (drivename == NULL)
518				goto bad;
519			/* Then we add it to the request. */
520			gctl_ro_param(req, buf, -1, drivename);
521		}
522	}
523
524	gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize);
525
526	/* Find a free volume name. */
527	if (volname == NULL)
528		volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME);
529
530	/* Then we send a request to actually create the volumes. */
531	gctl_ro_param(req, "verb", -1, verb);
532	gctl_ro_param(req, "flags", sizeof(int), &flags);
533	gctl_ro_param(req, "drives", sizeof(int), &drives);
534	gctl_ro_param(req, "name", -1, volname);
535	errstr = gctl_issue(req);
536	if (errstr != NULL)
537		warnx("creating %s volume failed: %s", verb, errstr);
538bad:
539	gctl_free(req);
540}
541
542/* Parse a line of the config, return the word after <pattern>. */
543char *
544find_pattern(char *line, char *pattern)
545{
546	char *ptr;
547
548	ptr = strsep(&line, " ");
549	while (ptr != NULL) {
550		if (!strcmp(ptr, pattern)) {
551			/* Return the next. */
552			ptr = strsep(&line, " ");
553			return (ptr);
554		}
555		ptr = strsep(&line, " ");
556	}
557	return (NULL);
558}
559
560/* Find a free name for an object given a a prefix. */
561char *
562find_name(const char *prefix, int type, int namelen)
563{
564	struct gctl_req *req;
565	char comment[1], buf[GV_CFG_LEN - 1], *name, *sname, *ptr;
566	const char *errstr;
567	int i, n, begin, len, conflict;
568	char line[1024];
569
570	comment[0] = '\0';
571
572	/* Find a name. Fetch out configuration first. */
573	req = gctl_get_handle();
574	gctl_ro_param(req, "class", -1, "VINUM");
575	gctl_ro_param(req, "verb", -1, "getconfig");
576	gctl_ro_param(req, "comment", -1, comment);
577	gctl_rw_param(req, "config", sizeof(buf), buf);
578	errstr = gctl_issue(req);
579	if (errstr != NULL) {
580		warnx("can't get configuration: %s", errstr);
581		return (NULL);
582	}
583	gctl_free(req);
584
585	begin = 0;
586	len = strlen(buf);
587	i = 0;
588	sname = malloc(namelen + 1);
589
590	/* XXX: Max object setting? */
591	for (n = 0; n < 10000; n++) {
592		snprintf(sname, namelen, "%s%d", prefix, n);
593		conflict = 0;
594		begin = 0;
595		/* Loop through the configuration line by line. */
596		for (i = 0; i < len; i++) {
597			if (buf[i] == '\n' || buf[i] == '\0') {
598				ptr = buf + begin;
599				strlcpy(line, ptr, (i - begin) + 1);
600				begin = i + 1;
601				switch (type) {
602				case GV_TYPE_DRIVE:
603					name = find_pattern(line, "drive");
604					break;
605				case GV_TYPE_VOL:
606					name = find_pattern(line, "volume");
607					break;
608				case GV_TYPE_PLEX:
609				case GV_TYPE_SD:
610					name = find_pattern(line, "name");
611					break;
612				default:
613					printf("Invalid type given\n");
614					continue;
615				}
616				if (name == NULL)
617					continue;
618				if (!strcmp(sname, name)) {
619					conflict = 1;
620					/* XXX: Could quit the loop earlier. */
621				}
622			}
623		}
624		if (!conflict)
625			return (sname);
626	}
627	free(sname);
628	return (NULL);
629}
630
631/* Detach a plex or subdisk from its parent. */
632void
633gvinum_detach(int argc, char **argv)
634{
635	const char *errstr;
636	struct gctl_req *req;
637	int flags, i;
638
639	optreset = 1;
640	optind = 1;
641	while ((i = getopt(argc, argv, "f")) != -1) {
642		switch(i) {
643		case 'f':
644			flags |= GV_FLAG_F;
645			break;
646		default:
647			warn("invalid flag: %c", i);
648			return;
649		}
650	}
651	argc -= optind;
652	argv += optind;
653	if (argc != 1) {
654		warnx("usage: detach [-f] <subdisk> | <plex>");
655		return;
656	}
657
658	req = gctl_get_handle();
659	gctl_ro_param(req, "class", -1, "VINUM");
660	gctl_ro_param(req, "verb", -1, "detach");
661	gctl_ro_param(req, "object", -1, argv[0]);
662	gctl_ro_param(req, "flags", sizeof(int), &flags);
663
664	errstr = gctl_issue(req);
665	if (errstr != NULL)
666		warnx("detach failed: %s", errstr);
667	gctl_free(req);
668}
669
670void
671gvinum_help(void)
672{
673	printf("COMMANDS\n"
674	    "checkparity [-f] plex\n"
675	    "        Check the parity blocks of a RAID-5 plex.\n"
676	    "create [-f] description-file\n"
677	    "        Create as per description-file or open editor.\n"
678	    "attach plex volume [rename]\n"
679	    "attach subdisk plex [offset] [rename]\n"
680	    "        Attach a plex to a volume, or a subdisk to a plex\n"
681	    "concat [-fv] [-n name] drives\n"
682	    "        Create a concatenated volume from the specified drives.\n"
683	    "detach [-f] [plex | subdisk]\n"
684	    "        Detach a plex or a subdisk from the volume or plex to\n"
685	    "        which it is attached.\n"
686	    "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
687	    "        List information about specified objects.\n"
688	    "ld [-r] [-v] [-V] [volume]\n"
689	    "        List information about drives.\n"
690	    "ls [-r] [-v] [-V] [subdisk]\n"
691	    "        List information about subdisks.\n"
692	    "lp [-r] [-v] [-V] [plex]\n"
693	    "        List information about plexes.\n"
694	    "lv [-r] [-v] [-V] [volume]\n"
695	    "        List information about volumes.\n"
696	    "mirror [-fsv] [-n name] drives\n"
697	    "        Create a mirrored volume from the specified drives.\n"
698	    "move | mv -f drive object ...\n"
699	    "        Move the object(s) to the specified drive.\n"
700	    "quit    Exit the vinum program when running in interactive mode."
701	    "  Nor-\n"
702	    "        mally this would be done by entering the EOF character.\n"
703	    "raid5 [-fv] [-s stripesize] [-n name] drives\n"
704	    "        Create a RAID-5 volume from the specified drives.\n"
705	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
706	    "        Change the name of the specified object.\n"
707	    "rebuildparity plex [-f]\n"
708	    "        Rebuild the parity blocks of a RAID-5 plex.\n"
709	    "resetconfig\n"
710	    "        Reset the complete gvinum configuration\n"
711	    "rm [-r] [-f] volume | plex | subdisk | drive\n"
712	    "        Remove an object.\n"
713	    "saveconfig\n"
714	    "        Save vinum configuration to disk after configuration"
715	    " failures.\n"
716	    "setstate [-f] state [volume | plex | subdisk | drive]\n"
717	    "        Set state without influencing other objects, for"
718	    " diagnostic pur-\n"
719	    "        poses only.\n"
720	    "start [-S size] volume | plex | subdisk\n"
721	    "        Allow the system to access the objects.\n"
722	    "stripe [-fv] [-n name] drives\n"
723	    "        Create a striped volume from the specified drives.\n"
724	);
725
726	return;
727}
728
729void
730gvinum_setstate(int argc, char **argv)
731{
732	struct gctl_req *req;
733	int flags, i;
734	const char *errstr;
735
736	flags = 0;
737
738	optreset = 1;
739	optind = 1;
740
741	while ((i = getopt(argc, argv, "f")) != -1) {
742		switch (i) {
743		case 'f':
744			flags |= GV_FLAG_F;
745			break;
746		case '?':
747		default:
748			warn("invalid flag: %c", i);
749			return;
750		}
751	}
752
753	argc -= optind;
754	argv += optind;
755
756	if (argc != 2) {
757		warnx("usage: setstate [-f] <state> <obj>");
758		return;
759	}
760
761	/*
762	 * XXX: This hack is needed to avoid tripping over (now) invalid
763	 * 'classic' vinum states and will go away later.
764	 */
765	if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
766	    strcmp(argv[0], "stale")) {
767		warnx("invalid state '%s'", argv[0]);
768		return;
769	}
770
771	req = gctl_get_handle();
772	gctl_ro_param(req, "class", -1, "VINUM");
773	gctl_ro_param(req, "verb", -1, "setstate");
774	gctl_ro_param(req, "state", -1, argv[0]);
775	gctl_ro_param(req, "object", -1, argv[1]);
776	gctl_ro_param(req, "flags", sizeof(int), &flags);
777
778	errstr = gctl_issue(req);
779	if (errstr != NULL)
780		warnx("%s", errstr);
781	gctl_free(req);
782}
783
784void
785gvinum_list(int argc, char **argv)
786{
787	struct gctl_req *req;
788	int flags, i, j;
789	const char *errstr;
790	char buf[20], *cmd, config[GV_CFG_LEN + 1];
791
792	flags = 0;
793	cmd = "list";
794
795	if (argc) {
796		optreset = 1;
797		optind = 1;
798		cmd = argv[0];
799		while ((j = getopt(argc, argv, "rsvV")) != -1) {
800			switch (j) {
801			case 'r':
802				flags |= GV_FLAG_R;
803				break;
804			case 's':
805				flags |= GV_FLAG_S;
806				break;
807			case 'v':
808				flags |= GV_FLAG_V;
809				break;
810			case 'V':
811				flags |= GV_FLAG_V;
812				flags |= GV_FLAG_VV;
813				break;
814			case '?':
815			default:
816				return;
817			}
818		}
819		argc -= optind;
820		argv += optind;
821
822	}
823
824	req = gctl_get_handle();
825	gctl_ro_param(req, "class", -1, "VINUM");
826	gctl_ro_param(req, "verb", -1, "list");
827	gctl_ro_param(req, "cmd", -1, cmd);
828	gctl_ro_param(req, "argc", sizeof(int), &argc);
829	gctl_ro_param(req, "flags", sizeof(int), &flags);
830	gctl_rw_param(req, "config", sizeof(config), config);
831	if (argc) {
832		for (i = 0; i < argc; i++) {
833			snprintf(buf, sizeof(buf), "argv%d", i);
834			gctl_ro_param(req, buf, -1, argv[i]);
835		}
836	}
837	errstr = gctl_issue(req);
838	if (errstr != NULL) {
839		warnx("can't get configuration: %s", errstr);
840		gctl_free(req);
841		return;
842	}
843
844	printf("%s", config);
845	gctl_free(req);
846	return;
847}
848
849/* Create a mirrored volume. */
850void
851gvinum_mirror(int argc, char **argv)
852{
853
854	if (argc < 2) {
855		warnx("usage\tmirror [-fsv] [-n name] drives\n");
856		return;
857	}
858	create_volume(argc, argv, "mirror");
859}
860
861/* Note that move is currently of form '[-r] target object [...]' */
862void
863gvinum_move(int argc, char **argv)
864{
865	struct gctl_req *req;
866	const char *errstr;
867	char buf[20];
868	int flags, i, j;
869
870	flags = 0;
871	if (argc) {
872		optreset = 1;
873		optind = 1;
874		while ((j = getopt(argc, argv, "f")) != -1) {
875			switch (j) {
876			case 'f':
877				flags |= GV_FLAG_F;
878				break;
879			case '?':
880			default:
881				return;
882			}
883		}
884		argc -= optind;
885		argv += optind;
886	}
887
888	switch (argc) {
889		case 0:
890			warnx("no destination or object(s) to move specified");
891			return;
892		case 1:
893			warnx("no object(s) to move specified");
894			return;
895		default:
896			break;
897	}
898
899	req = gctl_get_handle();
900	gctl_ro_param(req, "class", -1, "VINUM");
901	gctl_ro_param(req, "verb", -1, "move");
902	gctl_ro_param(req, "argc", sizeof(int), &argc);
903	gctl_ro_param(req, "flags", sizeof(int), &flags);
904	gctl_ro_param(req, "destination", -1, argv[0]);
905	for (i = 1; i < argc; i++) {
906		snprintf(buf, sizeof(buf), "argv%d", i);
907		gctl_ro_param(req, buf, -1, argv[i]);
908	}
909	errstr = gctl_issue(req);
910	if (errstr != NULL)
911		warnx("can't move object(s):  %s", errstr);
912	gctl_free(req);
913	return;
914}
915
916void
917gvinum_printconfig(int argc, char **argv)
918{
919	printconfig(stdout, "");
920}
921
922void
923gvinum_parityop(int argc, char **argv, int rebuild)
924{
925	struct gctl_req *req;
926	int flags, i;
927	const char *errstr;
928	char *op, *msg;
929
930	if (rebuild) {
931		op = "rebuildparity";
932		msg = "Rebuilding";
933	} else {
934		op = "checkparity";
935		msg = "Checking";
936	}
937
938	optreset = 1;
939	optind = 1;
940	flags = 0;
941	while ((i = getopt(argc, argv, "fv")) != -1) {
942		switch (i) {
943		case 'f':
944			flags |= GV_FLAG_F;
945			break;
946		case 'v':
947			flags |= GV_FLAG_V;
948			break;
949		case '?':
950		default:
951			warnx("invalid flag '%c'", i);
952			return;
953		}
954	}
955	argc -= optind;
956	argv += optind;
957
958	if (argc != 1) {
959		warn("usage: %s [-f] [-v] <plex>", op);
960		return;
961	}
962
963	req = gctl_get_handle();
964	gctl_ro_param(req, "class", -1, "VINUM");
965	gctl_ro_param(req, "verb", -1, op);
966	gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
967	gctl_ro_param(req, "flags", sizeof(int), &flags);
968	gctl_ro_param(req, "plex", -1, argv[0]);
969
970	errstr = gctl_issue(req);
971	if (errstr)
972		warnx("%s\n", errstr);
973	gctl_free(req);
974}
975
976/* Create a RAID-5 volume. */
977void
978gvinum_raid5(int argc, char **argv)
979{
980
981	if (argc < 2) {
982		warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
983		return;
984	}
985	create_volume(argc, argv, "raid5");
986}
987
988
989void
990gvinum_rename(int argc, char **argv)
991{
992	struct gctl_req *req;
993	const char *errstr;
994	int flags, j;
995
996	flags = 0;
997
998	if (argc) {
999		optreset = 1;
1000		optind = 1;
1001		while ((j = getopt(argc, argv, "r")) != -1) {
1002			switch (j) {
1003			case 'r':
1004				flags |= GV_FLAG_R;
1005				break;
1006			case '?':
1007			default:
1008				return;
1009			}
1010		}
1011		argc -= optind;
1012		argv += optind;
1013	}
1014
1015	switch (argc) {
1016		case 0:
1017			warnx("no object to rename specified");
1018			return;
1019		case 1:
1020			warnx("no new name specified");
1021			return;
1022		case 2:
1023			break;
1024		default:
1025			warnx("more than one new name specified");
1026			return;
1027	}
1028
1029	req = gctl_get_handle();
1030	gctl_ro_param(req, "class", -1, "VINUM");
1031	gctl_ro_param(req, "verb", -1, "rename");
1032	gctl_ro_param(req, "flags", sizeof(int), &flags);
1033	gctl_ro_param(req, "object", -1, argv[0]);
1034	gctl_ro_param(req, "newname", -1, argv[1]);
1035	errstr = gctl_issue(req);
1036	if (errstr != NULL)
1037		warnx("can't rename object:  %s", errstr);
1038	gctl_free(req);
1039	return;
1040}
1041
1042void
1043gvinum_rm(int argc, char **argv)
1044{
1045	struct gctl_req *req;
1046	int flags, i, j;
1047	const char *errstr;
1048	char buf[20], *cmd;
1049
1050	cmd = argv[0];
1051	flags = 0;
1052	optreset = 1;
1053	optind = 1;
1054	while ((j = getopt(argc, argv, "rf")) != -1) {
1055		switch (j) {
1056		case 'f':
1057			flags |= GV_FLAG_F;
1058			break;
1059		case 'r':
1060			flags |= GV_FLAG_R;
1061			break;
1062		case '?':
1063		default:
1064			return;
1065		}
1066	}
1067	argc -= optind;
1068	argv += optind;
1069
1070	req = gctl_get_handle();
1071	gctl_ro_param(req, "class", -1, "VINUM");
1072	gctl_ro_param(req, "verb", -1, "remove");
1073	gctl_ro_param(req, "argc", sizeof(int), &argc);
1074	gctl_ro_param(req, "flags", sizeof(int), &flags);
1075	if (argc) {
1076		for (i = 0; i < argc; i++) {
1077			snprintf(buf, sizeof(buf), "argv%d", i);
1078			gctl_ro_param(req, buf, -1, argv[i]);
1079		}
1080	}
1081	errstr = gctl_issue(req);
1082	if (errstr != NULL) {
1083		warnx("can't remove: %s", errstr);
1084		gctl_free(req);
1085		return;
1086	}
1087	gctl_free(req);
1088}
1089
1090void
1091gvinum_resetconfig(void)
1092{
1093	struct gctl_req *req;
1094	const char *errstr;
1095	char reply[32];
1096
1097	if (!isatty(STDIN_FILENO)) {
1098		warn("Please enter this command from a tty device\n");
1099		return;
1100	}
1101	printf(" WARNING!  This command will completely wipe out your gvinum"
1102	    "configuration.\n"
1103	    " All data will be lost.  If you really want to do this,"
1104	    " enter the text\n\n"
1105	    " NO FUTURE\n"
1106	    " Enter text -> ");
1107	fgets(reply, sizeof(reply), stdin);
1108	if (strcmp(reply, "NO FUTURE\n")) {
1109		printf("\n No change\n");
1110		return;
1111	}
1112	req = gctl_get_handle();
1113	gctl_ro_param(req, "class", -1, "VINUM");
1114	gctl_ro_param(req, "verb", -1, "resetconfig");
1115	errstr = gctl_issue(req);
1116	if (errstr != NULL) {
1117		warnx("can't reset config: %s", errstr);
1118		gctl_free(req);
1119		return;
1120	}
1121	gctl_free(req);
1122	printf("gvinum configuration obliterated\n");
1123}
1124
1125void
1126gvinum_saveconfig(void)
1127{
1128	struct gctl_req *req;
1129	const char *errstr;
1130
1131	req = gctl_get_handle();
1132	gctl_ro_param(req, "class", -1, "VINUM");
1133	gctl_ro_param(req, "verb", -1, "saveconfig");
1134	errstr = gctl_issue(req);
1135	if (errstr != NULL)
1136		warnx("can't save configuration: %s", errstr);
1137	gctl_free(req);
1138}
1139
1140void
1141gvinum_start(int argc, char **argv)
1142{
1143	struct gctl_req *req;
1144	int i, initsize, j;
1145	const char *errstr;
1146	char buf[20];
1147
1148	/* 'start' with no arguments is a no-op. */
1149	if (argc == 1)
1150		return;
1151
1152	initsize = 0;
1153
1154	optreset = 1;
1155	optind = 1;
1156	while ((j = getopt(argc, argv, "S")) != -1) {
1157		switch (j) {
1158		case 'S':
1159			initsize = atoi(optarg);
1160			break;
1161		case '?':
1162		default:
1163			return;
1164		}
1165	}
1166	argc -= optind;
1167	argv += optind;
1168
1169	if (!initsize)
1170		initsize = 512;
1171
1172	req = gctl_get_handle();
1173	gctl_ro_param(req, "class", -1, "VINUM");
1174	gctl_ro_param(req, "verb", -1, "start");
1175	gctl_ro_param(req, "argc", sizeof(int), &argc);
1176	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1177	if (argc) {
1178		for (i = 0; i < argc; i++) {
1179			snprintf(buf, sizeof(buf), "argv%d", i);
1180			gctl_ro_param(req, buf, -1, argv[i]);
1181		}
1182	}
1183	errstr = gctl_issue(req);
1184	if (errstr != NULL) {
1185		warnx("can't start: %s", errstr);
1186		gctl_free(req);
1187		return;
1188	}
1189
1190	gctl_free(req);
1191}
1192
1193void
1194gvinum_stop(int argc, char **argv)
1195{
1196	int err, fileid;
1197
1198	fileid = kldfind(GVINUMMOD);
1199	if (fileid == -1) {
1200		warn("cannot find " GVINUMMOD);
1201		return;
1202	}
1203
1204	/*
1205	 * This little hack prevents that we end up in an infinite loop in
1206	 * g_unload_class().  gv_unload() will return EAGAIN so that the GEOM
1207	 * event thread will be free for the g_wither_geom() call from
1208	 * gv_unload().  It's silly, but it works.
1209	 */
1210	printf("unloading " GVINUMMOD " kernel module... ");
1211	fflush(stdout);
1212	if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1213		sleep(1);
1214		err = kldunload(fileid);
1215	}
1216	if (err != 0) {
1217		printf(" failed!\n");
1218		warn("cannot unload " GVINUMMOD);
1219		return;
1220	}
1221
1222	printf("done\n");
1223	exit(0);
1224}
1225
1226/* Create a striped volume. */
1227void
1228gvinum_stripe(int argc, char **argv)
1229{
1230
1231	if (argc < 2) {
1232		warnx("usage:\tstripe [-fv] [-n name] drives\n");
1233		return;
1234	}
1235	create_volume(argc, argv, "stripe");
1236}
1237
1238void
1239parseline(int argc, char **argv)
1240{
1241	if (argc <= 0)
1242		return;
1243
1244	if (!strcmp(argv[0], "create"))
1245		gvinum_create(argc, argv);
1246	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
1247		exit(0);
1248	else if (!strcmp(argv[0], "attach"))
1249		gvinum_attach(argc, argv);
1250	else if (!strcmp(argv[0], "detach"))
1251		gvinum_detach(argc, argv);
1252	else if (!strcmp(argv[0], "concat"))
1253		gvinum_concat(argc, argv);
1254	else if (!strcmp(argv[0], "help"))
1255		gvinum_help();
1256	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
1257		gvinum_list(argc, argv);
1258	else if (!strcmp(argv[0], "ld"))
1259		gvinum_list(argc, argv);
1260	else if (!strcmp(argv[0], "lp"))
1261		gvinum_list(argc, argv);
1262	else if (!strcmp(argv[0], "ls"))
1263		gvinum_list(argc, argv);
1264	else if (!strcmp(argv[0], "lv"))
1265		gvinum_list(argc, argv);
1266	else if (!strcmp(argv[0], "mirror"))
1267		gvinum_mirror(argc, argv);
1268	else if (!strcmp(argv[0], "move"))
1269		gvinum_move(argc, argv);
1270	else if (!strcmp(argv[0], "mv"))
1271		gvinum_move(argc, argv);
1272	else if (!strcmp(argv[0], "printconfig"))
1273		gvinum_printconfig(argc, argv);
1274	else if (!strcmp(argv[0], "raid5"))
1275		gvinum_raid5(argc, argv);
1276	else if (!strcmp(argv[0], "rename"))
1277		gvinum_rename(argc, argv);
1278	else if (!strcmp(argv[0], "resetconfig"))
1279		gvinum_resetconfig();
1280	else if (!strcmp(argv[0], "rm"))
1281		gvinum_rm(argc, argv);
1282	else if (!strcmp(argv[0], "saveconfig"))
1283		gvinum_saveconfig();
1284	else if (!strcmp(argv[0], "setstate"))
1285		gvinum_setstate(argc, argv);
1286	else if (!strcmp(argv[0], "start"))
1287		gvinum_start(argc, argv);
1288	else if (!strcmp(argv[0], "stop"))
1289		gvinum_stop(argc, argv);
1290	else if (!strcmp(argv[0], "stripe"))
1291		gvinum_stripe(argc, argv);
1292	else if (!strcmp(argv[0], "checkparity"))
1293		gvinum_parityop(argc, argv, 0);
1294	else if (!strcmp(argv[0], "rebuildparity"))
1295		gvinum_parityop(argc, argv, 1);
1296	else
1297		printf("unknown command '%s'\n", argv[0]);
1298
1299	return;
1300}
1301
1302/*
1303 * The guts of printconfig.  This is called from gvinum_printconfig and from
1304 * gvinum_create when called without an argument, in order to give the user
1305 * something to edit.
1306 */
1307void
1308printconfig(FILE *of, char *comment)
1309{
1310	struct gctl_req *req;
1311	struct utsname uname_s;
1312	const char *errstr;
1313	time_t now;
1314	char buf[GV_CFG_LEN + 1];
1315
1316	uname(&uname_s);
1317	time(&now);
1318
1319	req = gctl_get_handle();
1320	gctl_ro_param(req, "class", -1, "VINUM");
1321	gctl_ro_param(req, "verb", -1, "getconfig");
1322	gctl_ro_param(req, "comment", -1, comment);
1323	gctl_rw_param(req, "config", sizeof(buf), buf);
1324	errstr = gctl_issue(req);
1325	if (errstr != NULL) {
1326		warnx("can't get configuration: %s", errstr);
1327		return;
1328	}
1329	gctl_free(req);
1330
1331	fprintf(of, "# Vinum configuration of %s, saved at %s",
1332	    uname_s.nodename,
1333	    ctime(&now));
1334
1335	if (*comment != '\0')
1336	    fprintf(of, "# Current configuration:\n");
1337
1338	fprintf(of, buf);
1339}
1340