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$
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 line, 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				line++;		    /* count the lines */
126				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
127				if (tokens)
128					parseline(tokens, token);
129			}
130		}
131	}
132	exit(0);
133}
134
135/* Attach a plex to a volume or a subdisk to a plex. */
136static void
137gvinum_attach(int argc, char * const *argv)
138{
139	struct gctl_req *req;
140	const char *errstr;
141	int rename;
142	off_t offset;
143
144	rename = 0;
145	offset = -1;
146	if (argc < 3) {
147		warnx("usage:\tattach <subdisk> <plex> [rename] "
148		    "[<plexoffset>]\n"
149		    "\tattach <plex> <volume> [rename]");
150		return;
151	}
152	if (argc > 3) {
153		if (!strcmp(argv[3], "rename")) {
154			rename = 1;
155			if (argc == 5)
156				offset = strtol(argv[4], NULL, 0);
157		} else
158			offset = strtol(argv[3], NULL, 0);
159	}
160	req = gctl_get_handle();
161	gctl_ro_param(req, "class", -1, "VINUM");
162	gctl_ro_param(req, "verb", -1, "attach");
163	gctl_ro_param(req, "child", -1, argv[1]);
164	gctl_ro_param(req, "parent", -1, argv[2]);
165	gctl_ro_param(req, "offset", sizeof(off_t), &offset);
166	gctl_ro_param(req, "rename", sizeof(int), &rename);
167	errstr = gctl_issue(req);
168	if (errstr != NULL)
169		warnx("attach failed: %s", errstr);
170	gctl_free(req);
171}
172
173static void
174gvinum_create(int argc, char * const *argv)
175{
176	struct gctl_req *req;
177	struct gv_drive *d;
178	struct gv_plex *p;
179	struct gv_sd *s;
180	struct gv_volume *v;
181	FILE *tmp;
182	int drives, errors, fd, flags, i, line, plexes, plex_in_volume;
183	int sd_in_plex, status, subdisks, tokens, undeffd, volumes;
184	const char *errstr;
185	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname;
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
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
636static void
637copy_device(struct gv_drive *d, const char *device)
638{
639
640	if (strncmp(device, "/dev/", 5) == 0)
641		strlcpy(d->device, (device + 5), sizeof(d->device));
642	else
643		strlcpy(d->device, device, sizeof(d->device));
644}
645
646/* Detach a plex or subdisk from its parent. */
647static void
648gvinum_detach(int argc, char * const *argv)
649{
650	const char *errstr;
651	struct gctl_req *req;
652	int flags, i;
653
654	flags = 0;
655	optreset = 1;
656	optind = 1;
657	while ((i = getopt(argc, argv, "f")) != -1) {
658		switch (i) {
659		case 'f':
660			flags |= GV_FLAG_F;
661			break;
662		default:
663			warn("invalid flag: %c", i);
664			return;
665		}
666	}
667	argc -= optind;
668	argv += optind;
669	if (argc != 1) {
670		warnx("usage: detach [-f] <subdisk> | <plex>");
671		return;
672	}
673
674	req = gctl_get_handle();
675	gctl_ro_param(req, "class", -1, "VINUM");
676	gctl_ro_param(req, "verb", -1, "detach");
677	gctl_ro_param(req, "object", -1, argv[0]);
678	gctl_ro_param(req, "flags", sizeof(int), &flags);
679
680	errstr = gctl_issue(req);
681	if (errstr != NULL)
682		warnx("detach failed: %s", errstr);
683	gctl_free(req);
684}
685
686static void
687gvinum_help(void)
688{
689
690	printf("COMMANDS\n"
691	    "checkparity [-f] plex\n"
692	    "        Check the parity blocks of a RAID-5 plex.\n"
693	    "create [-f] description-file\n"
694	    "        Create as per description-file or open editor.\n"
695	    "attach plex volume [rename]\n"
696	    "attach subdisk plex [offset] [rename]\n"
697	    "        Attach a plex to a volume, or a subdisk to a plex\n"
698	    "concat [-fv] [-n name] drives\n"
699	    "        Create a concatenated volume from the specified drives.\n"
700	    "detach [-f] [plex | subdisk]\n"
701	    "        Detach a plex or a subdisk from the volume or plex to\n"
702	    "        which it is attached.\n"
703	    "grow plex drive\n"
704	    "        Grow plex by creating a properly sized subdisk on drive\n"
705	    "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
706	    "        List information about specified objects.\n"
707	    "ld [-r] [-v] [-V] [volume]\n"
708	    "        List information about drives.\n"
709	    "ls [-r] [-v] [-V] [subdisk]\n"
710	    "        List information about subdisks.\n"
711	    "lp [-r] [-v] [-V] [plex]\n"
712	    "        List information about plexes.\n"
713	    "lv [-r] [-v] [-V] [volume]\n"
714	    "        List information about volumes.\n"
715	    "mirror [-fsv] [-n name] drives\n"
716	    "        Create a mirrored volume from the specified drives.\n"
717	    "move | mv -f drive object ...\n"
718	    "        Move the object(s) to the specified drive.\n"
719	    "quit    Exit the vinum program when running in interactive mode."
720	    "  Nor-\n"
721	    "        mally this would be done by entering the EOF character.\n"
722	    "raid5 [-fv] [-s stripesize] [-n name] drives\n"
723	    "        Create a RAID-5 volume from the specified drives.\n"
724	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
725	    "        Change the name of the specified object.\n"
726	    "rebuildparity plex [-f]\n"
727	    "        Rebuild the parity blocks of a RAID-5 plex.\n"
728	    "resetconfig [-f]\n"
729	    "        Reset the complete gvinum configuration\n"
730	    "rm [-r] [-f] volume | plex | subdisk | drive\n"
731	    "        Remove an object.\n"
732	    "saveconfig\n"
733	    "        Save vinum configuration to disk after configuration"
734	    " failures.\n"
735	    "setstate [-f] state [volume | plex | subdisk | drive]\n"
736	    "        Set state without influencing other objects, for"
737	    " diagnostic pur-\n"
738	    "        poses only.\n"
739	    "start [-S size] volume | plex | subdisk\n"
740	    "        Allow the system to access the objects.\n"
741	    "stripe [-fv] [-n name] drives\n"
742	    "        Create a striped volume from the specified drives.\n"
743	);
744}
745
746static void
747gvinum_setstate(int argc, char * const *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
801static void
802gvinum_list(int argc, char * const *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}
864
865/* Create a mirrored volume. */
866static void
867gvinum_mirror(int argc, char * const *argv)
868{
869
870	if (argc < 2) {
871		warnx("usage\tmirror [-fsv] [-n name] drives\n");
872		return;
873	}
874	create_volume(argc, argv, "mirror");
875}
876
877/* Note that move is currently of form '[-r] target object [...]' */
878static void
879gvinum_move(int argc, char * const *argv)
880{
881	struct gctl_req *req;
882	const char *errstr;
883	char buf[20];
884	int flags, i, j;
885
886	flags = 0;
887	if (argc) {
888		optreset = 1;
889		optind = 1;
890		while ((j = getopt(argc, argv, "f")) != -1) {
891			switch (j) {
892			case 'f':
893				flags |= GV_FLAG_F;
894				break;
895			case '?':
896			default:
897				return;
898			}
899		}
900		argc -= optind;
901		argv += optind;
902	}
903
904	switch (argc) {
905		case 0:
906			warnx("no destination or object(s) to move specified");
907			return;
908		case 1:
909			warnx("no object(s) to move specified");
910			return;
911		default:
912			break;
913	}
914
915	req = gctl_get_handle();
916	gctl_ro_param(req, "class", -1, "VINUM");
917	gctl_ro_param(req, "verb", -1, "move");
918	gctl_ro_param(req, "argc", sizeof(int), &argc);
919	gctl_ro_param(req, "flags", sizeof(int), &flags);
920	gctl_ro_param(req, "destination", -1, argv[0]);
921	for (i = 1; i < argc; i++) {
922		snprintf(buf, sizeof(buf), "argv%d", i);
923		gctl_ro_param(req, buf, -1, argv[i]);
924	}
925	errstr = gctl_issue(req);
926	if (errstr != NULL)
927		warnx("can't move object(s):  %s", errstr);
928	gctl_free(req);
929}
930
931static void
932gvinum_printconfig(int argc, char * const *argv)
933{
934
935	printconfig(stdout, "");
936}
937
938static void
939gvinum_parityop(int argc, char * const *argv, int rebuild)
940{
941	struct gctl_req *req;
942	int flags, i;
943	const char *errstr;
944	char *op;
945
946	if (rebuild) {
947		op = "rebuildparity";
948	} else {
949		op = "checkparity";
950	}
951
952	optreset = 1;
953	optind = 1;
954	flags = 0;
955	while ((i = getopt(argc, argv, "fv")) != -1) {
956		switch (i) {
957		case 'f':
958			flags |= GV_FLAG_F;
959			break;
960		case 'v':
961			flags |= GV_FLAG_V;
962			break;
963		default:
964			warnx("invalid flag '%c'", i);
965			return;
966		}
967	}
968	argc -= optind;
969	argv += optind;
970
971	if (argc != 1) {
972		warn("usage: %s [-f] [-v] <plex>", op);
973		return;
974	}
975
976	req = gctl_get_handle();
977	gctl_ro_param(req, "class", -1, "VINUM");
978	gctl_ro_param(req, "verb", -1, op);
979	gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
980	gctl_ro_param(req, "flags", sizeof(int), &flags);
981	gctl_ro_param(req, "plex", -1, argv[0]);
982
983	errstr = gctl_issue(req);
984	if (errstr)
985		warnx("%s\n", errstr);
986	gctl_free(req);
987}
988
989/* Create a RAID-5 volume. */
990static void
991gvinum_raid5(int argc, char * const *argv)
992{
993
994	if (argc < 2) {
995		warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
996		return;
997	}
998	create_volume(argc, argv, "raid5");
999}
1000
1001static void
1002gvinum_rename(int argc, char * const *argv)
1003{
1004	struct gctl_req *req;
1005	const char *errstr;
1006	int flags, j;
1007
1008	flags = 0;
1009
1010	if (argc) {
1011		optreset = 1;
1012		optind = 1;
1013		while ((j = getopt(argc, argv, "r")) != -1) {
1014			switch (j) {
1015			case 'r':
1016				flags |= GV_FLAG_R;
1017				break;
1018			default:
1019				return;
1020			}
1021		}
1022		argc -= optind;
1023		argv += optind;
1024	}
1025
1026	switch (argc) {
1027		case 0:
1028			warnx("no object to rename specified");
1029			return;
1030		case 1:
1031			warnx("no new name specified");
1032			return;
1033		case 2:
1034			break;
1035		default:
1036			warnx("more than one new name specified");
1037			return;
1038	}
1039
1040	req = gctl_get_handle();
1041	gctl_ro_param(req, "class", -1, "VINUM");
1042	gctl_ro_param(req, "verb", -1, "rename");
1043	gctl_ro_param(req, "flags", sizeof(int), &flags);
1044	gctl_ro_param(req, "object", -1, argv[0]);
1045	gctl_ro_param(req, "newname", -1, argv[1]);
1046	errstr = gctl_issue(req);
1047	if (errstr != NULL)
1048		warnx("can't rename object:  %s", errstr);
1049	gctl_free(req);
1050}
1051
1052static void
1053gvinum_rm(int argc, char * const *argv)
1054{
1055	struct gctl_req *req;
1056	int flags, i, j;
1057	const char *errstr;
1058	char buf[20];
1059
1060	flags = 0;
1061	optreset = 1;
1062	optind = 1;
1063	while ((j = getopt(argc, argv, "rf")) != -1) {
1064		switch (j) {
1065		case 'f':
1066			flags |= GV_FLAG_F;
1067			break;
1068		case 'r':
1069			flags |= GV_FLAG_R;
1070			break;
1071		default:
1072			return;
1073		}
1074	}
1075	argc -= optind;
1076	argv += optind;
1077
1078	req = gctl_get_handle();
1079	gctl_ro_param(req, "class", -1, "VINUM");
1080	gctl_ro_param(req, "verb", -1, "remove");
1081	gctl_ro_param(req, "argc", sizeof(int), &argc);
1082	gctl_ro_param(req, "flags", sizeof(int), &flags);
1083	if (argc) {
1084		for (i = 0; i < argc; i++) {
1085			snprintf(buf, sizeof(buf), "argv%d", i);
1086			gctl_ro_param(req, buf, -1, argv[i]);
1087		}
1088	}
1089	errstr = gctl_issue(req);
1090	if (errstr != NULL) {
1091		warnx("can't remove: %s", errstr);
1092		gctl_free(req);
1093		return;
1094	}
1095	gctl_free(req);
1096}
1097
1098static void
1099gvinum_resetconfig(int argc, char * const *argv)
1100{
1101	struct gctl_req *req;
1102	const char *errstr;
1103	char reply[32];
1104	int flags, i;
1105
1106	flags = 0;
1107	while ((i = getopt(argc, argv, "f")) != -1) {
1108		switch (i) {
1109		case 'f':
1110			flags |= GV_FLAG_F;
1111			break;
1112		default:
1113			warn("invalid flag: %c", i);
1114			return;
1115		}
1116	}
1117	if ((flags & GV_FLAG_F) == 0) {
1118		if (!isatty(STDIN_FILENO)) {
1119			warn("Please enter this command from a tty device\n");
1120			return;
1121		}
1122		printf(" WARNING!  This command will completely wipe out"
1123		    " your gvinum configuration.\n"
1124		    " All data will be lost.  If you really want to do this,"
1125		    " enter the text\n\n"
1126		    " NO FUTURE\n"
1127		    " Enter text -> ");
1128		fgets(reply, sizeof(reply), stdin);
1129		if (strcmp(reply, "NO FUTURE\n")) {
1130			printf("\n No change\n");
1131			return;
1132		}
1133	}
1134	req = gctl_get_handle();
1135	gctl_ro_param(req, "class", -1, "VINUM");
1136	gctl_ro_param(req, "verb", -1, "resetconfig");
1137	errstr = gctl_issue(req);
1138	if (errstr != NULL) {
1139		warnx("can't reset config: %s", errstr);
1140		gctl_free(req);
1141		return;
1142	}
1143	gctl_free(req);
1144	printf("gvinum configuration obliterated\n");
1145}
1146
1147static void
1148gvinum_saveconfig(void)
1149{
1150	struct gctl_req *req;
1151	const char *errstr;
1152
1153	req = gctl_get_handle();
1154	gctl_ro_param(req, "class", -1, "VINUM");
1155	gctl_ro_param(req, "verb", -1, "saveconfig");
1156	errstr = gctl_issue(req);
1157	if (errstr != NULL)
1158		warnx("can't save configuration: %s", errstr);
1159	gctl_free(req);
1160}
1161
1162static void
1163gvinum_start(int argc, char * const *argv)
1164{
1165	struct gctl_req *req;
1166	int i, initsize, j;
1167	const char *errstr;
1168	char buf[20];
1169
1170	/* 'start' with no arguments is a no-op. */
1171	if (argc == 1)
1172		return;
1173
1174	initsize = 0;
1175
1176	optreset = 1;
1177	optind = 1;
1178	while ((j = getopt(argc, argv, "S")) != -1) {
1179		switch (j) {
1180		case 'S':
1181			initsize = atoi(optarg);
1182			break;
1183		default:
1184			return;
1185		}
1186	}
1187	argc -= optind;
1188	argv += optind;
1189
1190	if (!initsize)
1191		initsize = 512;
1192
1193	req = gctl_get_handle();
1194	gctl_ro_param(req, "class", -1, "VINUM");
1195	gctl_ro_param(req, "verb", -1, "start");
1196	gctl_ro_param(req, "argc", sizeof(int), &argc);
1197	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1198	if (argc) {
1199		for (i = 0; i < argc; i++) {
1200			snprintf(buf, sizeof(buf), "argv%d", i);
1201			gctl_ro_param(req, buf, -1, argv[i]);
1202		}
1203	}
1204	errstr = gctl_issue(req);
1205	if (errstr != NULL) {
1206		warnx("can't start: %s", errstr);
1207		gctl_free(req);
1208		return;
1209	}
1210
1211	gctl_free(req);
1212}
1213
1214static void
1215gvinum_stop(int argc, char * const *argv)
1216{
1217	int err, fileid;
1218
1219	fileid = kldfind(GVINUMKLD);
1220	if (fileid == -1) {
1221		if (modfind(GVINUMMOD) < 0)
1222			warn("cannot find " GVINUMKLD);
1223		return;
1224	}
1225
1226	/*
1227	 * This little hack prevents that we end up in an infinite loop in
1228	 * g_unload_class().  gv_unload() will return EAGAIN so that the GEOM
1229	 * event thread will be free for the g_wither_geom() call from
1230	 * gv_unload().  It's silly, but it works.
1231	 */
1232	printf("unloading " GVINUMKLD " kernel module... ");
1233	fflush(stdout);
1234	if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1235		sleep(1);
1236		err = kldunload(fileid);
1237	}
1238	if (err != 0) {
1239		printf(" failed!\n");
1240		warn("cannot unload " GVINUMKLD);
1241		return;
1242	}
1243
1244	printf("done\n");
1245	exit(0);
1246}
1247
1248/* Create a striped volume. */
1249static void
1250gvinum_stripe(int argc, char * const *argv)
1251{
1252
1253	if (argc < 2) {
1254		warnx("usage:\tstripe [-fv] [-n name] drives\n");
1255		return;
1256	}
1257	create_volume(argc, argv, "stripe");
1258}
1259
1260/* Grow a subdisk by adding disk backed by provider. */
1261static void
1262gvinum_grow(int argc, char * const *argv)
1263{
1264	struct gctl_req *req;
1265	char *drive, *sdname;
1266	char sdprefix[GV_MAXSDNAME];
1267	struct gv_drive *d;
1268	struct gv_sd *s;
1269	const char *errstr;
1270	int drives, volumes, plexes, subdisks, flags;
1271
1272	flags = 0;
1273	drives = volumes = plexes = subdisks = 0;
1274	if (argc < 3) {
1275		warnx("usage:\tgrow plex drive\n");
1276		return;
1277	}
1278
1279	s = gv_alloc_sd();
1280	if (s == NULL) {
1281		warn("unable to create subdisk");
1282		return;
1283	}
1284	d = gv_alloc_drive();
1285	if (d == NULL) {
1286		warn("unable to create drive");
1287		free(s);
1288		return;
1289	}
1290	/* Lookup device and set an appropriate drive name. */
1291	drive = find_drive();
1292	if (drive == NULL) {
1293		warn("unable to find an appropriate drive name");
1294		free(s);
1295		free(d);
1296		return;
1297	}
1298	strlcpy(d->name, drive, sizeof(d->name));
1299	copy_device(d, argv[2]);
1300
1301	drives = 1;
1302
1303	/* We try to use the plex name as basis for the subdisk name. */
1304	snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]);
1305	sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME);
1306	if (sdname == NULL) {
1307		warn("unable to find an appropriate subdisk name");
1308		free(s);
1309		free(d);
1310		free(drive);
1311		return;
1312	}
1313	strlcpy(s->name, sdname, sizeof(s->name));
1314	free(sdname);
1315	strlcpy(s->plex, argv[1], sizeof(s->plex));
1316	strlcpy(s->drive, d->name, sizeof(s->drive));
1317	subdisks = 1;
1318
1319	req = gctl_get_handle();
1320	gctl_ro_param(req, "class", -1, "VINUM");
1321	gctl_ro_param(req, "verb", -1, "create");
1322	gctl_ro_param(req, "flags", sizeof(int), &flags);
1323	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
1324	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
1325	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
1326	gctl_ro_param(req, "drives", sizeof(int), &drives);
1327	gctl_ro_param(req, "drive0", sizeof(*d), d);
1328	gctl_ro_param(req, "sd0", sizeof(*s), s);
1329	errstr = gctl_issue(req);
1330	free(drive);
1331	if (errstr != NULL) {
1332		warnx("unable to grow plex: %s", errstr);
1333		free(s);
1334		free(d);
1335		return;
1336	}
1337	gctl_free(req);
1338}
1339
1340static void
1341parseline(int argc, char * const *argv)
1342{
1343
1344	if (argc <= 0)
1345		return;
1346
1347	if (!strcmp(argv[0], "create"))
1348		gvinum_create(argc, argv);
1349	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
1350		exit(0);
1351	else if (!strcmp(argv[0], "attach"))
1352		gvinum_attach(argc, argv);
1353	else if (!strcmp(argv[0], "detach"))
1354		gvinum_detach(argc, argv);
1355	else if (!strcmp(argv[0], "concat"))
1356		gvinum_concat(argc, argv);
1357	else if (!strcmp(argv[0], "grow"))
1358		gvinum_grow(argc, argv);
1359	else if (!strcmp(argv[0], "help"))
1360		gvinum_help();
1361	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
1362		gvinum_list(argc, argv);
1363	else if (!strcmp(argv[0], "ld"))
1364		gvinum_list(argc, argv);
1365	else if (!strcmp(argv[0], "lp"))
1366		gvinum_list(argc, argv);
1367	else if (!strcmp(argv[0], "ls"))
1368		gvinum_list(argc, argv);
1369	else if (!strcmp(argv[0], "lv"))
1370		gvinum_list(argc, argv);
1371	else if (!strcmp(argv[0], "mirror"))
1372		gvinum_mirror(argc, argv);
1373	else if (!strcmp(argv[0], "move"))
1374		gvinum_move(argc, argv);
1375	else if (!strcmp(argv[0], "mv"))
1376		gvinum_move(argc, argv);
1377	else if (!strcmp(argv[0], "printconfig"))
1378		gvinum_printconfig(argc, argv);
1379	else if (!strcmp(argv[0], "raid5"))
1380		gvinum_raid5(argc, argv);
1381	else if (!strcmp(argv[0], "rename"))
1382		gvinum_rename(argc, argv);
1383	else if (!strcmp(argv[0], "resetconfig"))
1384		gvinum_resetconfig(argc, argv);
1385	else if (!strcmp(argv[0], "rm"))
1386		gvinum_rm(argc, argv);
1387	else if (!strcmp(argv[0], "saveconfig"))
1388		gvinum_saveconfig();
1389	else if (!strcmp(argv[0], "setstate"))
1390		gvinum_setstate(argc, argv);
1391	else if (!strcmp(argv[0], "start"))
1392		gvinum_start(argc, argv);
1393	else if (!strcmp(argv[0], "stop"))
1394		gvinum_stop(argc, argv);
1395	else if (!strcmp(argv[0], "stripe"))
1396		gvinum_stripe(argc, argv);
1397	else if (!strcmp(argv[0], "checkparity"))
1398		gvinum_parityop(argc, argv, 0);
1399	else if (!strcmp(argv[0], "rebuildparity"))
1400		gvinum_parityop(argc, argv, 1);
1401	else
1402		printf("unknown command '%s'\n", argv[0]);
1403}
1404
1405/*
1406 * The guts of printconfig.  This is called from gvinum_printconfig and from
1407 * gvinum_create when called without an argument, in order to give the user
1408 * something to edit.
1409 */
1410static void
1411printconfig(FILE *of, const char *comment)
1412{
1413	struct gctl_req *req;
1414	struct utsname uname_s;
1415	const char *errstr;
1416	time_t now;
1417	char buf[GV_CFG_LEN + 1];
1418
1419	uname(&uname_s);
1420	time(&now);
1421
1422	req = gctl_get_handle();
1423	gctl_ro_param(req, "class", -1, "VINUM");
1424	gctl_ro_param(req, "verb", -1, "getconfig");
1425	gctl_ro_param(req, "comment", -1, comment);
1426	gctl_rw_param(req, "config", sizeof(buf), buf);
1427	errstr = gctl_issue(req);
1428	if (errstr != NULL) {
1429		warnx("can't get configuration: %s", errstr);
1430		return;
1431	}
1432	gctl_free(req);
1433
1434	fprintf(of, "# Vinum configuration of %s, saved at %s",
1435	    uname_s.nodename,
1436	    ctime(&now));
1437
1438	if (*comment != '\0')
1439	    fprintf(of, "# Current configuration:\n");
1440
1441	fprintf(of, "%s", buf);
1442}
1443