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