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