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