1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
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
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
61static void gvinum_attach(int, char * const *);
62static void gvinum_concat(int, char * const *);
63static void gvinum_create(int, char * const *);
64static void gvinum_detach(int, char * const *);
65static void gvinum_grow(int, char * const *);
66static void gvinum_help(void);
67static void gvinum_list(int, char * const *);
68static void gvinum_move(int, char * const *);
69static void gvinum_mirror(int, char * const *);
70static void gvinum_parityop(int, char * const * , int);
71static void gvinum_printconfig(int, char * const *);
72static void gvinum_raid5(int, char * const *);
73static void gvinum_rename(int, char * const *);
74static void gvinum_resetconfig(int, char * const *);
75static void gvinum_rm(int, char * const *);
76static void gvinum_saveconfig(void);
77static void gvinum_setstate(int, char * const *);
78static void gvinum_start(int, char * const *);
79static void gvinum_stop(int, char * const *);
80static void gvinum_stripe(int, char * const *);
81static void parseline(int, char * const *);
82static void printconfig(FILE *, const char *);
83
84static char *create_drive(const char *);
85static void create_volume(int, char * const * , const char *);
86static char *find_name(const char *, int, int);
87static const char *find_pattern(char *, const char *);
88static void copy_device(struct gv_drive *, const char *);
89#define	find_drive()							\
90    find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME)
91
92int
93main(int argc, char **argv)
94{
95	int tokens;
96	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
97
98	/* Load the module if necessary. */
99	if (modfind(GVINUMMOD) < 0) {
100		if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0)
101			err(1, GVINUMKLD ": Kernel module not available");
102	}
103
104	/* Arguments given on the command line. */
105	if (argc > 1) {
106		argc--;
107		argv++;
108		parseline(argc, argv);
109
110	/* Interactive mode. */
111	} else {
112		for (;;) {
113			inputline = readline("gvinum -> ");
114			if (inputline == NULL) {
115				if (ferror(stdin)) {
116					err(1, "can't read input");
117				} else {
118					printf("\n");
119					exit(0);
120				}
121			} else if (*inputline) {
122				add_history(inputline);
123				strcpy(buffer, inputline);
124				free(inputline);
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. */
135static void
136gvinum_attach(int argc, char * const *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
172static void
173gvinum_create(int argc, char * const *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], *sdname;
185	const char *ed;
186	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
187	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
188
189	tmp = NULL;
190	flags = 0;
191	for (i = 1; i < argc; i++) {
192		/* Force flag used to ignore already created drives. */
193		if (!strcmp(argv[i], "-f")) {
194			flags |= GV_FLAG_F;
195		/* Else it must be a file. */
196		} else {
197			if ((tmp = fopen(argv[i], "r")) == NULL) {
198				warn("can't open '%s' for reading", argv[i]);
199				return;
200			}
201		}
202	}
203
204	/* We didn't get a file. */
205	if (tmp == NULL) {
206		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
207
208		if ((fd = mkstemp(tmpfile)) == -1) {
209			warn("temporary file not accessible");
210			return;
211		}
212		if ((tmp = fdopen(fd, "w")) == NULL) {
213			warn("can't open '%s' for writing", tmpfile);
214			return;
215		}
216		printconfig(tmp, "# ");
217		fclose(tmp);
218
219		ed = getenv("EDITOR");
220		if (ed == NULL)
221			ed = _PATH_VI;
222
223		snprintf(commandline, sizeof(commandline), "%s %s", ed,
224		    tmpfile);
225		status = system(commandline);
226		if (status != 0) {
227			warn("couldn't exec %s; status: %d", ed, status);
228			return;
229		}
230
231		if ((tmp = fopen(tmpfile, "r")) == NULL) {
232			warn("can't open '%s' for reading", tmpfile);
233			return;
234		}
235	}
236
237	req = gctl_get_handle();
238	gctl_ro_param(req, "class", -1, "VINUM");
239	gctl_ro_param(req, "verb", -1, "create");
240	gctl_ro_param(req, "flags", sizeof(int), &flags);
241
242	drives = volumes = plexes = subdisks = 0;
243	plex_in_volume = sd_in_plex = undeffd = 0;
244	plex[0] = '\0';
245	errors = 0;
246	line = 1;
247	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
248
249		/* Skip empty lines and comments. */
250		if (*buf == '\0' || *buf == '#') {
251			line++;
252			continue;
253		}
254
255		/* Kill off the newline. */
256		buf[strlen(buf) - 1] = '\0';
257
258		/*
259		 * Copy the original input line in case we need it for error
260		 * output.
261		 */
262		strlcpy(original, buf, sizeof(original));
263
264		tokens = gv_tokenize(buf, token, GV_MAXARGS);
265		if (tokens <= 0) {
266			line++;
267			continue;
268		}
269
270		/* Volume definition. */
271		if (!strcmp(token[0], "volume")) {
272			v = gv_new_volume(tokens, token);
273			if (v == NULL) {
274				warnx("line %d: invalid volume definition",
275				    line);
276				warnx("line %d: '%s'", line, original);
277				errors++;
278				line++;
279				continue;
280			}
281
282			/* Reset plex count for this volume. */
283			plex_in_volume = 0;
284
285			/*
286			 * Set default volume name for following plex
287			 * definitions.
288			 */
289			strlcpy(volume, v->name, sizeof(volume));
290
291			snprintf(buf1, sizeof(buf1), "volume%d", volumes);
292			gctl_ro_param(req, buf1, sizeof(*v), v);
293			volumes++;
294
295		/* Plex definition. */
296		} else if (!strcmp(token[0], "plex")) {
297			p = gv_new_plex(tokens, token);
298			if (p == NULL) {
299				warnx("line %d: invalid plex definition", line);
300				warnx("line %d: '%s'", line, original);
301				errors++;
302				line++;
303				continue;
304			}
305
306			/* Reset subdisk count for this plex. */
307			sd_in_plex = 0;
308
309			/* Default name. */
310			if (strlen(p->name) == 0) {
311				snprintf(p->name, sizeof(p->name), "%s.p%d",
312				    volume, plex_in_volume++);
313			}
314
315			/* Default volume. */
316			if (strlen(p->volume) == 0) {
317				snprintf(p->volume, sizeof(p->volume), "%s",
318				    volume);
319			}
320
321			/*
322			 * Set default plex name for following subdisk
323			 * definitions.
324			 */
325			strlcpy(plex, p->name, sizeof(plex));
326
327			snprintf(buf1, sizeof(buf1), "plex%d", plexes);
328			gctl_ro_param(req, buf1, sizeof(*p), p);
329			plexes++;
330
331		/* Subdisk definition. */
332		} else if (!strcmp(token[0], "sd")) {
333			s = gv_new_sd(tokens, token);
334			if (s == NULL) {
335				warnx("line %d: invalid subdisk "
336				    "definition:", line);
337				warnx("line %d: '%s'", line, original);
338				errors++;
339				line++;
340				continue;
341			}
342
343			/* Default name. */
344			if (strlen(s->name) == 0) {
345				if (strlen(plex) == 0) {
346					sdname = find_name("gvinumsubdisk.p",
347					    GV_TYPE_SD, GV_MAXSDNAME);
348					snprintf(s->name, sizeof(s->name),
349					    "%s.s%d", sdname, undeffd++);
350					free(sdname);
351				} else {
352					snprintf(s->name, sizeof(s->name),
353					    "%s.s%d",plex, sd_in_plex++);
354				}
355			}
356
357			/* Default plex. */
358			if (strlen(s->plex) == 0)
359				snprintf(s->plex, sizeof(s->plex), "%s", plex);
360
361			snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
362			gctl_ro_param(req, buf1, sizeof(*s), s);
363			subdisks++;
364
365		/* Subdisk definition. */
366		} else if (!strcmp(token[0], "drive")) {
367			d = gv_new_drive(tokens, token);
368			if (d == NULL) {
369				warnx("line %d: invalid drive definition:",
370				    line);
371				warnx("line %d: '%s'", line, original);
372				errors++;
373				line++;
374				continue;
375			}
376
377			snprintf(buf1, sizeof(buf1), "drive%d", drives);
378			gctl_ro_param(req, buf1, sizeof(*d), d);
379			drives++;
380
381		/* Everything else is bogus. */
382		} else {
383			warnx("line %d: invalid definition:", line);
384			warnx("line %d: '%s'", line, original);
385			errors++;
386		}
387		line++;
388	}
389
390	fclose(tmp);
391	unlink(tmpfile);
392
393	if (!errors && (volumes || plexes || subdisks || drives)) {
394		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
395		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
396		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
397		gctl_ro_param(req, "drives", sizeof(int), &drives);
398		errstr = gctl_issue(req);
399		if (errstr != NULL)
400			warnx("create failed: %s", errstr);
401	}
402	gctl_free(req);
403}
404
405/* Create a concatenated volume. */
406static void
407gvinum_concat(int argc, char * const *argv)
408{
409
410	if (argc < 2) {
411		warnx("usage:\tconcat [-fv] [-n name] drives\n");
412		return;
413	}
414	create_volume(argc, argv, "concat");
415}
416
417/* Create a drive quick and dirty. */
418static char *
419create_drive(const 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 */
486static void
487create_volume(int argc, char * const *argv, const 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>. */
548static const char *
549find_pattern(char *line, const 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. */
566static char *
567find_name(const char *prefix, int type, int namelen)
568{
569	struct gctl_req *req;
570	char comment[1], buf[GV_CFG_LEN - 1], *sname, *ptr;
571	const char *errstr, *name;
572	int i, n, begin, len, conflict;
573	char line[1024];
574
575	comment[0] = '\0';
576	buf[0] = '\0';
577
578	/* Find a name. Fetch out configuration first. */
579	req = gctl_get_handle();
580	gctl_ro_param(req, "class", -1, "VINUM");
581	gctl_ro_param(req, "verb", -1, "getconfig");
582	gctl_ro_param(req, "comment", -1, comment);
583	gctl_add_param(req, "config", sizeof(buf), buf,
584	    GCTL_PARAM_WR | GCTL_PARAM_ASCII);
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], config[GV_CFG_LEN + 1];
810	const char *cmd;
811
812	flags = 0;
813	cmd = "list";
814
815	if (argc) {
816		optreset = 1;
817		optind = 1;
818		cmd = argv[0];
819		while ((j = getopt(argc, argv, "rsvV")) != -1) {
820			switch (j) {
821			case 'r':
822				flags |= GV_FLAG_R;
823				break;
824			case 's':
825				flags |= GV_FLAG_S;
826				break;
827			case 'v':
828				flags |= GV_FLAG_V;
829				break;
830			case 'V':
831				flags |= GV_FLAG_V;
832				flags |= GV_FLAG_VV;
833				break;
834			case '?':
835			default:
836				return;
837			}
838		}
839		argc -= optind;
840		argv += optind;
841
842	}
843
844	config[0] = '\0';
845
846	req = gctl_get_handle();
847	gctl_ro_param(req, "class", -1, "VINUM");
848	gctl_ro_param(req, "verb", -1, "list");
849	gctl_ro_param(req, "cmd", -1, cmd);
850	gctl_ro_param(req, "argc", sizeof(int), &argc);
851	gctl_ro_param(req, "flags", sizeof(int), &flags);
852	gctl_add_param(req, "config", sizeof(config), config,
853	    GCTL_PARAM_WR | GCTL_PARAM_ASCII);
854	if (argc) {
855		for (i = 0; i < argc; i++) {
856			snprintf(buf, sizeof(buf), "argv%d", i);
857			gctl_ro_param(req, buf, -1, argv[i]);
858		}
859	}
860	errstr = gctl_issue(req);
861	if (errstr != NULL) {
862		warnx("can't get configuration: %s", errstr);
863		gctl_free(req);
864		return;
865	}
866
867	printf("%s", config);
868	gctl_free(req);
869}
870
871/* Create a mirrored volume. */
872static void
873gvinum_mirror(int argc, char * const *argv)
874{
875
876	if (argc < 2) {
877		warnx("usage\tmirror [-fsv] [-n name] drives\n");
878		return;
879	}
880	create_volume(argc, argv, "mirror");
881}
882
883/* Note that move is currently of form '[-r] target object [...]' */
884static void
885gvinum_move(int argc, char * const *argv)
886{
887	struct gctl_req *req;
888	const char *errstr;
889	char buf[20];
890	int flags, i, j;
891
892	flags = 0;
893	if (argc) {
894		optreset = 1;
895		optind = 1;
896		while ((j = getopt(argc, argv, "f")) != -1) {
897			switch (j) {
898			case 'f':
899				flags |= GV_FLAG_F;
900				break;
901			case '?':
902			default:
903				return;
904			}
905		}
906		argc -= optind;
907		argv += optind;
908	}
909
910	switch (argc) {
911		case 0:
912			warnx("no destination or object(s) to move specified");
913			return;
914		case 1:
915			warnx("no object(s) to move specified");
916			return;
917		default:
918			break;
919	}
920
921	req = gctl_get_handle();
922	gctl_ro_param(req, "class", -1, "VINUM");
923	gctl_ro_param(req, "verb", -1, "move");
924	gctl_ro_param(req, "argc", sizeof(int), &argc);
925	gctl_ro_param(req, "flags", sizeof(int), &flags);
926	gctl_ro_param(req, "destination", -1, argv[0]);
927	for (i = 1; i < argc; i++) {
928		snprintf(buf, sizeof(buf), "argv%d", i);
929		gctl_ro_param(req, buf, -1, argv[i]);
930	}
931	errstr = gctl_issue(req);
932	if (errstr != NULL)
933		warnx("can't move object(s):  %s", errstr);
934	gctl_free(req);
935}
936
937static void
938gvinum_printconfig(int argc __unused, char * const *argv __unused)
939{
940
941	printconfig(stdout, "");
942}
943
944static void
945gvinum_parityop(int argc, char * const *argv, int rebuild)
946{
947	struct gctl_req *req;
948	int flags, i;
949	const char *errstr;
950	const char *op;
951
952	if (rebuild) {
953		op = "rebuildparity";
954	} else {
955		op = "checkparity";
956	}
957
958	optreset = 1;
959	optind = 1;
960	flags = 0;
961	while ((i = getopt(argc, argv, "fv")) != -1) {
962		switch (i) {
963		case 'f':
964			flags |= GV_FLAG_F;
965			break;
966		case 'v':
967			flags |= GV_FLAG_V;
968			break;
969		default:
970			warnx("invalid flag '%c'", i);
971			return;
972		}
973	}
974	argc -= optind;
975	argv += optind;
976
977	if (argc != 1) {
978		warn("usage: %s [-f] [-v] <plex>", op);
979		return;
980	}
981
982	req = gctl_get_handle();
983	gctl_ro_param(req, "class", -1, "VINUM");
984	gctl_ro_param(req, "verb", -1, op);
985	gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
986	gctl_ro_param(req, "flags", sizeof(int), &flags);
987	gctl_ro_param(req, "plex", -1, argv[0]);
988
989	errstr = gctl_issue(req);
990	if (errstr)
991		warnx("%s\n", errstr);
992	gctl_free(req);
993}
994
995/* Create a RAID-5 volume. */
996static void
997gvinum_raid5(int argc, char * const *argv)
998{
999
1000	if (argc < 2) {
1001		warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
1002		return;
1003	}
1004	create_volume(argc, argv, "raid5");
1005}
1006
1007static void
1008gvinum_rename(int argc, char * const *argv)
1009{
1010	struct gctl_req *req;
1011	const char *errstr;
1012	int flags, j;
1013
1014	flags = 0;
1015
1016	if (argc) {
1017		optreset = 1;
1018		optind = 1;
1019		while ((j = getopt(argc, argv, "r")) != -1) {
1020			switch (j) {
1021			case 'r':
1022				flags |= GV_FLAG_R;
1023				break;
1024			default:
1025				return;
1026			}
1027		}
1028		argc -= optind;
1029		argv += optind;
1030	}
1031
1032	switch (argc) {
1033		case 0:
1034			warnx("no object to rename specified");
1035			return;
1036		case 1:
1037			warnx("no new name specified");
1038			return;
1039		case 2:
1040			break;
1041		default:
1042			warnx("more than one new name specified");
1043			return;
1044	}
1045
1046	req = gctl_get_handle();
1047	gctl_ro_param(req, "class", -1, "VINUM");
1048	gctl_ro_param(req, "verb", -1, "rename");
1049	gctl_ro_param(req, "flags", sizeof(int), &flags);
1050	gctl_ro_param(req, "object", -1, argv[0]);
1051	gctl_ro_param(req, "newname", -1, argv[1]);
1052	errstr = gctl_issue(req);
1053	if (errstr != NULL)
1054		warnx("can't rename object:  %s", errstr);
1055	gctl_free(req);
1056}
1057
1058static void
1059gvinum_rm(int argc, char * const *argv)
1060{
1061	struct gctl_req *req;
1062	int flags, i, j;
1063	const char *errstr;
1064	char buf[20];
1065
1066	flags = 0;
1067	optreset = 1;
1068	optind = 1;
1069	while ((j = getopt(argc, argv, "rf")) != -1) {
1070		switch (j) {
1071		case 'f':
1072			flags |= GV_FLAG_F;
1073			break;
1074		case 'r':
1075			flags |= GV_FLAG_R;
1076			break;
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
1104static void
1105gvinum_resetconfig(int argc, char * const *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
1153static void
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
1168static void
1169gvinum_start(int argc, char * const *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		default:
1190			return;
1191		}
1192	}
1193	argc -= optind;
1194	argv += optind;
1195
1196	if (!initsize)
1197		initsize = 512;
1198
1199	req = gctl_get_handle();
1200	gctl_ro_param(req, "class", -1, "VINUM");
1201	gctl_ro_param(req, "verb", -1, "start");
1202	gctl_ro_param(req, "argc", sizeof(int), &argc);
1203	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1204	if (argc) {
1205		for (i = 0; i < argc; i++) {
1206			snprintf(buf, sizeof(buf), "argv%d", i);
1207			gctl_ro_param(req, buf, -1, argv[i]);
1208		}
1209	}
1210	errstr = gctl_issue(req);
1211	if (errstr != NULL) {
1212		warnx("can't start: %s", errstr);
1213		gctl_free(req);
1214		return;
1215	}
1216
1217	gctl_free(req);
1218}
1219
1220static void
1221gvinum_stop(int argc __unused, char * const *argv __unused)
1222{
1223	int err, fileid;
1224
1225	fileid = kldfind(GVINUMKLD);
1226	if (fileid == -1) {
1227		if (modfind(GVINUMMOD) < 0)
1228			warn("cannot find " GVINUMKLD);
1229		return;
1230	}
1231
1232	/*
1233	 * This little hack prevents that we end up in an infinite loop in
1234	 * g_unload_class().  gv_unload() will return EAGAIN so that the GEOM
1235	 * event thread will be free for the g_wither_geom() call from
1236	 * gv_unload().  It's silly, but it works.
1237	 */
1238	printf("unloading " GVINUMKLD " kernel module... ");
1239	fflush(stdout);
1240	if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1241		sleep(1);
1242		err = kldunload(fileid);
1243	}
1244	if (err != 0) {
1245		printf(" failed!\n");
1246		warn("cannot unload " GVINUMKLD);
1247		return;
1248	}
1249
1250	printf("done\n");
1251	exit(0);
1252}
1253
1254/* Create a striped volume. */
1255static void
1256gvinum_stripe(int argc, char * const *argv)
1257{
1258
1259	if (argc < 2) {
1260		warnx("usage:\tstripe [-fv] [-n name] drives\n");
1261		return;
1262	}
1263	create_volume(argc, argv, "stripe");
1264}
1265
1266/* Grow a subdisk by adding disk backed by provider. */
1267static void
1268gvinum_grow(int argc, char * const *argv)
1269{
1270	struct gctl_req *req;
1271	char *drive, *sdname;
1272	char sdprefix[GV_MAXSDNAME];
1273	struct gv_drive *d;
1274	struct gv_sd *s;
1275	const char *errstr;
1276	int drives, volumes, plexes, subdisks, flags;
1277
1278	flags = 0;
1279	drives = volumes = plexes = subdisks = 0;
1280	if (argc < 3) {
1281		warnx("usage:\tgrow plex drive\n");
1282		return;
1283	}
1284
1285	s = gv_alloc_sd();
1286	if (s == NULL) {
1287		warn("unable to create subdisk");
1288		return;
1289	}
1290	d = gv_alloc_drive();
1291	if (d == NULL) {
1292		warn("unable to create drive");
1293		free(s);
1294		return;
1295	}
1296	/* Lookup device and set an appropriate drive name. */
1297	drive = find_drive();
1298	if (drive == NULL) {
1299		warn("unable to find an appropriate drive name");
1300		free(s);
1301		free(d);
1302		return;
1303	}
1304	strlcpy(d->name, drive, sizeof(d->name));
1305	copy_device(d, argv[2]);
1306
1307	drives = 1;
1308
1309	/* We try to use the plex name as basis for the subdisk name. */
1310	snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]);
1311	sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME);
1312	if (sdname == NULL) {
1313		warn("unable to find an appropriate subdisk name");
1314		free(s);
1315		free(d);
1316		free(drive);
1317		return;
1318	}
1319	strlcpy(s->name, sdname, sizeof(s->name));
1320	free(sdname);
1321	strlcpy(s->plex, argv[1], sizeof(s->plex));
1322	strlcpy(s->drive, d->name, sizeof(s->drive));
1323	subdisks = 1;
1324
1325	req = gctl_get_handle();
1326	gctl_ro_param(req, "class", -1, "VINUM");
1327	gctl_ro_param(req, "verb", -1, "create");
1328	gctl_ro_param(req, "flags", sizeof(int), &flags);
1329	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
1330	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
1331	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
1332	gctl_ro_param(req, "drives", sizeof(int), &drives);
1333	gctl_ro_param(req, "drive0", sizeof(*d), d);
1334	gctl_ro_param(req, "sd0", sizeof(*s), s);
1335	errstr = gctl_issue(req);
1336	free(drive);
1337	if (errstr != NULL) {
1338		warnx("unable to grow plex: %s", errstr);
1339		free(s);
1340		free(d);
1341		return;
1342	}
1343	gctl_free(req);
1344}
1345
1346static void
1347parseline(int argc, char * const *argv)
1348{
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
1411/*
1412 * The guts of printconfig.  This is called from gvinum_printconfig and from
1413 * gvinum_create when called without an argument, in order to give the user
1414 * something to edit.
1415 */
1416static void
1417printconfig(FILE *of, const char *comment)
1418{
1419	struct gctl_req *req;
1420	struct utsname uname_s;
1421	const char *errstr;
1422	time_t now;
1423	char buf[GV_CFG_LEN + 1];
1424
1425	uname(&uname_s);
1426	time(&now);
1427	buf[0] = '\0';
1428
1429	req = gctl_get_handle();
1430	gctl_ro_param(req, "class", -1, "VINUM");
1431	gctl_ro_param(req, "verb", -1, "getconfig");
1432	gctl_ro_param(req, "comment", -1, comment);
1433	gctl_add_param(req, "config", sizeof(buf), buf,
1434	    GCTL_PARAM_WR | GCTL_PARAM_ASCII);
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