gvinum.c revision 265536
1130391Sle/*
2190507Slulf *  Copyright (c) 2004 Lukas Ertl
3190507Slulf *  Copyright (c) 2005 Chris Jones
4190507Slulf *  Copyright (c) 2007 Ulf Lilleengen
5130391Sle *  All rights reserved.
6152631Sle *
7152631Sle * Portions of this software were developed for the FreeBSD Project
8152631Sle * by Chris Jones thanks to the support of Google's Summer of Code
9152631Sle * program and mentoring by Lukas Ertl.
10152631Sle *
11130391Sle * Redistribution and use in source and binary forms, with or without
12130391Sle * modification, are permitted provided that the following conditions
13130391Sle * are met:
14130391Sle * 1. Redistributions of source code must retain the above copyright
15130391Sle *    notice, this list of conditions and the following disclaimer.
16130391Sle * 2. Redistributions in binary form must reproduce the above copyright
17130391Sle *    notice, this list of conditions and the following disclaimer in the
18130391Sle *    documentation and/or other materials provided with the distribution.
19152631Sle *
20130391Sle * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21130391Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22130391Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23130391Sle * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24130391Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25130391Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26130391Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27130391Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28130391Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29130391Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30130391Sle * SUCH DAMAGE.
31130391Sle *
32130391Sle * $FreeBSD: stable/10/sbin/gvinum/gvinum.c 265536 2014-05-07 09:55:47Z marius $
33130391Sle */
34130391Sle
35130391Sle#include <sys/param.h>
36130391Sle#include <sys/linker.h>
37130391Sle#include <sys/lock.h>
38130391Sle#include <sys/module.h>
39130391Sle#include <sys/mutex.h>
40130391Sle#include <sys/queue.h>
41130391Sle#include <sys/utsname.h>
42130391Sle
43130391Sle#include <geom/vinum/geom_vinum_var.h>
44130391Sle#include <geom/vinum/geom_vinum_share.h>
45130391Sle
46130391Sle#include <ctype.h>
47130391Sle#include <err.h>
48190507Slulf#include <errno.h>
49130391Sle#include <libgeom.h>
50130391Sle#include <stdint.h>
51130391Sle#include <stdio.h>
52130391Sle#include <stdlib.h>
53220370Sobrien#include <string.h>
54130391Sle#include <paths.h>
55130391Sle#include <readline/readline.h>
56130391Sle#include <readline/history.h>
57130391Sle#include <unistd.h>
58130391Sle
59130391Sle#include "gvinum.h"
60130391Sle
61190507Slulfvoid	gvinum_attach(int, char **);
62190507Slulfvoid	gvinum_concat(int, char **);
63130391Slevoid	gvinum_create(int, char **);
64190507Slulfvoid	gvinum_detach(int, char **);
65190884Slulfvoid	gvinum_grow(int, char **);
66130391Slevoid	gvinum_help(void);
67130391Slevoid	gvinum_list(int, char **);
68152616Slevoid	gvinum_move(int, char **);
69190507Slulfvoid	gvinum_mirror(int, char **);
70138110Slevoid	gvinum_parityop(int, char **, int);
71130391Slevoid	gvinum_printconfig(int, char **);
72190507Slulfvoid	gvinum_raid5(int, char **);
73152616Slevoid	gvinum_rename(int, char **);
74157052Slevoid	gvinum_resetconfig(void);
75130391Slevoid	gvinum_rm(int, char **);
76130391Slevoid	gvinum_saveconfig(void);
77138112Slevoid	gvinum_setstate(int, char **);
78130391Slevoid	gvinum_start(int, char **);
79130391Slevoid	gvinum_stop(int, char **);
80190507Slulfvoid	gvinum_stripe(int, char **);
81130391Slevoid	parseline(int, char **);
82130391Slevoid	printconfig(FILE *, char *);
83130391Sle
84190507Slulfchar	*create_drive(char *);
85190507Slulfvoid	 create_volume(int, char **, char *);
86190507Slulfchar	*find_name(const char *, int, int);
87190507Slulfchar	*find_pattern(char *, char *);
88204665Slulfvoid	 copy_device(struct gv_drive *, const char *);
89204665Slulf#define find_drive() find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME)
90190507Slulf
91130391Sleint
92130391Slemain(int argc, char **argv)
93130391Sle{
94130391Sle	int line, tokens;
95130391Sle	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
96130391Sle
97130391Sle	/* Load the module if necessary. */
98265536Smarius	if (modfind(GVINUMMOD) < 0) {
99265536Smarius		if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0)
100265536Smarius			err(1, GVINUMKLD ": Kernel module not available");
101265536Smarius	}
102130391Sle
103130391Sle	/* Arguments given on the command line. */
104130391Sle	if (argc > 1) {
105130391Sle		argc--;
106130391Sle		argv++;
107130391Sle		parseline(argc, argv);
108130391Sle
109130391Sle	/* Interactive mode. */
110130391Sle	} else {
111130391Sle		for (;;) {
112130391Sle			inputline = readline("gvinum -> ");
113130391Sle			if (inputline == NULL) {
114130391Sle				if (ferror(stdin)) {
115130391Sle					err(1, "can't read input");
116130391Sle				} else {
117130391Sle					printf("\n");
118130391Sle					exit(0);
119130391Sle				}
120130391Sle			} else if (*inputline) {
121130391Sle				add_history(inputline);
122130391Sle				strcpy(buffer, inputline);
123130391Sle				free(inputline);
124130391Sle				line++;		    /* count the lines */
125130391Sle				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
126130391Sle				if (tokens)
127130391Sle					parseline(tokens, token);
128130391Sle			}
129130391Sle		}
130130391Sle	}
131130391Sle	exit(0);
132130391Sle}
133130391Sle
134190507Slulf/* Attach a plex to a volume or a subdisk to a plex. */
135130391Slevoid
136190507Slulfgvinum_attach(int argc, char **argv)
137190507Slulf{
138190507Slulf	struct gctl_req *req;
139190507Slulf	const char *errstr;
140190507Slulf	int rename;
141190507Slulf	off_t offset;
142190507Slulf
143190507Slulf	rename = 0;
144190507Slulf	offset = -1;
145190507Slulf	if (argc < 3) {
146190507Slulf		warnx("usage:\tattach <subdisk> <plex> [rename] "
147190507Slulf		    "[<plexoffset>]\n"
148190507Slulf		    "\tattach <plex> <volume> [rename]");
149190507Slulf		return;
150190507Slulf	}
151190507Slulf	if (argc > 3) {
152190507Slulf		if (!strcmp(argv[3], "rename")) {
153190507Slulf			rename = 1;
154190507Slulf			if (argc == 5)
155190507Slulf				offset = strtol(argv[4], NULL, 0);
156190507Slulf		} else
157190507Slulf			offset = strtol(argv[3], NULL, 0);
158190507Slulf	}
159190507Slulf	req = gctl_get_handle();
160190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
161190507Slulf	gctl_ro_param(req, "verb", -1, "attach");
162190507Slulf	gctl_ro_param(req, "child", -1, argv[1]);
163190507Slulf	gctl_ro_param(req, "parent", -1, argv[2]);
164190507Slulf	gctl_ro_param(req, "offset", sizeof(off_t), &offset);
165190507Slulf	gctl_ro_param(req, "rename", sizeof(int), &rename);
166190507Slulf	errstr = gctl_issue(req);
167190507Slulf	if (errstr != NULL)
168190507Slulf		warnx("attach failed: %s", errstr);
169190507Slulf	gctl_free(req);
170190507Slulf}
171190507Slulf
172190507Slulfvoid
173130391Slegvinum_create(int argc, char **argv)
174130391Sle{
175130391Sle	struct gctl_req *req;
176130391Sle	struct gv_drive *d;
177130391Sle	struct gv_plex *p;
178130391Sle	struct gv_sd *s;
179130391Sle	struct gv_volume *v;
180130391Sle	FILE *tmp;
181190507Slulf	int drives, errors, fd, flags, i, line, plexes, plex_in_volume;
182190507Slulf	int sd_in_plex, status, subdisks, tokens, undeffd, volumes;
183130391Sle	const char *errstr;
184190507Slulf	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname;
185130391Sle	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
186130391Sle	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
187130391Sle
188190507Slulf	tmp = NULL;
189190507Slulf	flags = 0;
190190507Slulf	for (i = 1; i < argc; i++) {
191190507Slulf		/* Force flag used to ignore already created drives. */
192190507Slulf		if (!strcmp(argv[i], "-f")) {
193190507Slulf			flags |= GV_FLAG_F;
194190507Slulf		/* Else it must be a file. */
195190507Slulf		} else {
196190507Slulf			if ((tmp = fopen(argv[1], "r")) == NULL) {
197190507Slulf				warn("can't open '%s' for reading", argv[1]);
198190507Slulf				return;
199190507Slulf			}
200190507Slulf		}
201190507Slulf	}
202190507Slulf
203190507Slulf	/* We didn't get a file. */
204190507Slulf	if (tmp == NULL) {
205133097Sle		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
206133097Sle
207133097Sle		if ((fd = mkstemp(tmpfile)) == -1) {
208133097Sle			warn("temporary file not accessible");
209133097Sle			return;
210133097Sle		}
211133097Sle		if ((tmp = fdopen(fd, "w")) == NULL) {
212133097Sle			warn("can't open '%s' for writing", tmpfile);
213133097Sle			return;
214133097Sle		}
215133097Sle		printconfig(tmp, "# ");
216133097Sle		fclose(tmp);
217133097Sle
218133097Sle		ed = getenv("EDITOR");
219133097Sle		if (ed == NULL)
220133097Sle			ed = _PATH_VI;
221133097Sle
222133097Sle		snprintf(commandline, sizeof(commandline), "%s %s", ed,
223133097Sle		    tmpfile);
224133097Sle		status = system(commandline);
225133097Sle		if (status != 0) {
226133097Sle			warn("couldn't exec %s; status: %d", ed, status);
227133097Sle			return;
228133097Sle		}
229133097Sle
230133097Sle		if ((tmp = fopen(tmpfile, "r")) == NULL) {
231133097Sle			warn("can't open '%s' for reading", tmpfile);
232133097Sle			return;
233133097Sle		}
234130391Sle	}
235130391Sle
236130391Sle	req = gctl_get_handle();
237130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
238130391Sle	gctl_ro_param(req, "verb", -1, "create");
239190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
240130391Sle
241130391Sle	drives = volumes = plexes = subdisks = 0;
242190507Slulf	plex_in_volume = sd_in_plex = undeffd = 0;
243190507Slulf	plex[0] = '\0';
244130391Sle	errors = 0;
245130391Sle	line = 1;
246130391Sle	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
247130391Sle
248130391Sle		/* Skip empty lines and comments. */
249130391Sle		if (*buf == '\0' || *buf == '#') {
250130391Sle			line++;
251130391Sle			continue;
252130391Sle		}
253130391Sle
254130391Sle		/* Kill off the newline. */
255130391Sle		buf[strlen(buf) - 1] = '\0';
256130391Sle
257130391Sle		/*
258130391Sle		 * Copy the original input line in case we need it for error
259130391Sle		 * output.
260130391Sle		 */
261190507Slulf		strlcpy(original, buf, sizeof(original));
262130391Sle
263130391Sle		tokens = gv_tokenize(buf, token, GV_MAXARGS);
264150044Sle		if (tokens <= 0) {
265150044Sle			line++;
266150044Sle			continue;
267150044Sle		}
268130391Sle
269150044Sle		/* Volume definition. */
270150044Sle		if (!strcmp(token[0], "volume")) {
271150044Sle			v = gv_new_volume(tokens, token);
272150044Sle			if (v == NULL) {
273150044Sle				warnx("line %d: invalid volume definition",
274150044Sle				    line);
275150044Sle				warnx("line %d: '%s'", line, original);
276150044Sle				errors++;
277150044Sle				line++;
278150044Sle				continue;
279150044Sle			}
280130391Sle
281150044Sle			/* Reset plex count for this volume. */
282150044Sle			plex_in_volume = 0;
283130391Sle
284150044Sle			/*
285150044Sle			 * Set default volume name for following plex
286150044Sle			 * definitions.
287150044Sle			 */
288190507Slulf			strlcpy(volume, v->name, sizeof(volume));
289130391Sle
290150044Sle			snprintf(buf1, sizeof(buf1), "volume%d", volumes);
291150044Sle			gctl_ro_param(req, buf1, sizeof(*v), v);
292150044Sle			volumes++;
293130391Sle
294150044Sle		/* Plex definition. */
295150044Sle		} else if (!strcmp(token[0], "plex")) {
296150044Sle			p = gv_new_plex(tokens, token);
297150044Sle			if (p == NULL) {
298150044Sle				warnx("line %d: invalid plex definition", line);
299150044Sle				warnx("line %d: '%s'", line, original);
300150044Sle				errors++;
301150044Sle				line++;
302150044Sle				continue;
303150044Sle			}
304130391Sle
305150044Sle			/* Reset subdisk count for this plex. */
306150044Sle			sd_in_plex = 0;
307130391Sle
308150044Sle			/* Default name. */
309150044Sle			if (strlen(p->name) == 0) {
310190507Slulf				snprintf(p->name, sizeof(p->name), "%s.p%d",
311150044Sle				    volume, plex_in_volume++);
312150044Sle			}
313130391Sle
314150044Sle			/* Default volume. */
315150044Sle			if (strlen(p->volume) == 0) {
316190507Slulf				snprintf(p->volume, sizeof(p->volume), "%s",
317150044Sle				    volume);
318150044Sle			}
319130391Sle
320150044Sle			/*
321150044Sle			 * Set default plex name for following subdisk
322150044Sle			 * definitions.
323150044Sle			 */
324190507Slulf			strlcpy(plex, p->name, sizeof(plex));
325130391Sle
326150044Sle			snprintf(buf1, sizeof(buf1), "plex%d", plexes);
327150044Sle			gctl_ro_param(req, buf1, sizeof(*p), p);
328150044Sle			plexes++;
329130391Sle
330150044Sle		/* Subdisk definition. */
331150044Sle		} else if (!strcmp(token[0], "sd")) {
332150044Sle			s = gv_new_sd(tokens, token);
333150044Sle			if (s == NULL) {
334150044Sle				warnx("line %d: invalid subdisk "
335150044Sle				    "definition:", line);
336150044Sle				warnx("line %d: '%s'", line, original);
337150044Sle				errors++;
338150044Sle				line++;
339150044Sle				continue;
340150044Sle			}
341130391Sle
342150044Sle			/* Default name. */
343150044Sle			if (strlen(s->name) == 0) {
344190507Slulf				if (strlen(plex) == 0) {
345190507Slulf					sdname = find_name("gvinumsubdisk.p",
346190507Slulf					    GV_TYPE_SD, GV_MAXSDNAME);
347190507Slulf					snprintf(s->name, sizeof(s->name),
348190507Slulf					    "%s.s%d", sdname, undeffd++);
349190507Slulf					free(sdname);
350190507Slulf				} else {
351190507Slulf					snprintf(s->name, sizeof(s->name),
352190507Slulf					    "%s.s%d",plex, sd_in_plex++);
353190507Slulf				}
354150044Sle			}
355150044Sle
356150044Sle			/* Default plex. */
357150044Sle			if (strlen(s->plex) == 0)
358190507Slulf				snprintf(s->plex, sizeof(s->plex), "%s", plex);
359150044Sle
360150044Sle			snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
361150044Sle			gctl_ro_param(req, buf1, sizeof(*s), s);
362150044Sle			subdisks++;
363150044Sle
364150044Sle		/* Subdisk definition. */
365150044Sle		} else if (!strcmp(token[0], "drive")) {
366150044Sle			d = gv_new_drive(tokens, token);
367150044Sle			if (d == NULL) {
368150044Sle				warnx("line %d: invalid drive definition:",
369150044Sle				    line);
370130391Sle				warnx("line %d: '%s'", line, original);
371130391Sle				errors++;
372150044Sle				line++;
373150044Sle				continue;
374130391Sle			}
375150044Sle
376150044Sle			snprintf(buf1, sizeof(buf1), "drive%d", drives);
377150044Sle			gctl_ro_param(req, buf1, sizeof(*d), d);
378150044Sle			drives++;
379150044Sle
380150044Sle		/* Everything else is bogus. */
381150044Sle		} else {
382150044Sle			warnx("line %d: invalid definition:", line);
383150044Sle			warnx("line %d: '%s'", line, original);
384150044Sle			errors++;
385130391Sle		}
386130391Sle		line++;
387130391Sle	}
388130391Sle
389130391Sle	fclose(tmp);
390130391Sle	unlink(tmpfile);
391130391Sle
392130391Sle	if (!errors && (volumes || plexes || subdisks || drives)) {
393130391Sle		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
394130391Sle		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
395130391Sle		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
396130391Sle		gctl_ro_param(req, "drives", sizeof(int), &drives);
397130391Sle		errstr = gctl_issue(req);
398130391Sle		if (errstr != NULL)
399130391Sle			warnx("create failed: %s", errstr);
400130391Sle	}
401130391Sle	gctl_free(req);
402130391Sle}
403130391Sle
404190507Slulf/* Create a concatenated volume. */
405130391Slevoid
406190507Slulfgvinum_concat(int argc, char **argv)
407190507Slulf{
408190507Slulf
409190507Slulf	if (argc < 2) {
410190507Slulf		warnx("usage:\tconcat [-fv] [-n name] drives\n");
411190507Slulf		return;
412190507Slulf	}
413190507Slulf	create_volume(argc, argv, "concat");
414190507Slulf}
415190507Slulf
416190507Slulf
417190507Slulf/* Create a drive quick and dirty. */
418190507Slulfchar *
419190507Slulfcreate_drive(char *device)
420190507Slulf{
421190507Slulf	struct gv_drive *d;
422190507Slulf	struct gctl_req *req;
423190507Slulf	const char *errstr;
424190507Slulf	char *drivename, *dname;
425190507Slulf	int drives, i, flags, volumes, subdisks, plexes;
426190507Slulf
427190507Slulf	flags = plexes = subdisks = volumes = 0;
428190507Slulf	drives = 1;
429190507Slulf	dname = NULL;
430190507Slulf
431204665Slulf	drivename = find_drive();
432190507Slulf	if (drivename == NULL)
433190507Slulf		return (NULL);
434190507Slulf
435190507Slulf	req = gctl_get_handle();
436190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
437190507Slulf	gctl_ro_param(req, "verb", -1, "create");
438190881Slulf	d = gv_alloc_drive();
439190507Slulf	if (d == NULL)
440190507Slulf		err(1, "unable to allocate for gv_drive object");
441190507Slulf
442190507Slulf	strlcpy(d->name, drivename, sizeof(d->name));
443204665Slulf	copy_device(d, device);
444190507Slulf	gctl_ro_param(req, "drive0", sizeof(*d), d);
445190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
446190507Slulf	gctl_ro_param(req, "drives", sizeof(int), &drives);
447190507Slulf	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
448190507Slulf	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
449190507Slulf	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
450190507Slulf	errstr = gctl_issue(req);
451190507Slulf	if (errstr != NULL) {
452190507Slulf		warnx("error creating drive: %s", errstr);
453190507Slulf		gctl_free(req);
454190507Slulf		return (NULL);
455190507Slulf	} else {
456190507Slulf		gctl_free(req);
457190507Slulf		/* XXX: This is needed because we have to make sure the drives
458190507Slulf		 * are created before we return. */
459190507Slulf		/* Loop until it's in the config. */
460190507Slulf		for (i = 0; i < 100000; i++) {
461190507Slulf			dname = find_name("gvinumdrive", GV_TYPE_DRIVE,
462190507Slulf			    GV_MAXDRIVENAME);
463190507Slulf			/* If we got a different name, quit. */
464190507Slulf			if (dname == NULL)
465190507Slulf				continue;
466190507Slulf			if (strcmp(dname, drivename)) {
467190507Slulf				free(dname);
468190507Slulf				return (drivename);
469190507Slulf			}
470190507Slulf			free(dname);
471190507Slulf			dname = NULL;
472190507Slulf			usleep(100000); /* Sleep for 0.1s */
473190507Slulf		}
474190507Slulf	}
475190507Slulf	gctl_free(req);
476190507Slulf	return (drivename);
477190507Slulf}
478190507Slulf
479190507Slulf/*
480190507Slulf * General routine for creating a volume. Mainly for use by concat, mirror,
481190507Slulf * raid5 and stripe commands.
482190507Slulf */
483190507Slulfvoid
484190507Slulfcreate_volume(int argc, char **argv, char *verb)
485190507Slulf{
486190507Slulf	struct gctl_req *req;
487190507Slulf	const char *errstr;
488190507Slulf	char buf[BUFSIZ], *drivename, *volname;
489190507Slulf	int drives, flags, i;
490190507Slulf	off_t stripesize;
491190507Slulf
492190507Slulf	flags = 0;
493190507Slulf	drives = 0;
494190507Slulf	volname = NULL;
495190507Slulf	stripesize = 262144;
496190507Slulf
497190507Slulf	/* XXX: Should we check for argument length? */
498190507Slulf
499190507Slulf	req = gctl_get_handle();
500190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
501190507Slulf
502190507Slulf	for (i = 1; i < argc; i++) {
503190507Slulf		if (!strcmp(argv[i], "-f")) {
504190507Slulf			flags |= GV_FLAG_F;
505190507Slulf		} else if (!strcmp(argv[i], "-n")) {
506190507Slulf			volname = argv[++i];
507190507Slulf		} else if (!strcmp(argv[i], "-v")) {
508190507Slulf			flags |= GV_FLAG_V;
509190507Slulf		} else if (!strcmp(argv[i], "-s")) {
510190507Slulf			flags |= GV_FLAG_S;
511190507Slulf			if (!strcmp(verb, "raid5"))
512190507Slulf				stripesize = gv_sizespec(argv[++i]);
513190507Slulf		} else {
514190507Slulf			/* Assume it's a drive. */
515190507Slulf			snprintf(buf, sizeof(buf), "drive%d", drives++);
516190507Slulf
517190507Slulf			/* First we create the drive. */
518190507Slulf			drivename = create_drive(argv[i]);
519190507Slulf			if (drivename == NULL)
520190507Slulf				goto bad;
521190507Slulf			/* Then we add it to the request. */
522190507Slulf			gctl_ro_param(req, buf, -1, drivename);
523190507Slulf		}
524190507Slulf	}
525190507Slulf
526190507Slulf	gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize);
527190507Slulf
528190507Slulf	/* Find a free volume name. */
529190507Slulf	if (volname == NULL)
530190507Slulf		volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME);
531190507Slulf
532190507Slulf	/* Then we send a request to actually create the volumes. */
533190507Slulf	gctl_ro_param(req, "verb", -1, verb);
534190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
535190507Slulf	gctl_ro_param(req, "drives", sizeof(int), &drives);
536190507Slulf	gctl_ro_param(req, "name", -1, volname);
537190507Slulf	errstr = gctl_issue(req);
538190507Slulf	if (errstr != NULL)
539190507Slulf		warnx("creating %s volume failed: %s", verb, errstr);
540190507Slulfbad:
541190507Slulf	gctl_free(req);
542190507Slulf}
543190507Slulf
544190507Slulf/* Parse a line of the config, return the word after <pattern>. */
545190507Slulfchar *
546190507Slulffind_pattern(char *line, char *pattern)
547190507Slulf{
548190507Slulf	char *ptr;
549190507Slulf
550190507Slulf	ptr = strsep(&line, " ");
551190507Slulf	while (ptr != NULL) {
552190507Slulf		if (!strcmp(ptr, pattern)) {
553190507Slulf			/* Return the next. */
554190507Slulf			ptr = strsep(&line, " ");
555190507Slulf			return (ptr);
556190507Slulf		}
557190507Slulf		ptr = strsep(&line, " ");
558190507Slulf	}
559190507Slulf	return (NULL);
560190507Slulf}
561190507Slulf
562227489Seadler/* Find a free name for an object given a prefix. */
563190507Slulfchar *
564190507Slulffind_name(const char *prefix, int type, int namelen)
565190507Slulf{
566190507Slulf	struct gctl_req *req;
567190507Slulf	char comment[1], buf[GV_CFG_LEN - 1], *name, *sname, *ptr;
568190507Slulf	const char *errstr;
569190507Slulf	int i, n, begin, len, conflict;
570190507Slulf	char line[1024];
571190507Slulf
572190507Slulf	comment[0] = '\0';
573190507Slulf
574190507Slulf	/* Find a name. Fetch out configuration first. */
575190507Slulf	req = gctl_get_handle();
576190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
577190507Slulf	gctl_ro_param(req, "verb", -1, "getconfig");
578190507Slulf	gctl_ro_param(req, "comment", -1, comment);
579190507Slulf	gctl_rw_param(req, "config", sizeof(buf), buf);
580190507Slulf	errstr = gctl_issue(req);
581190507Slulf	if (errstr != NULL) {
582190507Slulf		warnx("can't get configuration: %s", errstr);
583190507Slulf		return (NULL);
584190507Slulf	}
585190507Slulf	gctl_free(req);
586190507Slulf
587190507Slulf	begin = 0;
588190507Slulf	len = strlen(buf);
589190507Slulf	i = 0;
590190507Slulf	sname = malloc(namelen + 1);
591190507Slulf
592190507Slulf	/* XXX: Max object setting? */
593190507Slulf	for (n = 0; n < 10000; n++) {
594190507Slulf		snprintf(sname, namelen, "%s%d", prefix, n);
595190507Slulf		conflict = 0;
596190507Slulf		begin = 0;
597190507Slulf		/* Loop through the configuration line by line. */
598190507Slulf		for (i = 0; i < len; i++) {
599190507Slulf			if (buf[i] == '\n' || buf[i] == '\0') {
600190507Slulf				ptr = buf + begin;
601190507Slulf				strlcpy(line, ptr, (i - begin) + 1);
602190507Slulf				begin = i + 1;
603190507Slulf				switch (type) {
604190507Slulf				case GV_TYPE_DRIVE:
605190507Slulf					name = find_pattern(line, "drive");
606190507Slulf					break;
607190507Slulf				case GV_TYPE_VOL:
608190507Slulf					name = find_pattern(line, "volume");
609190507Slulf					break;
610190507Slulf				case GV_TYPE_PLEX:
611190507Slulf				case GV_TYPE_SD:
612190507Slulf					name = find_pattern(line, "name");
613190507Slulf					break;
614190507Slulf				default:
615190507Slulf					printf("Invalid type given\n");
616190507Slulf					continue;
617190507Slulf				}
618190507Slulf				if (name == NULL)
619190507Slulf					continue;
620190507Slulf				if (!strcmp(sname, name)) {
621190507Slulf					conflict = 1;
622190507Slulf					/* XXX: Could quit the loop earlier. */
623190507Slulf				}
624190507Slulf			}
625190507Slulf		}
626190507Slulf		if (!conflict)
627190507Slulf			return (sname);
628190507Slulf	}
629190507Slulf	free(sname);
630190507Slulf	return (NULL);
631190507Slulf}
632190507Slulf
633204665Slulfvoid
634204665Slulfcopy_device(struct gv_drive *d, const char *device)
635190882Slulf{
636190882Slulf	if (strncmp(device, "/dev/", 5) == 0)
637204665Slulf		strlcpy(d->device, (device + 5), sizeof(d->device));
638204665Slulf	else
639204665Slulf		strlcpy(d->device, device, sizeof(d->device));
640190882Slulf}
641190882Slulf
642190507Slulf/* Detach a plex or subdisk from its parent. */
643190507Slulfvoid
644190507Slulfgvinum_detach(int argc, char **argv)
645190507Slulf{
646190507Slulf	const char *errstr;
647190507Slulf	struct gctl_req *req;
648190507Slulf	int flags, i;
649190507Slulf
650209051Suqs	flags = 0;
651190507Slulf	optreset = 1;
652190507Slulf	optind = 1;
653190507Slulf	while ((i = getopt(argc, argv, "f")) != -1) {
654190507Slulf		switch(i) {
655190507Slulf		case 'f':
656190507Slulf			flags |= GV_FLAG_F;
657190507Slulf			break;
658190507Slulf		default:
659190507Slulf			warn("invalid flag: %c", i);
660190507Slulf			return;
661190507Slulf		}
662190507Slulf	}
663190507Slulf	argc -= optind;
664190507Slulf	argv += optind;
665190507Slulf	if (argc != 1) {
666190507Slulf		warnx("usage: detach [-f] <subdisk> | <plex>");
667190507Slulf		return;
668190507Slulf	}
669190507Slulf
670190507Slulf	req = gctl_get_handle();
671190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
672190507Slulf	gctl_ro_param(req, "verb", -1, "detach");
673190507Slulf	gctl_ro_param(req, "object", -1, argv[0]);
674190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
675190507Slulf
676190507Slulf	errstr = gctl_issue(req);
677190507Slulf	if (errstr != NULL)
678190507Slulf		warnx("detach failed: %s", errstr);
679190507Slulf	gctl_free(req);
680190507Slulf}
681190507Slulf
682190507Slulfvoid
683130391Slegvinum_help(void)
684130391Sle{
685130391Sle	printf("COMMANDS\n"
686152616Sle	    "checkparity [-f] plex\n"
687152616Sle	    "        Check the parity blocks of a RAID-5 plex.\n"
688190507Slulf	    "create [-f] description-file\n"
689152616Sle	    "        Create as per description-file or open editor.\n"
690190507Slulf	    "attach plex volume [rename]\n"
691190507Slulf	    "attach subdisk plex [offset] [rename]\n"
692190507Slulf	    "        Attach a plex to a volume, or a subdisk to a plex\n"
693190507Slulf	    "concat [-fv] [-n name] drives\n"
694190507Slulf	    "        Create a concatenated volume from the specified drives.\n"
695190507Slulf	    "detach [-f] [plex | subdisk]\n"
696190507Slulf	    "        Detach a plex or a subdisk from the volume or plex to\n"
697190507Slulf	    "        which it is attached.\n"
698190884Slulf	    "grow plex drive\n"
699190884Slulf	    "        Grow plex by creating a properly sized subdisk on drive\n"
700152616Sle	    "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
701130391Sle	    "        List information about specified objects.\n"
702152616Sle	    "ld [-r] [-v] [-V] [volume]\n"
703130391Sle	    "        List information about drives.\n"
704152616Sle	    "ls [-r] [-v] [-V] [subdisk]\n"
705130391Sle	    "        List information about subdisks.\n"
706152616Sle	    "lp [-r] [-v] [-V] [plex]\n"
707130391Sle	    "        List information about plexes.\n"
708152616Sle	    "lv [-r] [-v] [-V] [volume]\n"
709130391Sle	    "        List information about volumes.\n"
710190507Slulf	    "mirror [-fsv] [-n name] drives\n"
711190507Slulf	    "        Create a mirrored volume from the specified drives.\n"
712130391Sle	    "move | mv -f drive object ...\n"
713130391Sle	    "        Move the object(s) to the specified drive.\n"
714130391Sle	    "quit    Exit the vinum program when running in interactive mode."
715130391Sle	    "  Nor-\n"
716130391Sle	    "        mally this would be done by entering the EOF character.\n"
717190507Slulf	    "raid5 [-fv] [-s stripesize] [-n name] drives\n"
718190507Slulf	    "        Create a RAID-5 volume from the specified drives.\n"
719130391Sle	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
720130391Sle	    "        Change the name of the specified object.\n"
721152616Sle	    "rebuildparity plex [-f]\n"
722152616Sle	    "        Rebuild the parity blocks of a RAID-5 plex.\n"
723157052Sle	    "resetconfig\n"
724157052Sle	    "        Reset the complete gvinum configuration\n"
725190507Slulf	    "rm [-r] [-f] volume | plex | subdisk | drive\n"
726130391Sle	    "        Remove an object.\n"
727130391Sle	    "saveconfig\n"
728130391Sle	    "        Save vinum configuration to disk after configuration"
729130391Sle	    " failures.\n"
730152616Sle	    "setstate [-f] state [volume | plex | subdisk | drive]\n"
731130391Sle	    "        Set state without influencing other objects, for"
732130391Sle	    " diagnostic pur-\n"
733130391Sle	    "        poses only.\n"
734152616Sle	    "start [-S size] volume | plex | subdisk\n"
735130391Sle	    "        Allow the system to access the objects.\n"
736190507Slulf	    "stripe [-fv] [-n name] drives\n"
737190507Slulf	    "        Create a striped volume from the specified drives.\n"
738130391Sle	);
739130391Sle
740130391Sle	return;
741130391Sle}
742130391Sle
743130391Slevoid
744138112Slegvinum_setstate(int argc, char **argv)
745138112Sle{
746138112Sle	struct gctl_req *req;
747138112Sle	int flags, i;
748138112Sle	const char *errstr;
749138112Sle
750138112Sle	flags = 0;
751138112Sle
752138112Sle	optreset = 1;
753138112Sle	optind = 1;
754138112Sle
755138112Sle	while ((i = getopt(argc, argv, "f")) != -1) {
756138112Sle		switch (i) {
757138112Sle		case 'f':
758138112Sle			flags |= GV_FLAG_F;
759138112Sle			break;
760138112Sle		case '?':
761138112Sle		default:
762138112Sle			warn("invalid flag: %c", i);
763138112Sle			return;
764138112Sle		}
765138112Sle	}
766138112Sle
767138112Sle	argc -= optind;
768138112Sle	argv += optind;
769138112Sle
770138112Sle	if (argc != 2) {
771138112Sle		warnx("usage: setstate [-f] <state> <obj>");
772138112Sle		return;
773138112Sle	}
774138112Sle
775138112Sle	/*
776138112Sle	 * XXX: This hack is needed to avoid tripping over (now) invalid
777138112Sle	 * 'classic' vinum states and will go away later.
778138112Sle	 */
779138112Sle	if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
780138112Sle	    strcmp(argv[0], "stale")) {
781138112Sle		warnx("invalid state '%s'", argv[0]);
782138112Sle		return;
783138112Sle	}
784138112Sle
785138112Sle	req = gctl_get_handle();
786138112Sle	gctl_ro_param(req, "class", -1, "VINUM");
787138112Sle	gctl_ro_param(req, "verb", -1, "setstate");
788138112Sle	gctl_ro_param(req, "state", -1, argv[0]);
789138112Sle	gctl_ro_param(req, "object", -1, argv[1]);
790138112Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
791138112Sle
792138112Sle	errstr = gctl_issue(req);
793138112Sle	if (errstr != NULL)
794138112Sle		warnx("%s", errstr);
795138112Sle	gctl_free(req);
796138112Sle}
797138112Sle
798138112Slevoid
799130391Slegvinum_list(int argc, char **argv)
800130391Sle{
801130391Sle	struct gctl_req *req;
802130391Sle	int flags, i, j;
803130391Sle	const char *errstr;
804130391Sle	char buf[20], *cmd, config[GV_CFG_LEN + 1];
805130391Sle
806130391Sle	flags = 0;
807130391Sle	cmd = "list";
808130391Sle
809130391Sle	if (argc) {
810130391Sle		optreset = 1;
811130391Sle		optind = 1;
812130391Sle		cmd = argv[0];
813130391Sle		while ((j = getopt(argc, argv, "rsvV")) != -1) {
814130391Sle			switch (j) {
815130391Sle			case 'r':
816130391Sle				flags |= GV_FLAG_R;
817130391Sle				break;
818130391Sle			case 's':
819130391Sle				flags |= GV_FLAG_S;
820130391Sle				break;
821130391Sle			case 'v':
822130391Sle				flags |= GV_FLAG_V;
823130391Sle				break;
824130391Sle			case 'V':
825130391Sle				flags |= GV_FLAG_V;
826130391Sle				flags |= GV_FLAG_VV;
827130391Sle				break;
828130391Sle			case '?':
829130391Sle			default:
830130391Sle				return;
831130391Sle			}
832130391Sle		}
833130391Sle		argc -= optind;
834130391Sle		argv += optind;
835130391Sle
836130391Sle	}
837130391Sle
838130391Sle	req = gctl_get_handle();
839130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
840130391Sle	gctl_ro_param(req, "verb", -1, "list");
841130391Sle	gctl_ro_param(req, "cmd", -1, cmd);
842130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
843130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
844130391Sle	gctl_rw_param(req, "config", sizeof(config), config);
845130391Sle	if (argc) {
846130391Sle		for (i = 0; i < argc; i++) {
847130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
848130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
849130391Sle		}
850130391Sle	}
851130391Sle	errstr = gctl_issue(req);
852130391Sle	if (errstr != NULL) {
853130391Sle		warnx("can't get configuration: %s", errstr);
854130391Sle		gctl_free(req);
855130391Sle		return;
856130391Sle	}
857130391Sle
858130391Sle	printf("%s", config);
859130391Sle	gctl_free(req);
860130391Sle	return;
861130391Sle}
862130391Sle
863190507Slulf/* Create a mirrored volume. */
864190507Slulfvoid
865190507Slulfgvinum_mirror(int argc, char **argv)
866190507Slulf{
867190507Slulf
868190507Slulf	if (argc < 2) {
869190507Slulf		warnx("usage\tmirror [-fsv] [-n name] drives\n");
870190507Slulf		return;
871190507Slulf	}
872190507Slulf	create_volume(argc, argv, "mirror");
873190507Slulf}
874190507Slulf
875152616Sle/* Note that move is currently of form '[-r] target object [...]' */
876130391Slevoid
877152616Slegvinum_move(int argc, char **argv)
878152616Sle{
879152616Sle	struct gctl_req *req;
880152616Sle	const char *errstr;
881152616Sle	char buf[20];
882152616Sle	int flags, i, j;
883152616Sle
884152616Sle	flags = 0;
885152616Sle	if (argc) {
886152616Sle		optreset = 1;
887152616Sle		optind = 1;
888152616Sle		while ((j = getopt(argc, argv, "f")) != -1) {
889152616Sle			switch (j) {
890152616Sle			case 'f':
891152616Sle				flags |= GV_FLAG_F;
892152616Sle				break;
893152616Sle			case '?':
894152616Sle			default:
895152616Sle				return;
896152616Sle			}
897152616Sle		}
898152616Sle		argc -= optind;
899152616Sle		argv += optind;
900152616Sle	}
901152616Sle
902152616Sle	switch (argc) {
903152616Sle		case 0:
904152616Sle			warnx("no destination or object(s) to move specified");
905152616Sle			return;
906152616Sle		case 1:
907152616Sle			warnx("no object(s) to move specified");
908152616Sle			return;
909152616Sle		default:
910152616Sle			break;
911152616Sle	}
912152616Sle
913152616Sle	req = gctl_get_handle();
914152616Sle	gctl_ro_param(req, "class", -1, "VINUM");
915152616Sle	gctl_ro_param(req, "verb", -1, "move");
916152616Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
917152616Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
918152616Sle	gctl_ro_param(req, "destination", -1, argv[0]);
919152616Sle	for (i = 1; i < argc; i++) {
920152616Sle		snprintf(buf, sizeof(buf), "argv%d", i);
921152616Sle		gctl_ro_param(req, buf, -1, argv[i]);
922152616Sle	}
923152631Sle	errstr = gctl_issue(req);
924152616Sle	if (errstr != NULL)
925152616Sle		warnx("can't move object(s):  %s", errstr);
926152616Sle	gctl_free(req);
927152616Sle	return;
928152616Sle}
929152616Sle
930152616Slevoid
931130391Slegvinum_printconfig(int argc, char **argv)
932130391Sle{
933130391Sle	printconfig(stdout, "");
934130391Sle}
935130391Sle
936130391Slevoid
937138110Slegvinum_parityop(int argc, char **argv, int rebuild)
938138110Sle{
939138110Sle	struct gctl_req *req;
940190507Slulf	int flags, i;
941138110Sle	const char *errstr;
942229915Seadler	char *op;
943138110Sle
944138110Sle	if (rebuild) {
945138110Sle		op = "rebuildparity";
946138110Sle	} else {
947138110Sle		op = "checkparity";
948138110Sle	}
949138110Sle
950138110Sle	optreset = 1;
951138110Sle	optind = 1;
952138110Sle	flags = 0;
953138110Sle	while ((i = getopt(argc, argv, "fv")) != -1) {
954138110Sle		switch (i) {
955138110Sle		case 'f':
956138110Sle			flags |= GV_FLAG_F;
957138110Sle			break;
958138110Sle		case 'v':
959138110Sle			flags |= GV_FLAG_V;
960138110Sle			break;
961138110Sle		case '?':
962138110Sle		default:
963138110Sle			warnx("invalid flag '%c'", i);
964138110Sle			return;
965138110Sle		}
966138110Sle	}
967138110Sle	argc -= optind;
968138110Sle	argv += optind;
969138110Sle
970138110Sle	if (argc != 1) {
971138110Sle		warn("usage: %s [-f] [-v] <plex>", op);
972138110Sle		return;
973138110Sle	}
974138110Sle
975190507Slulf	req = gctl_get_handle();
976190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
977190507Slulf	gctl_ro_param(req, "verb", -1, op);
978190507Slulf	gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
979190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
980190507Slulf	gctl_ro_param(req, "plex", -1, argv[0]);
981138110Sle
982190507Slulf	errstr = gctl_issue(req);
983190507Slulf	if (errstr)
984190507Slulf		warnx("%s\n", errstr);
985190507Slulf	gctl_free(req);
986190507Slulf}
987138110Sle
988190507Slulf/* Create a RAID-5 volume. */
989190507Slulfvoid
990190507Slulfgvinum_raid5(int argc, char **argv)
991190507Slulf{
992190507Slulf
993190507Slulf	if (argc < 2) {
994190507Slulf		warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
995190507Slulf		return;
996138110Sle	}
997190507Slulf	create_volume(argc, argv, "raid5");
998138110Sle}
999138110Sle
1000190507Slulf
1001138110Slevoid
1002152616Slegvinum_rename(int argc, char **argv)
1003152616Sle{
1004152616Sle	struct gctl_req *req;
1005152616Sle	const char *errstr;
1006152616Sle	int flags, j;
1007152616Sle
1008152616Sle	flags = 0;
1009152616Sle
1010152616Sle	if (argc) {
1011152616Sle		optreset = 1;
1012152616Sle		optind = 1;
1013152616Sle		while ((j = getopt(argc, argv, "r")) != -1) {
1014152616Sle			switch (j) {
1015152616Sle			case 'r':
1016152616Sle				flags |= GV_FLAG_R;
1017152616Sle				break;
1018152616Sle			case '?':
1019152616Sle			default:
1020152616Sle				return;
1021152616Sle			}
1022152616Sle		}
1023152616Sle		argc -= optind;
1024152616Sle		argv += optind;
1025152616Sle	}
1026152616Sle
1027152616Sle	switch (argc) {
1028152616Sle		case 0:
1029152616Sle			warnx("no object to rename specified");
1030152616Sle			return;
1031152616Sle		case 1:
1032152616Sle			warnx("no new name specified");
1033152616Sle			return;
1034152616Sle		case 2:
1035152616Sle			break;
1036152616Sle		default:
1037152616Sle			warnx("more than one new name specified");
1038152616Sle			return;
1039152616Sle	}
1040152616Sle
1041152616Sle	req = gctl_get_handle();
1042152616Sle	gctl_ro_param(req, "class", -1, "VINUM");
1043152616Sle	gctl_ro_param(req, "verb", -1, "rename");
1044152616Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
1045152616Sle	gctl_ro_param(req, "object", -1, argv[0]);
1046152616Sle	gctl_ro_param(req, "newname", -1, argv[1]);
1047152631Sle	errstr = gctl_issue(req);
1048152616Sle	if (errstr != NULL)
1049152616Sle		warnx("can't rename object:  %s", errstr);
1050152616Sle	gctl_free(req);
1051152616Sle	return;
1052152616Sle}
1053152616Sle
1054152616Slevoid
1055130391Slegvinum_rm(int argc, char **argv)
1056130391Sle{
1057130391Sle	struct gctl_req *req;
1058130391Sle	int flags, i, j;
1059130391Sle	const char *errstr;
1060229915Seadler	char buf[20];
1061130391Sle
1062130391Sle	flags = 0;
1063130391Sle	optreset = 1;
1064130391Sle	optind = 1;
1065190507Slulf	while ((j = getopt(argc, argv, "rf")) != -1) {
1066130391Sle		switch (j) {
1067190507Slulf		case 'f':
1068190507Slulf			flags |= GV_FLAG_F;
1069190507Slulf			break;
1070130391Sle		case 'r':
1071130391Sle			flags |= GV_FLAG_R;
1072130391Sle			break;
1073130391Sle		case '?':
1074130391Sle		default:
1075130391Sle			return;
1076130391Sle		}
1077130391Sle	}
1078130391Sle	argc -= optind;
1079130391Sle	argv += optind;
1080130391Sle
1081130391Sle	req = gctl_get_handle();
1082130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
1083130391Sle	gctl_ro_param(req, "verb", -1, "remove");
1084130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
1085130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
1086130391Sle	if (argc) {
1087130391Sle		for (i = 0; i < argc; i++) {
1088130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
1089130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
1090130391Sle		}
1091130391Sle	}
1092130391Sle	errstr = gctl_issue(req);
1093130391Sle	if (errstr != NULL) {
1094130391Sle		warnx("can't remove: %s", errstr);
1095130391Sle		gctl_free(req);
1096130391Sle		return;
1097130391Sle	}
1098130391Sle	gctl_free(req);
1099130391Sle}
1100130391Sle
1101130391Slevoid
1102157052Slegvinum_resetconfig(void)
1103157052Sle{
1104157052Sle	struct gctl_req *req;
1105157052Sle	const char *errstr;
1106157052Sle	char reply[32];
1107157052Sle
1108157052Sle	if (!isatty(STDIN_FILENO)) {
1109157052Sle		warn("Please enter this command from a tty device\n");
1110157052Sle		return;
1111157052Sle	}
1112157052Sle	printf(" WARNING!  This command will completely wipe out your gvinum"
1113157052Sle	    "configuration.\n"
1114157052Sle	    " All data will be lost.  If you really want to do this,"
1115157052Sle	    " enter the text\n\n"
1116157052Sle	    " NO FUTURE\n"
1117157052Sle	    " Enter text -> ");
1118157052Sle	fgets(reply, sizeof(reply), stdin);
1119157052Sle	if (strcmp(reply, "NO FUTURE\n")) {
1120157052Sle		printf("\n No change\n");
1121157052Sle		return;
1122157052Sle	}
1123157052Sle	req = gctl_get_handle();
1124157052Sle	gctl_ro_param(req, "class", -1, "VINUM");
1125157052Sle	gctl_ro_param(req, "verb", -1, "resetconfig");
1126157052Sle	errstr = gctl_issue(req);
1127157052Sle	if (errstr != NULL) {
1128157052Sle		warnx("can't reset config: %s", errstr);
1129157052Sle		gctl_free(req);
1130157052Sle		return;
1131157052Sle	}
1132157052Sle	gctl_free(req);
1133157052Sle	printf("gvinum configuration obliterated\n");
1134157052Sle}
1135157052Sle
1136157052Slevoid
1137130391Slegvinum_saveconfig(void)
1138130391Sle{
1139130391Sle	struct gctl_req *req;
1140130391Sle	const char *errstr;
1141130391Sle
1142130391Sle	req = gctl_get_handle();
1143130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
1144130391Sle	gctl_ro_param(req, "verb", -1, "saveconfig");
1145130391Sle	errstr = gctl_issue(req);
1146130391Sle	if (errstr != NULL)
1147130391Sle		warnx("can't save configuration: %s", errstr);
1148130391Sle	gctl_free(req);
1149130391Sle}
1150130391Sle
1151130391Slevoid
1152130391Slegvinum_start(int argc, char **argv)
1153130391Sle{
1154130391Sle	struct gctl_req *req;
1155130391Sle	int i, initsize, j;
1156130391Sle	const char *errstr;
1157130391Sle	char buf[20];
1158130391Sle
1159130391Sle	/* 'start' with no arguments is a no-op. */
1160130391Sle	if (argc == 1)
1161130391Sle		return;
1162130391Sle
1163130391Sle	initsize = 0;
1164130391Sle
1165130391Sle	optreset = 1;
1166130391Sle	optind = 1;
1167130391Sle	while ((j = getopt(argc, argv, "S")) != -1) {
1168130391Sle		switch (j) {
1169130391Sle		case 'S':
1170130391Sle			initsize = atoi(optarg);
1171130391Sle			break;
1172130391Sle		case '?':
1173130391Sle		default:
1174130391Sle			return;
1175130391Sle		}
1176130391Sle	}
1177130391Sle	argc -= optind;
1178130391Sle	argv += optind;
1179130391Sle
1180130391Sle	if (!initsize)
1181130391Sle		initsize = 512;
1182130391Sle
1183130391Sle	req = gctl_get_handle();
1184130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
1185130391Sle	gctl_ro_param(req, "verb", -1, "start");
1186130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
1187130391Sle	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1188130391Sle	if (argc) {
1189130391Sle		for (i = 0; i < argc; i++) {
1190130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
1191130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
1192130391Sle		}
1193130391Sle	}
1194130391Sle	errstr = gctl_issue(req);
1195130391Sle	if (errstr != NULL) {
1196130391Sle		warnx("can't start: %s", errstr);
1197130391Sle		gctl_free(req);
1198130391Sle		return;
1199130391Sle	}
1200130391Sle
1201130391Sle	gctl_free(req);
1202130391Sle}
1203130391Sle
1204130391Slevoid
1205130391Slegvinum_stop(int argc, char **argv)
1206130391Sle{
1207190507Slulf	int err, fileid;
1208130391Sle
1209265536Smarius	fileid = kldfind(GVINUMKLD);
1210130391Sle	if (fileid == -1) {
1211265536Smarius		if (modfind(GVINUMMOD) < 0)
1212265536Smarius			warn("cannot find " GVINUMKLD);
1213130391Sle		return;
1214130391Sle	}
1215190507Slulf
1216190507Slulf	/*
1217190507Slulf	 * This little hack prevents that we end up in an infinite loop in
1218190507Slulf	 * g_unload_class().  gv_unload() will return EAGAIN so that the GEOM
1219190507Slulf	 * event thread will be free for the g_wither_geom() call from
1220190507Slulf	 * gv_unload().  It's silly, but it works.
1221190507Slulf	 */
1222265536Smarius	printf("unloading " GVINUMKLD " kernel module... ");
1223190507Slulf	fflush(stdout);
1224190507Slulf	if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1225190507Slulf		sleep(1);
1226190507Slulf		err = kldunload(fileid);
1227190507Slulf	}
1228190507Slulf	if (err != 0) {
1229190507Slulf		printf(" failed!\n");
1230265536Smarius		warn("cannot unload " GVINUMKLD);
1231130391Sle		return;
1232130391Sle	}
1233130391Sle
1234190507Slulf	printf("done\n");
1235130391Sle	exit(0);
1236130391Sle}
1237130391Sle
1238190507Slulf/* Create a striped volume. */
1239130391Slevoid
1240190507Slulfgvinum_stripe(int argc, char **argv)
1241190507Slulf{
1242190507Slulf
1243190507Slulf	if (argc < 2) {
1244190507Slulf		warnx("usage:\tstripe [-fv] [-n name] drives\n");
1245190507Slulf		return;
1246190507Slulf	}
1247190507Slulf	create_volume(argc, argv, "stripe");
1248190507Slulf}
1249190507Slulf
1250190884Slulf/* Grow a subdisk by adding disk backed by provider. */
1251190507Slulfvoid
1252190884Slulfgvinum_grow(int argc, char **argv)
1253190884Slulf{
1254190884Slulf	struct gctl_req *req;
1255190884Slulf	char *drive, *sdname;
1256190884Slulf	char sdprefix[GV_MAXSDNAME];
1257190884Slulf	struct gv_drive *d;
1258190884Slulf	struct gv_sd *s;
1259190884Slulf	const char *errstr;
1260190884Slulf	int drives, volumes, plexes, subdisks, flags;
1261190884Slulf
1262190884Slulf	drives = volumes = plexes = subdisks = 0;
1263190884Slulf	if (argc < 3) {
1264190884Slulf		warnx("usage:\tgrow plex drive\n");
1265190884Slulf		return;
1266190884Slulf	}
1267190884Slulf
1268190884Slulf	s = gv_alloc_sd();
1269190884Slulf	if (s == NULL) {
1270190884Slulf		warn("unable to create subdisk");
1271190884Slulf		return;
1272190884Slulf	}
1273190884Slulf	d = gv_alloc_drive();
1274190884Slulf	if (d == NULL) {
1275190884Slulf		warn("unable to create drive");
1276190884Slulf		free(s);
1277190884Slulf		return;
1278190884Slulf	}
1279190884Slulf	/* Lookup device and set an appropriate drive name. */
1280204665Slulf	drive = find_drive();
1281190884Slulf	if (drive == NULL) {
1282190884Slulf		warn("unable to find an appropriate drive name");
1283190884Slulf		free(s);
1284190884Slulf		free(d);
1285190884Slulf		return;
1286190884Slulf	}
1287190884Slulf	strlcpy(d->name, drive, sizeof(d->name));
1288204665Slulf	copy_device(d, argv[2]);
1289204665Slulf
1290190884Slulf	drives = 1;
1291190884Slulf
1292190884Slulf	/* We try to use the plex name as basis for the subdisk name. */
1293190884Slulf	snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]);
1294190884Slulf	sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME);
1295190884Slulf	if (sdname == NULL) {
1296190884Slulf		warn("unable to find an appropriate subdisk name");
1297190884Slulf		free(s);
1298190884Slulf		free(d);
1299190884Slulf		free(drive);
1300190884Slulf		return;
1301190884Slulf	}
1302190884Slulf	strlcpy(s->name, sdname, sizeof(s->name));
1303190884Slulf	free(sdname);
1304190884Slulf	strlcpy(s->plex, argv[1], sizeof(s->plex));
1305190884Slulf	strlcpy(s->drive, d->name, sizeof(s->drive));
1306190884Slulf	subdisks = 1;
1307190884Slulf
1308190884Slulf	req = gctl_get_handle();
1309190884Slulf	gctl_ro_param(req, "class", -1, "VINUM");
1310190884Slulf	gctl_ro_param(req, "verb", -1, "create");
1311190884Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
1312190884Slulf	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
1313190884Slulf	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
1314190884Slulf	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
1315190884Slulf	gctl_ro_param(req, "drives", sizeof(int), &drives);
1316190884Slulf	gctl_ro_param(req, "drive0", sizeof(*d), d);
1317190884Slulf	gctl_ro_param(req, "sd0", sizeof(*s), s);
1318190884Slulf	errstr = gctl_issue(req);
1319190884Slulf	free(drive);
1320190884Slulf	if (errstr != NULL) {
1321190884Slulf		warnx("unable to grow plex: %s", errstr);
1322190884Slulf		free(s);
1323190884Slulf		free(d);
1324190884Slulf		return;
1325190884Slulf	}
1326190884Slulf	gctl_free(req);
1327190884Slulf}
1328190884Slulf
1329190884Slulfvoid
1330130391Sleparseline(int argc, char **argv)
1331130391Sle{
1332130391Sle	if (argc <= 0)
1333130391Sle		return;
1334130391Sle
1335150044Sle	if (!strcmp(argv[0], "create"))
1336130391Sle		gvinum_create(argc, argv);
1337130391Sle	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
1338130391Sle		exit(0);
1339190507Slulf	else if (!strcmp(argv[0], "attach"))
1340190507Slulf		gvinum_attach(argc, argv);
1341190507Slulf	else if (!strcmp(argv[0], "detach"))
1342190507Slulf		gvinum_detach(argc, argv);
1343190507Slulf	else if (!strcmp(argv[0], "concat"))
1344190507Slulf		gvinum_concat(argc, argv);
1345190884Slulf	else if (!strcmp(argv[0], "grow"))
1346190884Slulf		gvinum_grow(argc, argv);
1347130391Sle	else if (!strcmp(argv[0], "help"))
1348130391Sle		gvinum_help();
1349130391Sle	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
1350130391Sle		gvinum_list(argc, argv);
1351130391Sle	else if (!strcmp(argv[0], "ld"))
1352130391Sle		gvinum_list(argc, argv);
1353130391Sle	else if (!strcmp(argv[0], "lp"))
1354130391Sle		gvinum_list(argc, argv);
1355130391Sle	else if (!strcmp(argv[0], "ls"))
1356130391Sle		gvinum_list(argc, argv);
1357130391Sle	else if (!strcmp(argv[0], "lv"))
1358130391Sle		gvinum_list(argc, argv);
1359190507Slulf	else if (!strcmp(argv[0], "mirror"))
1360190507Slulf		gvinum_mirror(argc, argv);
1361152616Sle	else if (!strcmp(argv[0], "move"))
1362152616Sle		gvinum_move(argc, argv);
1363152616Sle	else if (!strcmp(argv[0], "mv"))
1364152616Sle		gvinum_move(argc, argv);
1365130391Sle	else if (!strcmp(argv[0], "printconfig"))
1366130391Sle		gvinum_printconfig(argc, argv);
1367190507Slulf	else if (!strcmp(argv[0], "raid5"))
1368190507Slulf		gvinum_raid5(argc, argv);
1369152616Sle	else if (!strcmp(argv[0], "rename"))
1370152616Sle		gvinum_rename(argc, argv);
1371157052Sle	else if (!strcmp(argv[0], "resetconfig"))
1372157052Sle		gvinum_resetconfig();
1373130391Sle	else if (!strcmp(argv[0], "rm"))
1374130391Sle		gvinum_rm(argc, argv);
1375130391Sle	else if (!strcmp(argv[0], "saveconfig"))
1376130391Sle		gvinum_saveconfig();
1377138112Sle	else if (!strcmp(argv[0], "setstate"))
1378138112Sle		gvinum_setstate(argc, argv);
1379130391Sle	else if (!strcmp(argv[0], "start"))
1380130391Sle		gvinum_start(argc, argv);
1381130391Sle	else if (!strcmp(argv[0], "stop"))
1382130391Sle		gvinum_stop(argc, argv);
1383190507Slulf	else if (!strcmp(argv[0], "stripe"))
1384190507Slulf		gvinum_stripe(argc, argv);
1385138110Sle	else if (!strcmp(argv[0], "checkparity"))
1386138110Sle		gvinum_parityop(argc, argv, 0);
1387138110Sle	else if (!strcmp(argv[0], "rebuildparity"))
1388138110Sle		gvinum_parityop(argc, argv, 1);
1389130391Sle	else
1390130391Sle		printf("unknown command '%s'\n", argv[0]);
1391130391Sle
1392130391Sle	return;
1393130391Sle}
1394130391Sle
1395130391Sle/*
1396130391Sle * The guts of printconfig.  This is called from gvinum_printconfig and from
1397130391Sle * gvinum_create when called without an argument, in order to give the user
1398130391Sle * something to edit.
1399130391Sle */
1400130391Slevoid
1401130391Sleprintconfig(FILE *of, char *comment)
1402130391Sle{
1403130391Sle	struct gctl_req *req;
1404130391Sle	struct utsname uname_s;
1405130391Sle	const char *errstr;
1406130391Sle	time_t now;
1407130391Sle	char buf[GV_CFG_LEN + 1];
1408130391Sle
1409130391Sle	uname(&uname_s);
1410130391Sle	time(&now);
1411130391Sle
1412130391Sle	req = gctl_get_handle();
1413130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
1414130391Sle	gctl_ro_param(req, "verb", -1, "getconfig");
1415130391Sle	gctl_ro_param(req, "comment", -1, comment);
1416130391Sle	gctl_rw_param(req, "config", sizeof(buf), buf);
1417130391Sle	errstr = gctl_issue(req);
1418130391Sle	if (errstr != NULL) {
1419130391Sle		warnx("can't get configuration: %s", errstr);
1420130391Sle		return;
1421130391Sle	}
1422130391Sle	gctl_free(req);
1423130391Sle
1424130391Sle	fprintf(of, "# Vinum configuration of %s, saved at %s",
1425130391Sle	    uname_s.nodename,
1426130391Sle	    ctime(&now));
1427130391Sle
1428130391Sle	if (*comment != '\0')
1429130391Sle	    fprintf(of, "# Current configuration:\n");
1430130391Sle
1431215704Sbrucec	fprintf(of, "%s", buf);
1432130391Sle}
1433