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