gvinum.c revision 265454
1/*
2 *  Copyright (c) 2004 Lukas Ertl
3 *  Copyright (c) 2005 Chris Jones
4 *  Copyright (c) 2007 Ulf Lilleengen
5 *  All rights reserved.
6 *
7 * Portions of this software were developed for the FreeBSD Project
8 * by Chris Jones thanks to the support of Google's Summer of Code
9 * program and mentoring by Lukas Ertl.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/sbin/gvinum/gvinum.c 265454 2014-05-06 16:29:02Z marius $
33 */
34
35#include <sys/param.h>
36#include <sys/linker.h>
37#include <sys/lock.h>
38#include <sys/module.h>
39#include <sys/mutex.h>
40#include <sys/queue.h>
41#include <sys/utsname.h>
42
43#include <geom/vinum/geom_vinum_var.h>
44#include <geom/vinum/geom_vinum_share.h>
45
46#include <ctype.h>
47#include <err.h>
48#include <errno.h>
49#include <libgeom.h>
50#include <stdint.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <paths.h>
55#include <readline/readline.h>
56#include <readline/history.h>
57#include <unistd.h>
58
59#include "gvinum.h"
60
61void	gvinum_attach(int, char **);
62void	gvinum_concat(int, char **);
63void	gvinum_create(int, char **);
64void	gvinum_detach(int, char **);
65void	gvinum_grow(int, char **);
66void	gvinum_help(void);
67void	gvinum_list(int, char **);
68void	gvinum_move(int, char **);
69void	gvinum_mirror(int, char **);
70void	gvinum_parityop(int, char **, int);
71void	gvinum_printconfig(int, char **);
72void	gvinum_raid5(int, char **);
73void	gvinum_rename(int, char **);
74void	gvinum_resetconfig(int, char **);
75void	gvinum_rm(int, char **);
76void	gvinum_saveconfig(void);
77void	gvinum_setstate(int, char **);
78void	gvinum_start(int, char **);
79void	gvinum_stop(int, char **);
80void	gvinum_stripe(int, char **);
81void	parseline(int, char **);
82void	printconfig(FILE *, char *);
83
84char	*create_drive(char *);
85void	 create_volume(int, char **, char *);
86char	*find_name(const char *, int, int);
87char	*find_pattern(char *, char *);
88void	 copy_device(struct gv_drive *, const char *);
89#define find_drive() find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME)
90
91int
92main(int argc, char **argv)
93{
94	int line, tokens;
95	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
96
97	/* Load the module if necessary. */
98	if (modfind(GVINUMMOD) < 0) {
99		if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0)
100			err(1, GVINUMKLD ": Kernel module not available");
101	}
102
103	/* Arguments given on the command line. */
104	if (argc > 1) {
105		argc--;
106		argv++;
107		parseline(argc, argv);
108
109	/* Interactive mode. */
110	} else {
111		for (;;) {
112			inputline = readline("gvinum -> ");
113			if (inputline == NULL) {
114				if (ferror(stdin)) {
115					err(1, "can't read input");
116				} else {
117					printf("\n");
118					exit(0);
119				}
120			} else if (*inputline) {
121				add_history(inputline);
122				strcpy(buffer, inputline);
123				free(inputline);
124				line++;		    /* count the lines */
125				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
126				if (tokens)
127					parseline(tokens, token);
128			}
129		}
130	}
131	exit(0);
132}
133
134/* Attach a plex to a volume or a subdisk to a plex. */
135void
136gvinum_attach(int argc, char **argv)
137{
138	struct gctl_req *req;
139	const char *errstr;
140	int rename;
141	off_t offset;
142
143	rename = 0;
144	offset = -1;
145	if (argc < 3) {
146		warnx("usage:\tattach <subdisk> <plex> [rename] "
147		    "[<plexoffset>]\n"
148		    "\tattach <plex> <volume> [rename]");
149		return;
150	}
151	if (argc > 3) {
152		if (!strcmp(argv[3], "rename")) {
153			rename = 1;
154			if (argc == 5)
155				offset = strtol(argv[4], NULL, 0);
156		} else
157			offset = strtol(argv[3], NULL, 0);
158	}
159	req = gctl_get_handle();
160	gctl_ro_param(req, "class", -1, "VINUM");
161	gctl_ro_param(req, "verb", -1, "attach");
162	gctl_ro_param(req, "child", -1, argv[1]);
163	gctl_ro_param(req, "parent", -1, argv[2]);
164	gctl_ro_param(req, "offset", sizeof(off_t), &offset);
165	gctl_ro_param(req, "rename", sizeof(int), &rename);
166	errstr = gctl_issue(req);
167	if (errstr != NULL)
168		warnx("attach failed: %s", errstr);
169	gctl_free(req);
170}
171
172void
173gvinum_create(int argc, char **argv)
174{
175	struct gctl_req *req;
176	struct gv_drive *d;
177	struct gv_plex *p;
178	struct gv_sd *s;
179	struct gv_volume *v;
180	FILE *tmp;
181	int drives, errors, fd, flags, i, line, plexes, plex_in_volume;
182	int sd_in_plex, status, subdisks, tokens, undeffd, volumes;
183	const char *errstr;
184	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname;
185	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
186	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
187
188	tmp = NULL;
189	flags = 0;
190	for (i = 1; i < argc; i++) {
191		/* Force flag used to ignore already created drives. */
192		if (!strcmp(argv[i], "-f")) {
193			flags |= GV_FLAG_F;
194		/* Else it must be a file. */
195		} else {
196			if ((tmp = fopen(argv[i], "r")) == NULL) {
197				warn("can't open '%s' for reading", argv[i]);
198				return;
199			}
200		}
201	}
202
203	/* We didn't get a file. */
204	if (tmp == NULL) {
205		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
206
207		if ((fd = mkstemp(tmpfile)) == -1) {
208			warn("temporary file not accessible");
209			return;
210		}
211		if ((tmp = fdopen(fd, "w")) == NULL) {
212			warn("can't open '%s' for writing", tmpfile);
213			return;
214		}
215		printconfig(tmp, "# ");
216		fclose(tmp);
217
218		ed = getenv("EDITOR");
219		if (ed == NULL)
220			ed = _PATH_VI;
221
222		snprintf(commandline, sizeof(commandline), "%s %s", ed,
223		    tmpfile);
224		status = system(commandline);
225		if (status != 0) {
226			warn("couldn't exec %s; status: %d", ed, status);
227			return;
228		}
229
230		if ((tmp = fopen(tmpfile, "r")) == NULL) {
231			warn("can't open '%s' for reading", tmpfile);
232			return;
233		}
234	}
235
236	req = gctl_get_handle();
237	gctl_ro_param(req, "class", -1, "VINUM");
238	gctl_ro_param(req, "verb", -1, "create");
239	gctl_ro_param(req, "flags", sizeof(int), &flags);
240
241	drives = volumes = plexes = subdisks = 0;
242	plex_in_volume = sd_in_plex = undeffd = 0;
243	plex[0] = '\0';
244	errors = 0;
245	line = 1;
246	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
247
248		/* Skip empty lines and comments. */
249		if (*buf == '\0' || *buf == '#') {
250			line++;
251			continue;
252		}
253
254		/* Kill off the newline. */
255		buf[strlen(buf) - 1] = '\0';
256
257		/*
258		 * Copy the original input line in case we need it for error
259		 * output.
260		 */
261		strlcpy(original, buf, sizeof(original));
262
263		tokens = gv_tokenize(buf, token, GV_MAXARGS);
264		if (tokens <= 0) {
265			line++;
266			continue;
267		}
268
269		/* Volume definition. */
270		if (!strcmp(token[0], "volume")) {
271			v = gv_new_volume(tokens, token);
272			if (v == NULL) {
273				warnx("line %d: invalid volume definition",
274				    line);
275				warnx("line %d: '%s'", line, original);
276				errors++;
277				line++;
278				continue;
279			}
280
281			/* Reset plex count for this volume. */
282			plex_in_volume = 0;
283
284			/*
285			 * Set default volume name for following plex
286			 * definitions.
287			 */
288			strlcpy(volume, v->name, sizeof(volume));
289
290			snprintf(buf1, sizeof(buf1), "volume%d", volumes);
291			gctl_ro_param(req, buf1, sizeof(*v), v);
292			volumes++;
293
294		/* Plex definition. */
295		} else if (!strcmp(token[0], "plex")) {
296			p = gv_new_plex(tokens, token);
297			if (p == NULL) {
298				warnx("line %d: invalid plex definition", line);
299				warnx("line %d: '%s'", line, original);
300				errors++;
301				line++;
302				continue;
303			}
304
305			/* Reset subdisk count for this plex. */
306			sd_in_plex = 0;
307
308			/* Default name. */
309			if (strlen(p->name) == 0) {
310				snprintf(p->name, sizeof(p->name), "%s.p%d",
311				    volume, plex_in_volume++);
312			}
313
314			/* Default volume. */
315			if (strlen(p->volume) == 0) {
316				snprintf(p->volume, sizeof(p->volume), "%s",
317				    volume);
318			}
319
320			/*
321			 * Set default plex name for following subdisk
322			 * definitions.
323			 */
324			strlcpy(plex, p->name, sizeof(plex));
325
326			snprintf(buf1, sizeof(buf1), "plex%d", plexes);
327			gctl_ro_param(req, buf1, sizeof(*p), p);
328			plexes++;
329
330		/* Subdisk definition. */
331		} else if (!strcmp(token[0], "sd")) {
332			s = gv_new_sd(tokens, token);
333			if (s == NULL) {
334				warnx("line %d: invalid subdisk "
335				    "definition:", line);
336				warnx("line %d: '%s'", line, original);
337				errors++;
338				line++;
339				continue;
340			}
341
342			/* Default name. */
343			if (strlen(s->name) == 0) {
344				if (strlen(plex) == 0) {
345					sdname = find_name("gvinumsubdisk.p",
346					    GV_TYPE_SD, GV_MAXSDNAME);
347					snprintf(s->name, sizeof(s->name),
348					    "%s.s%d", sdname, undeffd++);
349					free(sdname);
350				} else {
351					snprintf(s->name, sizeof(s->name),
352					    "%s.s%d",plex, sd_in_plex++);
353				}
354			}
355
356			/* Default plex. */
357			if (strlen(s->plex) == 0)
358				snprintf(s->plex, sizeof(s->plex), "%s", plex);
359
360			snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
361			gctl_ro_param(req, buf1, sizeof(*s), s);
362			subdisks++;
363
364		/* Subdisk definition. */
365		} else if (!strcmp(token[0], "drive")) {
366			d = gv_new_drive(tokens, token);
367			if (d == NULL) {
368				warnx("line %d: invalid drive definition:",
369				    line);
370				warnx("line %d: '%s'", line, original);
371				errors++;
372				line++;
373				continue;
374			}
375
376			snprintf(buf1, sizeof(buf1), "drive%d", drives);
377			gctl_ro_param(req, buf1, sizeof(*d), d);
378			drives++;
379
380		/* Everything else is bogus. */
381		} else {
382			warnx("line %d: invalid definition:", line);
383			warnx("line %d: '%s'", line, original);
384			errors++;
385		}
386		line++;
387	}
388
389	fclose(tmp);
390	unlink(tmpfile);
391
392	if (!errors && (volumes || plexes || subdisks || drives)) {
393		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
394		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
395		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
396		gctl_ro_param(req, "drives", sizeof(int), &drives);
397		errstr = gctl_issue(req);
398		if (errstr != NULL)
399			warnx("create failed: %s", errstr);
400	}
401	gctl_free(req);
402}
403
404/* Create a concatenated volume. */
405void
406gvinum_concat(int argc, char **argv)
407{
408
409	if (argc < 2) {
410		warnx("usage:\tconcat [-fv] [-n name] drives\n");
411		return;
412	}
413	create_volume(argc, argv, "concat");
414}
415
416
417/* Create a drive quick and dirty. */
418char *
419create_drive(char *device)
420{
421	struct gv_drive *d;
422	struct gctl_req *req;
423	const char *errstr;
424	char *drivename, *dname;
425	int drives, i, flags, volumes, subdisks, plexes;
426	int found = 0;
427
428	flags = plexes = subdisks = volumes = 0;
429	drives = 1;
430	dname = NULL;
431
432	drivename = find_drive();
433	if (drivename == NULL)
434		return (NULL);
435
436	req = gctl_get_handle();
437	gctl_ro_param(req, "class", -1, "VINUM");
438	gctl_ro_param(req, "verb", -1, "create");
439	d = gv_alloc_drive();
440	if (d == NULL)
441		err(1, "unable to allocate for gv_drive object");
442
443	strlcpy(d->name, drivename, sizeof(d->name));
444	copy_device(d, device);
445	gctl_ro_param(req, "drive0", sizeof(*d), d);
446	gctl_ro_param(req, "flags", sizeof(int), &flags);
447	gctl_ro_param(req, "drives", sizeof(int), &drives);
448	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
449	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
450	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
451	errstr = gctl_issue(req);
452	if (errstr != NULL) {
453		warnx("error creating drive: %s", errstr);
454		drivename = NULL;
455	} else {
456		/* XXX: This is needed because we have to make sure the drives
457		 * are created before we return. */
458		/* Loop until it's in the config. */
459		for (i = 0; i < 100000; i++) {
460			dname = find_name("gvinumdrive", GV_TYPE_DRIVE,
461			    GV_MAXDRIVENAME);
462			/* If we got a different name, quit. */
463			if (dname == NULL)
464				continue;
465			if (strcmp(dname, drivename))
466				found = 1;
467			free(dname);
468			dname = NULL;
469			if (found)
470				break;
471			usleep(100000); /* Sleep for 0.1s */
472		}
473		if (found == 0) {
474			warnx("error creating drive");
475			drivename = NULL;
476		}
477	}
478	gctl_free(req);
479	return (drivename);
480}
481
482/*
483 * General routine for creating a volume. Mainly for use by concat, mirror,
484 * raid5 and stripe commands.
485 */
486void
487create_volume(int argc, char **argv, char *verb)
488{
489	struct gctl_req *req;
490	const char *errstr;
491	char buf[BUFSIZ], *drivename, *volname;
492	int drives, flags, i;
493	off_t stripesize;
494
495	flags = 0;
496	drives = 0;
497	volname = NULL;
498	stripesize = 262144;
499
500	/* XXX: Should we check for argument length? */
501
502	req = gctl_get_handle();
503	gctl_ro_param(req, "class", -1, "VINUM");
504
505	for (i = 1; i < argc; i++) {
506		if (!strcmp(argv[i], "-f")) {
507			flags |= GV_FLAG_F;
508		} else if (!strcmp(argv[i], "-n")) {
509			volname = argv[++i];
510		} else if (!strcmp(argv[i], "-v")) {
511			flags |= GV_FLAG_V;
512		} else if (!strcmp(argv[i], "-s")) {
513			flags |= GV_FLAG_S;
514			if (!strcmp(verb, "raid5"))
515				stripesize = gv_sizespec(argv[++i]);
516		} else {
517			/* Assume it's a drive. */
518			snprintf(buf, sizeof(buf), "drive%d", drives++);
519
520			/* First we create the drive. */
521			drivename = create_drive(argv[i]);
522			if (drivename == NULL)
523				goto bad;
524			/* Then we add it to the request. */
525			gctl_ro_param(req, buf, -1, drivename);
526		}
527	}
528
529	gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize);
530
531	/* Find a free volume name. */
532	if (volname == NULL)
533		volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME);
534
535	/* Then we send a request to actually create the volumes. */
536	gctl_ro_param(req, "verb", -1, verb);
537	gctl_ro_param(req, "flags", sizeof(int), &flags);
538	gctl_ro_param(req, "drives", sizeof(int), &drives);
539	gctl_ro_param(req, "name", -1, volname);
540	errstr = gctl_issue(req);
541	if (errstr != NULL)
542		warnx("creating %s volume failed: %s", verb, errstr);
543bad:
544	gctl_free(req);
545}
546
547/* Parse a line of the config, return the word after <pattern>. */
548char *
549find_pattern(char *line, char *pattern)
550{
551	char *ptr;
552
553	ptr = strsep(&line, " ");
554	while (ptr != NULL) {
555		if (!strcmp(ptr, pattern)) {
556			/* Return the next. */
557			ptr = strsep(&line, " ");
558			return (ptr);
559		}
560		ptr = strsep(&line, " ");
561	}
562	return (NULL);
563}
564
565/* Find a free name for an object given a prefix. */
566char *
567find_name(const char *prefix, int type, int namelen)
568{
569	struct gctl_req *req;
570	char comment[1], buf[GV_CFG_LEN - 1], *name, *sname, *ptr;
571	const char *errstr;
572	int i, n, begin, len, conflict;
573	char line[1024];
574
575	comment[0] = '\0';
576
577	/* Find a name. Fetch out configuration first. */
578	req = gctl_get_handle();
579	gctl_ro_param(req, "class", -1, "VINUM");
580	gctl_ro_param(req, "verb", -1, "getconfig");
581	gctl_ro_param(req, "comment", -1, comment);
582	gctl_rw_param(req, "config", sizeof(buf), buf);
583	errstr = gctl_issue(req);
584	if (errstr != NULL) {
585		warnx("can't get configuration: %s", errstr);
586		return (NULL);
587	}
588	gctl_free(req);
589
590	begin = 0;
591	len = strlen(buf);
592	i = 0;
593	sname = malloc(namelen + 1);
594
595	/* XXX: Max object setting? */
596	for (n = 0; n < 10000; n++) {
597		snprintf(sname, namelen, "%s%d", prefix, n);
598		conflict = 0;
599		begin = 0;
600		/* Loop through the configuration line by line. */
601		for (i = 0; i < len; i++) {
602			if (buf[i] == '\n' || buf[i] == '\0') {
603				ptr = buf + begin;
604				strlcpy(line, ptr, (i - begin) + 1);
605				begin = i + 1;
606				switch (type) {
607				case GV_TYPE_DRIVE:
608					name = find_pattern(line, "drive");
609					break;
610				case GV_TYPE_VOL:
611					name = find_pattern(line, "volume");
612					break;
613				case GV_TYPE_PLEX:
614				case GV_TYPE_SD:
615					name = find_pattern(line, "name");
616					break;
617				default:
618					printf("Invalid type given\n");
619					continue;
620				}
621				if (name == NULL)
622					continue;
623				if (!strcmp(sname, name)) {
624					conflict = 1;
625					/* XXX: Could quit the loop earlier. */
626				}
627			}
628		}
629		if (!conflict)
630			return (sname);
631	}
632	free(sname);
633	return (NULL);
634}
635
636void
637copy_device(struct gv_drive *d, const char *device)
638{
639	if (strncmp(device, "/dev/", 5) == 0)
640		strlcpy(d->device, (device + 5), sizeof(d->device));
641	else
642		strlcpy(d->device, device, sizeof(d->device));
643}
644
645/* Detach a plex or subdisk from its parent. */
646void
647gvinum_detach(int argc, char **argv)
648{
649	const char *errstr;
650	struct gctl_req *req;
651	int flags, i;
652
653	flags = 0;
654	optreset = 1;
655	optind = 1;
656	while ((i = getopt(argc, argv, "f")) != -1) {
657		switch(i) {
658		case 'f':
659			flags |= GV_FLAG_F;
660			break;
661		default:
662			warn("invalid flag: %c", i);
663			return;
664		}
665	}
666	argc -= optind;
667	argv += optind;
668	if (argc != 1) {
669		warnx("usage: detach [-f] <subdisk> | <plex>");
670		return;
671	}
672
673	req = gctl_get_handle();
674	gctl_ro_param(req, "class", -1, "VINUM");
675	gctl_ro_param(req, "verb", -1, "detach");
676	gctl_ro_param(req, "object", -1, argv[0]);
677	gctl_ro_param(req, "flags", sizeof(int), &flags);
678
679	errstr = gctl_issue(req);
680	if (errstr != NULL)
681		warnx("detach failed: %s", errstr);
682	gctl_free(req);
683}
684
685void
686gvinum_help(void)
687{
688	printf("COMMANDS\n"
689	    "checkparity [-f] plex\n"
690	    "        Check the parity blocks of a RAID-5 plex.\n"
691	    "create [-f] description-file\n"
692	    "        Create as per description-file or open editor.\n"
693	    "attach plex volume [rename]\n"
694	    "attach subdisk plex [offset] [rename]\n"
695	    "        Attach a plex to a volume, or a subdisk to a plex\n"
696	    "concat [-fv] [-n name] drives\n"
697	    "        Create a concatenated volume from the specified drives.\n"
698	    "detach [-f] [plex | subdisk]\n"
699	    "        Detach a plex or a subdisk from the volume or plex to\n"
700	    "        which it is attached.\n"
701	    "grow plex drive\n"
702	    "        Grow plex by creating a properly sized subdisk on drive\n"
703	    "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
704	    "        List information about specified objects.\n"
705	    "ld [-r] [-v] [-V] [volume]\n"
706	    "        List information about drives.\n"
707	    "ls [-r] [-v] [-V] [subdisk]\n"
708	    "        List information about subdisks.\n"
709	    "lp [-r] [-v] [-V] [plex]\n"
710	    "        List information about plexes.\n"
711	    "lv [-r] [-v] [-V] [volume]\n"
712	    "        List information about volumes.\n"
713	    "mirror [-fsv] [-n name] drives\n"
714	    "        Create a mirrored volume from the specified drives.\n"
715	    "move | mv -f drive object ...\n"
716	    "        Move the object(s) to the specified drive.\n"
717	    "quit    Exit the vinum program when running in interactive mode."
718	    "  Nor-\n"
719	    "        mally this would be done by entering the EOF character.\n"
720	    "raid5 [-fv] [-s stripesize] [-n name] drives\n"
721	    "        Create a RAID-5 volume from the specified drives.\n"
722	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
723	    "        Change the name of the specified object.\n"
724	    "rebuildparity plex [-f]\n"
725	    "        Rebuild the parity blocks of a RAID-5 plex.\n"
726	    "resetconfig [-f]\n"
727	    "        Reset the complete gvinum configuration\n"
728	    "rm [-r] [-f] volume | plex | subdisk | drive\n"
729	    "        Remove an object.\n"
730	    "saveconfig\n"
731	    "        Save vinum configuration to disk after configuration"
732	    " failures.\n"
733	    "setstate [-f] state [volume | plex | subdisk | drive]\n"
734	    "        Set state without influencing other objects, for"
735	    " diagnostic pur-\n"
736	    "        poses only.\n"
737	    "start [-S size] volume | plex | subdisk\n"
738	    "        Allow the system to access the objects.\n"
739	    "stripe [-fv] [-n name] drives\n"
740	    "        Create a striped volume from the specified drives.\n"
741	);
742
743	return;
744}
745
746void
747gvinum_setstate(int argc, char **argv)
748{
749	struct gctl_req *req;
750	int flags, i;
751	const char *errstr;
752
753	flags = 0;
754
755	optreset = 1;
756	optind = 1;
757
758	while ((i = getopt(argc, argv, "f")) != -1) {
759		switch (i) {
760		case 'f':
761			flags |= GV_FLAG_F;
762			break;
763		case '?':
764		default:
765			warn("invalid flag: %c", i);
766			return;
767		}
768	}
769
770	argc -= optind;
771	argv += optind;
772
773	if (argc != 2) {
774		warnx("usage: setstate [-f] <state> <obj>");
775		return;
776	}
777
778	/*
779	 * XXX: This hack is needed to avoid tripping over (now) invalid
780	 * 'classic' vinum states and will go away later.
781	 */
782	if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
783	    strcmp(argv[0], "stale")) {
784		warnx("invalid state '%s'", argv[0]);
785		return;
786	}
787
788	req = gctl_get_handle();
789	gctl_ro_param(req, "class", -1, "VINUM");
790	gctl_ro_param(req, "verb", -1, "setstate");
791	gctl_ro_param(req, "state", -1, argv[0]);
792	gctl_ro_param(req, "object", -1, argv[1]);
793	gctl_ro_param(req, "flags", sizeof(int), &flags);
794
795	errstr = gctl_issue(req);
796	if (errstr != NULL)
797		warnx("%s", errstr);
798	gctl_free(req);
799}
800
801void
802gvinum_list(int argc, char **argv)
803{
804	struct gctl_req *req;
805	int flags, i, j;
806	const char *errstr;
807	char buf[20], *cmd, config[GV_CFG_LEN + 1];
808
809	flags = 0;
810	cmd = "list";
811
812	if (argc) {
813		optreset = 1;
814		optind = 1;
815		cmd = argv[0];
816		while ((j = getopt(argc, argv, "rsvV")) != -1) {
817			switch (j) {
818			case 'r':
819				flags |= GV_FLAG_R;
820				break;
821			case 's':
822				flags |= GV_FLAG_S;
823				break;
824			case 'v':
825				flags |= GV_FLAG_V;
826				break;
827			case 'V':
828				flags |= GV_FLAG_V;
829				flags |= GV_FLAG_VV;
830				break;
831			case '?':
832			default:
833				return;
834			}
835		}
836		argc -= optind;
837		argv += optind;
838
839	}
840
841	req = gctl_get_handle();
842	gctl_ro_param(req, "class", -1, "VINUM");
843	gctl_ro_param(req, "verb", -1, "list");
844	gctl_ro_param(req, "cmd", -1, cmd);
845	gctl_ro_param(req, "argc", sizeof(int), &argc);
846	gctl_ro_param(req, "flags", sizeof(int), &flags);
847	gctl_rw_param(req, "config", sizeof(config), config);
848	if (argc) {
849		for (i = 0; i < argc; i++) {
850			snprintf(buf, sizeof(buf), "argv%d", i);
851			gctl_ro_param(req, buf, -1, argv[i]);
852		}
853	}
854	errstr = gctl_issue(req);
855	if (errstr != NULL) {
856		warnx("can't get configuration: %s", errstr);
857		gctl_free(req);
858		return;
859	}
860
861	printf("%s", config);
862	gctl_free(req);
863	return;
864}
865
866/* Create a mirrored volume. */
867void
868gvinum_mirror(int argc, char **argv)
869{
870
871	if (argc < 2) {
872		warnx("usage\tmirror [-fsv] [-n name] drives\n");
873		return;
874	}
875	create_volume(argc, argv, "mirror");
876}
877
878/* Note that move is currently of form '[-r] target object [...]' */
879void
880gvinum_move(int argc, char **argv)
881{
882	struct gctl_req *req;
883	const char *errstr;
884	char buf[20];
885	int flags, i, j;
886
887	flags = 0;
888	if (argc) {
889		optreset = 1;
890		optind = 1;
891		while ((j = getopt(argc, argv, "f")) != -1) {
892			switch (j) {
893			case 'f':
894				flags |= GV_FLAG_F;
895				break;
896			case '?':
897			default:
898				return;
899			}
900		}
901		argc -= optind;
902		argv += optind;
903	}
904
905	switch (argc) {
906		case 0:
907			warnx("no destination or object(s) to move specified");
908			return;
909		case 1:
910			warnx("no object(s) to move specified");
911			return;
912		default:
913			break;
914	}
915
916	req = gctl_get_handle();
917	gctl_ro_param(req, "class", -1, "VINUM");
918	gctl_ro_param(req, "verb", -1, "move");
919	gctl_ro_param(req, "argc", sizeof(int), &argc);
920	gctl_ro_param(req, "flags", sizeof(int), &flags);
921	gctl_ro_param(req, "destination", -1, argv[0]);
922	for (i = 1; i < argc; i++) {
923		snprintf(buf, sizeof(buf), "argv%d", i);
924		gctl_ro_param(req, buf, -1, argv[i]);
925	}
926	errstr = gctl_issue(req);
927	if (errstr != NULL)
928		warnx("can't move object(s):  %s", errstr);
929	gctl_free(req);
930	return;
931}
932
933void
934gvinum_printconfig(int argc, char **argv)
935{
936	printconfig(stdout, "");
937}
938
939void
940gvinum_parityop(int argc, char **argv, int rebuild)
941{
942	struct gctl_req *req;
943	int flags, i;
944	const char *errstr;
945	char *op;
946
947	if (rebuild) {
948		op = "rebuildparity";
949	} else {
950		op = "checkparity";
951	}
952
953	optreset = 1;
954	optind = 1;
955	flags = 0;
956	while ((i = getopt(argc, argv, "fv")) != -1) {
957		switch (i) {
958		case 'f':
959			flags |= GV_FLAG_F;
960			break;
961		case 'v':
962			flags |= GV_FLAG_V;
963			break;
964		case '?':
965		default:
966			warnx("invalid flag '%c'", i);
967			return;
968		}
969	}
970	argc -= optind;
971	argv += optind;
972
973	if (argc != 1) {
974		warn("usage: %s [-f] [-v] <plex>", op);
975		return;
976	}
977
978	req = gctl_get_handle();
979	gctl_ro_param(req, "class", -1, "VINUM");
980	gctl_ro_param(req, "verb", -1, op);
981	gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
982	gctl_ro_param(req, "flags", sizeof(int), &flags);
983	gctl_ro_param(req, "plex", -1, argv[0]);
984
985	errstr = gctl_issue(req);
986	if (errstr)
987		warnx("%s\n", errstr);
988	gctl_free(req);
989}
990
991/* Create a RAID-5 volume. */
992void
993gvinum_raid5(int argc, char **argv)
994{
995
996	if (argc < 2) {
997		warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
998		return;
999	}
1000	create_volume(argc, argv, "raid5");
1001}
1002
1003
1004void
1005gvinum_rename(int argc, char **argv)
1006{
1007	struct gctl_req *req;
1008	const char *errstr;
1009	int flags, j;
1010
1011	flags = 0;
1012
1013	if (argc) {
1014		optreset = 1;
1015		optind = 1;
1016		while ((j = getopt(argc, argv, "r")) != -1) {
1017			switch (j) {
1018			case 'r':
1019				flags |= GV_FLAG_R;
1020				break;
1021			case '?':
1022			default:
1023				return;
1024			}
1025		}
1026		argc -= optind;
1027		argv += optind;
1028	}
1029
1030	switch (argc) {
1031		case 0:
1032			warnx("no object to rename specified");
1033			return;
1034		case 1:
1035			warnx("no new name specified");
1036			return;
1037		case 2:
1038			break;
1039		default:
1040			warnx("more than one new name specified");
1041			return;
1042	}
1043
1044	req = gctl_get_handle();
1045	gctl_ro_param(req, "class", -1, "VINUM");
1046	gctl_ro_param(req, "verb", -1, "rename");
1047	gctl_ro_param(req, "flags", sizeof(int), &flags);
1048	gctl_ro_param(req, "object", -1, argv[0]);
1049	gctl_ro_param(req, "newname", -1, argv[1]);
1050	errstr = gctl_issue(req);
1051	if (errstr != NULL)
1052		warnx("can't rename object:  %s", errstr);
1053	gctl_free(req);
1054	return;
1055}
1056
1057void
1058gvinum_rm(int argc, char **argv)
1059{
1060	struct gctl_req *req;
1061	int flags, i, j;
1062	const char *errstr;
1063	char buf[20];
1064
1065	flags = 0;
1066	optreset = 1;
1067	optind = 1;
1068	while ((j = getopt(argc, argv, "rf")) != -1) {
1069		switch (j) {
1070		case 'f':
1071			flags |= GV_FLAG_F;
1072			break;
1073		case 'r':
1074			flags |= GV_FLAG_R;
1075			break;
1076		case '?':
1077		default:
1078			return;
1079		}
1080	}
1081	argc -= optind;
1082	argv += optind;
1083
1084	req = gctl_get_handle();
1085	gctl_ro_param(req, "class", -1, "VINUM");
1086	gctl_ro_param(req, "verb", -1, "remove");
1087	gctl_ro_param(req, "argc", sizeof(int), &argc);
1088	gctl_ro_param(req, "flags", sizeof(int), &flags);
1089	if (argc) {
1090		for (i = 0; i < argc; i++) {
1091			snprintf(buf, sizeof(buf), "argv%d", i);
1092			gctl_ro_param(req, buf, -1, argv[i]);
1093		}
1094	}
1095	errstr = gctl_issue(req);
1096	if (errstr != NULL) {
1097		warnx("can't remove: %s", errstr);
1098		gctl_free(req);
1099		return;
1100	}
1101	gctl_free(req);
1102}
1103
1104void
1105gvinum_resetconfig(int argc, char **argv)
1106{
1107	struct gctl_req *req;
1108	const char *errstr;
1109	char reply[32];
1110	int flags, i;
1111
1112	flags = 0;
1113	while ((i = getopt(argc, argv, "f")) != -1) {
1114		switch (i) {
1115		case 'f':
1116			flags |= GV_FLAG_F;
1117			break;
1118		default:
1119			warn("invalid flag: %c", i);
1120			return;
1121		}
1122	}
1123	if ((flags & GV_FLAG_F) == 0) {
1124		if (!isatty(STDIN_FILENO)) {
1125			warn("Please enter this command from a tty device\n");
1126			return;
1127		}
1128		printf(" WARNING!  This command will completely wipe out"
1129		    " your gvinum configuration.\n"
1130		    " All data will be lost.  If you really want to do this,"
1131		    " enter the text\n\n"
1132		    " NO FUTURE\n"
1133		    " Enter text -> ");
1134		fgets(reply, sizeof(reply), stdin);
1135		if (strcmp(reply, "NO FUTURE\n")) {
1136			printf("\n No change\n");
1137			return;
1138		}
1139	}
1140	req = gctl_get_handle();
1141	gctl_ro_param(req, "class", -1, "VINUM");
1142	gctl_ro_param(req, "verb", -1, "resetconfig");
1143	errstr = gctl_issue(req);
1144	if (errstr != NULL) {
1145		warnx("can't reset config: %s", errstr);
1146		gctl_free(req);
1147		return;
1148	}
1149	gctl_free(req);
1150	printf("gvinum configuration obliterated\n");
1151}
1152
1153void
1154gvinum_saveconfig(void)
1155{
1156	struct gctl_req *req;
1157	const char *errstr;
1158
1159	req = gctl_get_handle();
1160	gctl_ro_param(req, "class", -1, "VINUM");
1161	gctl_ro_param(req, "verb", -1, "saveconfig");
1162	errstr = gctl_issue(req);
1163	if (errstr != NULL)
1164		warnx("can't save configuration: %s", errstr);
1165	gctl_free(req);
1166}
1167
1168void
1169gvinum_start(int argc, char **argv)
1170{
1171	struct gctl_req *req;
1172	int i, initsize, j;
1173	const char *errstr;
1174	char buf[20];
1175
1176	/* 'start' with no arguments is a no-op. */
1177	if (argc == 1)
1178		return;
1179
1180	initsize = 0;
1181
1182	optreset = 1;
1183	optind = 1;
1184	while ((j = getopt(argc, argv, "S")) != -1) {
1185		switch (j) {
1186		case 'S':
1187			initsize = atoi(optarg);
1188			break;
1189		case '?':
1190		default:
1191			return;
1192		}
1193	}
1194	argc -= optind;
1195	argv += optind;
1196
1197	if (!initsize)
1198		initsize = 512;
1199
1200	req = gctl_get_handle();
1201	gctl_ro_param(req, "class", -1, "VINUM");
1202	gctl_ro_param(req, "verb", -1, "start");
1203	gctl_ro_param(req, "argc", sizeof(int), &argc);
1204	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1205	if (argc) {
1206		for (i = 0; i < argc; i++) {
1207			snprintf(buf, sizeof(buf), "argv%d", i);
1208			gctl_ro_param(req, buf, -1, argv[i]);
1209		}
1210	}
1211	errstr = gctl_issue(req);
1212	if (errstr != NULL) {
1213		warnx("can't start: %s", errstr);
1214		gctl_free(req);
1215		return;
1216	}
1217
1218	gctl_free(req);
1219}
1220
1221void
1222gvinum_stop(int argc, char **argv)
1223{
1224	int err, fileid;
1225
1226	fileid = kldfind(GVINUMKLD);
1227	if (fileid == -1) {
1228		if (modfind(GVINUMMOD) < 0)
1229			warn("cannot find " GVINUMKLD);
1230		return;
1231	}
1232
1233	/*
1234	 * This little hack prevents that we end up in an infinite loop in
1235	 * g_unload_class().  gv_unload() will return EAGAIN so that the GEOM
1236	 * event thread will be free for the g_wither_geom() call from
1237	 * gv_unload().  It's silly, but it works.
1238	 */
1239	printf("unloading " GVINUMKLD " kernel module... ");
1240	fflush(stdout);
1241	if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1242		sleep(1);
1243		err = kldunload(fileid);
1244	}
1245	if (err != 0) {
1246		printf(" failed!\n");
1247		warn("cannot unload " GVINUMKLD);
1248		return;
1249	}
1250
1251	printf("done\n");
1252	exit(0);
1253}
1254
1255/* Create a striped volume. */
1256void
1257gvinum_stripe(int argc, char **argv)
1258{
1259
1260	if (argc < 2) {
1261		warnx("usage:\tstripe [-fv] [-n name] drives\n");
1262		return;
1263	}
1264	create_volume(argc, argv, "stripe");
1265}
1266
1267/* Grow a subdisk by adding disk backed by provider. */
1268void
1269gvinum_grow(int argc, char **argv)
1270{
1271	struct gctl_req *req;
1272	char *drive, *sdname;
1273	char sdprefix[GV_MAXSDNAME];
1274	struct gv_drive *d;
1275	struct gv_sd *s;
1276	const char *errstr;
1277	int drives, volumes, plexes, subdisks, flags;
1278
1279	flags = 0;
1280	drives = volumes = plexes = subdisks = 0;
1281	if (argc < 3) {
1282		warnx("usage:\tgrow plex drive\n");
1283		return;
1284	}
1285
1286	s = gv_alloc_sd();
1287	if (s == NULL) {
1288		warn("unable to create subdisk");
1289		return;
1290	}
1291	d = gv_alloc_drive();
1292	if (d == NULL) {
1293		warn("unable to create drive");
1294		free(s);
1295		return;
1296	}
1297	/* Lookup device and set an appropriate drive name. */
1298	drive = find_drive();
1299	if (drive == NULL) {
1300		warn("unable to find an appropriate drive name");
1301		free(s);
1302		free(d);
1303		return;
1304	}
1305	strlcpy(d->name, drive, sizeof(d->name));
1306	copy_device(d, argv[2]);
1307
1308	drives = 1;
1309
1310	/* We try to use the plex name as basis for the subdisk name. */
1311	snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]);
1312	sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME);
1313	if (sdname == NULL) {
1314		warn("unable to find an appropriate subdisk name");
1315		free(s);
1316		free(d);
1317		free(drive);
1318		return;
1319	}
1320	strlcpy(s->name, sdname, sizeof(s->name));
1321	free(sdname);
1322	strlcpy(s->plex, argv[1], sizeof(s->plex));
1323	strlcpy(s->drive, d->name, sizeof(s->drive));
1324	subdisks = 1;
1325
1326	req = gctl_get_handle();
1327	gctl_ro_param(req, "class", -1, "VINUM");
1328	gctl_ro_param(req, "verb", -1, "create");
1329	gctl_ro_param(req, "flags", sizeof(int), &flags);
1330	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
1331	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
1332	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
1333	gctl_ro_param(req, "drives", sizeof(int), &drives);
1334	gctl_ro_param(req, "drive0", sizeof(*d), d);
1335	gctl_ro_param(req, "sd0", sizeof(*s), s);
1336	errstr = gctl_issue(req);
1337	free(drive);
1338	if (errstr != NULL) {
1339		warnx("unable to grow plex: %s", errstr);
1340		free(s);
1341		free(d);
1342		return;
1343	}
1344	gctl_free(req);
1345}
1346
1347void
1348parseline(int argc, char **argv)
1349{
1350	if (argc <= 0)
1351		return;
1352
1353	if (!strcmp(argv[0], "create"))
1354		gvinum_create(argc, argv);
1355	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
1356		exit(0);
1357	else if (!strcmp(argv[0], "attach"))
1358		gvinum_attach(argc, argv);
1359	else if (!strcmp(argv[0], "detach"))
1360		gvinum_detach(argc, argv);
1361	else if (!strcmp(argv[0], "concat"))
1362		gvinum_concat(argc, argv);
1363	else if (!strcmp(argv[0], "grow"))
1364		gvinum_grow(argc, argv);
1365	else if (!strcmp(argv[0], "help"))
1366		gvinum_help();
1367	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
1368		gvinum_list(argc, argv);
1369	else if (!strcmp(argv[0], "ld"))
1370		gvinum_list(argc, argv);
1371	else if (!strcmp(argv[0], "lp"))
1372		gvinum_list(argc, argv);
1373	else if (!strcmp(argv[0], "ls"))
1374		gvinum_list(argc, argv);
1375	else if (!strcmp(argv[0], "lv"))
1376		gvinum_list(argc, argv);
1377	else if (!strcmp(argv[0], "mirror"))
1378		gvinum_mirror(argc, argv);
1379	else if (!strcmp(argv[0], "move"))
1380		gvinum_move(argc, argv);
1381	else if (!strcmp(argv[0], "mv"))
1382		gvinum_move(argc, argv);
1383	else if (!strcmp(argv[0], "printconfig"))
1384		gvinum_printconfig(argc, argv);
1385	else if (!strcmp(argv[0], "raid5"))
1386		gvinum_raid5(argc, argv);
1387	else if (!strcmp(argv[0], "rename"))
1388		gvinum_rename(argc, argv);
1389	else if (!strcmp(argv[0], "resetconfig"))
1390		gvinum_resetconfig(argc, argv);
1391	else if (!strcmp(argv[0], "rm"))
1392		gvinum_rm(argc, argv);
1393	else if (!strcmp(argv[0], "saveconfig"))
1394		gvinum_saveconfig();
1395	else if (!strcmp(argv[0], "setstate"))
1396		gvinum_setstate(argc, argv);
1397	else if (!strcmp(argv[0], "start"))
1398		gvinum_start(argc, argv);
1399	else if (!strcmp(argv[0], "stop"))
1400		gvinum_stop(argc, argv);
1401	else if (!strcmp(argv[0], "stripe"))
1402		gvinum_stripe(argc, argv);
1403	else if (!strcmp(argv[0], "checkparity"))
1404		gvinum_parityop(argc, argv, 0);
1405	else if (!strcmp(argv[0], "rebuildparity"))
1406		gvinum_parityop(argc, argv, 1);
1407	else
1408		printf("unknown command '%s'\n", argv[0]);
1409
1410	return;
1411}
1412
1413/*
1414 * The guts of printconfig.  This is called from gvinum_printconfig and from
1415 * gvinum_create when called without an argument, in order to give the user
1416 * something to edit.
1417 */
1418void
1419printconfig(FILE *of, char *comment)
1420{
1421	struct gctl_req *req;
1422	struct utsname uname_s;
1423	const char *errstr;
1424	time_t now;
1425	char buf[GV_CFG_LEN + 1];
1426
1427	uname(&uname_s);
1428	time(&now);
1429
1430	req = gctl_get_handle();
1431	gctl_ro_param(req, "class", -1, "VINUM");
1432	gctl_ro_param(req, "verb", -1, "getconfig");
1433	gctl_ro_param(req, "comment", -1, comment);
1434	gctl_rw_param(req, "config", sizeof(buf), buf);
1435	errstr = gctl_issue(req);
1436	if (errstr != NULL) {
1437		warnx("can't get configuration: %s", errstr);
1438		return;
1439	}
1440	gctl_free(req);
1441
1442	fprintf(of, "# Vinum configuration of %s, saved at %s",
1443	    uname_s.nodename,
1444	    ctime(&now));
1445
1446	if (*comment != '\0')
1447	    fprintf(of, "# Current configuration:\n");
1448
1449	fprintf(of, "%s", buf);
1450}
1451