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$
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
61266043Smariusstatic void gvinum_attach(int, char * const *);
62266043Smariusstatic void gvinum_concat(int, char * const *);
63266043Smariusstatic void gvinum_create(int, char * const *);
64266043Smariusstatic void gvinum_detach(int, char * const *);
65266043Smariusstatic void gvinum_grow(int, char * const *);
66266043Smariusstatic void gvinum_help(void);
67266043Smariusstatic void gvinum_list(int, char * const *);
68266043Smariusstatic void gvinum_move(int, char * const *);
69266043Smariusstatic void gvinum_mirror(int, char * const *);
70266043Smariusstatic void gvinum_parityop(int, char * const * , int);
71266043Smariusstatic void gvinum_printconfig(int, char * const *);
72266043Smariusstatic void gvinum_raid5(int, char * const *);
73266043Smariusstatic void gvinum_rename(int, char * const *);
74266043Smariusstatic void gvinum_resetconfig(int, char * const *);
75266043Smariusstatic void gvinum_rm(int, char * const *);
76266043Smariusstatic void gvinum_saveconfig(void);
77266043Smariusstatic void gvinum_setstate(int, char * const *);
78266043Smariusstatic void gvinum_start(int, char * const *);
79266043Smariusstatic void gvinum_stop(int, char * const *);
80266043Smariusstatic void gvinum_stripe(int, char * const *);
81266043Smariusstatic void parseline(int, char * const *);
82266043Smariusstatic void printconfig(FILE *, const char *);
83130391Sle
84266043Smariusstatic char *create_drive(const char *);
85266043Smariusstatic void create_volume(int, char * const * , const char *);
86266043Smariusstatic char *find_name(const char *, int, int);
87266043Smariusstatic const char *find_pattern(char *, const char *);
88266043Smariusstatic void copy_device(struct gv_drive *, const char *);
89266043Smarius#define	find_drive()							\
90266043Smarius    find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME)
91190507Slulf
92130391Sleint
93130391Slemain(int argc, char **argv)
94130391Sle{
95130391Sle	int line, tokens;
96130391Sle	char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
97130391Sle
98130391Sle	/* Load the module if necessary. */
99265536Smarius	if (modfind(GVINUMMOD) < 0) {
100265536Smarius		if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0)
101265536Smarius			err(1, GVINUMKLD ": Kernel module not available");
102265536Smarius	}
103130391Sle
104130391Sle	/* Arguments given on the command line. */
105130391Sle	if (argc > 1) {
106130391Sle		argc--;
107130391Sle		argv++;
108130391Sle		parseline(argc, argv);
109130391Sle
110130391Sle	/* Interactive mode. */
111130391Sle	} else {
112130391Sle		for (;;) {
113130391Sle			inputline = readline("gvinum -> ");
114130391Sle			if (inputline == NULL) {
115130391Sle				if (ferror(stdin)) {
116130391Sle					err(1, "can't read input");
117130391Sle				} else {
118130391Sle					printf("\n");
119130391Sle					exit(0);
120130391Sle				}
121130391Sle			} else if (*inputline) {
122130391Sle				add_history(inputline);
123130391Sle				strcpy(buffer, inputline);
124130391Sle				free(inputline);
125130391Sle				line++;		    /* count the lines */
126130391Sle				tokens = gv_tokenize(buffer, token, GV_MAXARGS);
127130391Sle				if (tokens)
128130391Sle					parseline(tokens, token);
129130391Sle			}
130130391Sle		}
131130391Sle	}
132130391Sle	exit(0);
133130391Sle}
134130391Sle
135190507Slulf/* Attach a plex to a volume or a subdisk to a plex. */
136266043Smariusstatic void
137266043Smariusgvinum_attach(int argc, char * const *argv)
138190507Slulf{
139190507Slulf	struct gctl_req *req;
140190507Slulf	const char *errstr;
141190507Slulf	int rename;
142190507Slulf	off_t offset;
143190507Slulf
144190507Slulf	rename = 0;
145190507Slulf	offset = -1;
146190507Slulf	if (argc < 3) {
147190507Slulf		warnx("usage:\tattach <subdisk> <plex> [rename] "
148190507Slulf		    "[<plexoffset>]\n"
149190507Slulf		    "\tattach <plex> <volume> [rename]");
150190507Slulf		return;
151190507Slulf	}
152190507Slulf	if (argc > 3) {
153190507Slulf		if (!strcmp(argv[3], "rename")) {
154190507Slulf			rename = 1;
155190507Slulf			if (argc == 5)
156190507Slulf				offset = strtol(argv[4], NULL, 0);
157190507Slulf		} else
158190507Slulf			offset = strtol(argv[3], NULL, 0);
159190507Slulf	}
160190507Slulf	req = gctl_get_handle();
161190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
162190507Slulf	gctl_ro_param(req, "verb", -1, "attach");
163190507Slulf	gctl_ro_param(req, "child", -1, argv[1]);
164190507Slulf	gctl_ro_param(req, "parent", -1, argv[2]);
165190507Slulf	gctl_ro_param(req, "offset", sizeof(off_t), &offset);
166190507Slulf	gctl_ro_param(req, "rename", sizeof(int), &rename);
167190507Slulf	errstr = gctl_issue(req);
168190507Slulf	if (errstr != NULL)
169190507Slulf		warnx("attach failed: %s", errstr);
170190507Slulf	gctl_free(req);
171190507Slulf}
172190507Slulf
173266043Smariusstatic void
174266043Smariusgvinum_create(int argc, char * const *argv)
175130391Sle{
176130391Sle	struct gctl_req *req;
177130391Sle	struct gv_drive *d;
178130391Sle	struct gv_plex *p;
179130391Sle	struct gv_sd *s;
180130391Sle	struct gv_volume *v;
181130391Sle	FILE *tmp;
182190507Slulf	int drives, errors, fd, flags, i, line, plexes, plex_in_volume;
183190507Slulf	int sd_in_plex, status, subdisks, tokens, undeffd, volumes;
184130391Sle	const char *errstr;
185190507Slulf	char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname;
186130391Sle	char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
187130391Sle	char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
188130391Sle
189190507Slulf	tmp = NULL;
190190507Slulf	flags = 0;
191190507Slulf	for (i = 1; i < argc; i++) {
192190507Slulf		/* Force flag used to ignore already created drives. */
193190507Slulf		if (!strcmp(argv[i], "-f")) {
194190507Slulf			flags |= GV_FLAG_F;
195190507Slulf		/* Else it must be a file. */
196190507Slulf		} else {
197266014Smarius			if ((tmp = fopen(argv[i], "r")) == NULL) {
198266014Smarius				warn("can't open '%s' for reading", argv[i]);
199190507Slulf				return;
200190507Slulf			}
201190507Slulf		}
202190507Slulf	}
203190507Slulf
204190507Slulf	/* We didn't get a file. */
205190507Slulf	if (tmp == NULL) {
206133097Sle		snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
207133097Sle
208133097Sle		if ((fd = mkstemp(tmpfile)) == -1) {
209133097Sle			warn("temporary file not accessible");
210133097Sle			return;
211133097Sle		}
212133097Sle		if ((tmp = fdopen(fd, "w")) == NULL) {
213133097Sle			warn("can't open '%s' for writing", tmpfile);
214133097Sle			return;
215133097Sle		}
216133097Sle		printconfig(tmp, "# ");
217133097Sle		fclose(tmp);
218133097Sle
219133097Sle		ed = getenv("EDITOR");
220133097Sle		if (ed == NULL)
221133097Sle			ed = _PATH_VI;
222133097Sle
223133097Sle		snprintf(commandline, sizeof(commandline), "%s %s", ed,
224133097Sle		    tmpfile);
225133097Sle		status = system(commandline);
226133097Sle		if (status != 0) {
227133097Sle			warn("couldn't exec %s; status: %d", ed, status);
228133097Sle			return;
229133097Sle		}
230133097Sle
231133097Sle		if ((tmp = fopen(tmpfile, "r")) == NULL) {
232133097Sle			warn("can't open '%s' for reading", tmpfile);
233133097Sle			return;
234133097Sle		}
235130391Sle	}
236130391Sle
237130391Sle	req = gctl_get_handle();
238130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
239130391Sle	gctl_ro_param(req, "verb", -1, "create");
240190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
241130391Sle
242130391Sle	drives = volumes = plexes = subdisks = 0;
243190507Slulf	plex_in_volume = sd_in_plex = undeffd = 0;
244190507Slulf	plex[0] = '\0';
245130391Sle	errors = 0;
246130391Sle	line = 1;
247130391Sle	while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
248130391Sle
249130391Sle		/* Skip empty lines and comments. */
250130391Sle		if (*buf == '\0' || *buf == '#') {
251130391Sle			line++;
252130391Sle			continue;
253130391Sle		}
254130391Sle
255130391Sle		/* Kill off the newline. */
256130391Sle		buf[strlen(buf) - 1] = '\0';
257130391Sle
258130391Sle		/*
259130391Sle		 * Copy the original input line in case we need it for error
260130391Sle		 * output.
261130391Sle		 */
262190507Slulf		strlcpy(original, buf, sizeof(original));
263130391Sle
264130391Sle		tokens = gv_tokenize(buf, token, GV_MAXARGS);
265150044Sle		if (tokens <= 0) {
266150044Sle			line++;
267150044Sle			continue;
268150044Sle		}
269130391Sle
270150044Sle		/* Volume definition. */
271150044Sle		if (!strcmp(token[0], "volume")) {
272150044Sle			v = gv_new_volume(tokens, token);
273150044Sle			if (v == NULL) {
274150044Sle				warnx("line %d: invalid volume definition",
275150044Sle				    line);
276150044Sle				warnx("line %d: '%s'", line, original);
277150044Sle				errors++;
278150044Sle				line++;
279150044Sle				continue;
280150044Sle			}
281130391Sle
282150044Sle			/* Reset plex count for this volume. */
283150044Sle			plex_in_volume = 0;
284130391Sle
285150044Sle			/*
286150044Sle			 * Set default volume name for following plex
287150044Sle			 * definitions.
288150044Sle			 */
289190507Slulf			strlcpy(volume, v->name, sizeof(volume));
290130391Sle
291150044Sle			snprintf(buf1, sizeof(buf1), "volume%d", volumes);
292150044Sle			gctl_ro_param(req, buf1, sizeof(*v), v);
293150044Sle			volumes++;
294130391Sle
295150044Sle		/* Plex definition. */
296150044Sle		} else if (!strcmp(token[0], "plex")) {
297150044Sle			p = gv_new_plex(tokens, token);
298150044Sle			if (p == NULL) {
299150044Sle				warnx("line %d: invalid plex definition", line);
300150044Sle				warnx("line %d: '%s'", line, original);
301150044Sle				errors++;
302150044Sle				line++;
303150044Sle				continue;
304150044Sle			}
305130391Sle
306150044Sle			/* Reset subdisk count for this plex. */
307150044Sle			sd_in_plex = 0;
308130391Sle
309150044Sle			/* Default name. */
310150044Sle			if (strlen(p->name) == 0) {
311190507Slulf				snprintf(p->name, sizeof(p->name), "%s.p%d",
312150044Sle				    volume, plex_in_volume++);
313150044Sle			}
314130391Sle
315150044Sle			/* Default volume. */
316150044Sle			if (strlen(p->volume) == 0) {
317190507Slulf				snprintf(p->volume, sizeof(p->volume), "%s",
318150044Sle				    volume);
319150044Sle			}
320130391Sle
321150044Sle			/*
322150044Sle			 * Set default plex name for following subdisk
323150044Sle			 * definitions.
324150044Sle			 */
325190507Slulf			strlcpy(plex, p->name, sizeof(plex));
326130391Sle
327150044Sle			snprintf(buf1, sizeof(buf1), "plex%d", plexes);
328150044Sle			gctl_ro_param(req, buf1, sizeof(*p), p);
329150044Sle			plexes++;
330130391Sle
331150044Sle		/* Subdisk definition. */
332150044Sle		} else if (!strcmp(token[0], "sd")) {
333150044Sle			s = gv_new_sd(tokens, token);
334150044Sle			if (s == NULL) {
335150044Sle				warnx("line %d: invalid subdisk "
336150044Sle				    "definition:", line);
337150044Sle				warnx("line %d: '%s'", line, original);
338150044Sle				errors++;
339150044Sle				line++;
340150044Sle				continue;
341150044Sle			}
342130391Sle
343150044Sle			/* Default name. */
344150044Sle			if (strlen(s->name) == 0) {
345190507Slulf				if (strlen(plex) == 0) {
346190507Slulf					sdname = find_name("gvinumsubdisk.p",
347190507Slulf					    GV_TYPE_SD, GV_MAXSDNAME);
348190507Slulf					snprintf(s->name, sizeof(s->name),
349190507Slulf					    "%s.s%d", sdname, undeffd++);
350190507Slulf					free(sdname);
351190507Slulf				} else {
352190507Slulf					snprintf(s->name, sizeof(s->name),
353190507Slulf					    "%s.s%d",plex, sd_in_plex++);
354190507Slulf				}
355150044Sle			}
356150044Sle
357150044Sle			/* Default plex. */
358150044Sle			if (strlen(s->plex) == 0)
359190507Slulf				snprintf(s->plex, sizeof(s->plex), "%s", plex);
360150044Sle
361150044Sle			snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
362150044Sle			gctl_ro_param(req, buf1, sizeof(*s), s);
363150044Sle			subdisks++;
364150044Sle
365150044Sle		/* Subdisk definition. */
366150044Sle		} else if (!strcmp(token[0], "drive")) {
367150044Sle			d = gv_new_drive(tokens, token);
368150044Sle			if (d == NULL) {
369150044Sle				warnx("line %d: invalid drive definition:",
370150044Sle				    line);
371130391Sle				warnx("line %d: '%s'", line, original);
372130391Sle				errors++;
373150044Sle				line++;
374150044Sle				continue;
375130391Sle			}
376150044Sle
377150044Sle			snprintf(buf1, sizeof(buf1), "drive%d", drives);
378150044Sle			gctl_ro_param(req, buf1, sizeof(*d), d);
379150044Sle			drives++;
380150044Sle
381150044Sle		/* Everything else is bogus. */
382150044Sle		} else {
383150044Sle			warnx("line %d: invalid definition:", line);
384150044Sle			warnx("line %d: '%s'", line, original);
385150044Sle			errors++;
386130391Sle		}
387130391Sle		line++;
388130391Sle	}
389130391Sle
390130391Sle	fclose(tmp);
391130391Sle	unlink(tmpfile);
392130391Sle
393130391Sle	if (!errors && (volumes || plexes || subdisks || drives)) {
394130391Sle		gctl_ro_param(req, "volumes", sizeof(int), &volumes);
395130391Sle		gctl_ro_param(req, "plexes", sizeof(int), &plexes);
396130391Sle		gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
397130391Sle		gctl_ro_param(req, "drives", sizeof(int), &drives);
398130391Sle		errstr = gctl_issue(req);
399130391Sle		if (errstr != NULL)
400130391Sle			warnx("create failed: %s", errstr);
401130391Sle	}
402130391Sle	gctl_free(req);
403130391Sle}
404130391Sle
405190507Slulf/* Create a concatenated volume. */
406266043Smariusstatic void
407266043Smariusgvinum_concat(int argc, char * const *argv)
408190507Slulf{
409190507Slulf
410190507Slulf	if (argc < 2) {
411190507Slulf		warnx("usage:\tconcat [-fv] [-n name] drives\n");
412190507Slulf		return;
413190507Slulf	}
414190507Slulf	create_volume(argc, argv, "concat");
415190507Slulf}
416190507Slulf
417190507Slulf/* Create a drive quick and dirty. */
418266043Smariusstatic char *
419266043Smariuscreate_drive(const 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;
426266042Smarius	int found = 0;
427190507Slulf
428190507Slulf	flags = plexes = subdisks = volumes = 0;
429190507Slulf	drives = 1;
430190507Slulf	dname = NULL;
431190507Slulf
432204665Slulf	drivename = find_drive();
433190507Slulf	if (drivename == NULL)
434190507Slulf		return (NULL);
435190507Slulf
436190507Slulf	req = gctl_get_handle();
437190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
438190507Slulf	gctl_ro_param(req, "verb", -1, "create");
439190881Slulf	d = gv_alloc_drive();
440190507Slulf	if (d == NULL)
441190507Slulf		err(1, "unable to allocate for gv_drive object");
442190507Slulf
443190507Slulf	strlcpy(d->name, drivename, sizeof(d->name));
444204665Slulf	copy_device(d, device);
445190507Slulf	gctl_ro_param(req, "drive0", sizeof(*d), d);
446190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
447190507Slulf	gctl_ro_param(req, "drives", sizeof(int), &drives);
448190507Slulf	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
449190507Slulf	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
450190507Slulf	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
451190507Slulf	errstr = gctl_issue(req);
452190507Slulf	if (errstr != NULL) {
453190507Slulf		warnx("error creating drive: %s", errstr);
454266042Smarius		drivename = NULL;
455190507Slulf	} else {
456190507Slulf		/* XXX: This is needed because we have to make sure the drives
457190507Slulf		 * are created before we return. */
458190507Slulf		/* Loop until it's in the config. */
459190507Slulf		for (i = 0; i < 100000; i++) {
460190507Slulf			dname = find_name("gvinumdrive", GV_TYPE_DRIVE,
461190507Slulf			    GV_MAXDRIVENAME);
462190507Slulf			/* If we got a different name, quit. */
463190507Slulf			if (dname == NULL)
464190507Slulf				continue;
465266042Smarius			if (strcmp(dname, drivename))
466266042Smarius				found = 1;
467190507Slulf			free(dname);
468190507Slulf			dname = NULL;
469266042Smarius			if (found)
470266042Smarius				break;
471190507Slulf			usleep(100000); /* Sleep for 0.1s */
472190507Slulf		}
473266042Smarius		if (found == 0) {
474266042Smarius			warnx("error creating drive");
475266042Smarius			drivename = NULL;
476266042Smarius		}
477190507Slulf	}
478190507Slulf	gctl_free(req);
479190507Slulf	return (drivename);
480190507Slulf}
481190507Slulf
482266043Smarius/*
483190507Slulf * General routine for creating a volume. Mainly for use by concat, mirror,
484190507Slulf * raid5 and stripe commands.
485190507Slulf */
486266043Smariusstatic void
487266043Smariuscreate_volume(int argc, char * const *argv, const char *verb)
488190507Slulf{
489190507Slulf	struct gctl_req *req;
490190507Slulf	const char *errstr;
491190507Slulf	char buf[BUFSIZ], *drivename, *volname;
492190507Slulf	int drives, flags, i;
493190507Slulf	off_t stripesize;
494190507Slulf
495190507Slulf	flags = 0;
496190507Slulf	drives = 0;
497190507Slulf	volname = NULL;
498190507Slulf	stripesize = 262144;
499190507Slulf
500190507Slulf	/* XXX: Should we check for argument length? */
501190507Slulf
502190507Slulf	req = gctl_get_handle();
503190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
504190507Slulf
505190507Slulf	for (i = 1; i < argc; i++) {
506190507Slulf		if (!strcmp(argv[i], "-f")) {
507190507Slulf			flags |= GV_FLAG_F;
508190507Slulf		} else if (!strcmp(argv[i], "-n")) {
509190507Slulf			volname = argv[++i];
510190507Slulf		} else if (!strcmp(argv[i], "-v")) {
511190507Slulf			flags |= GV_FLAG_V;
512190507Slulf		} else if (!strcmp(argv[i], "-s")) {
513190507Slulf			flags |= GV_FLAG_S;
514190507Slulf			if (!strcmp(verb, "raid5"))
515190507Slulf				stripesize = gv_sizespec(argv[++i]);
516190507Slulf		} else {
517190507Slulf			/* Assume it's a drive. */
518190507Slulf			snprintf(buf, sizeof(buf), "drive%d", drives++);
519190507Slulf
520190507Slulf			/* First we create the drive. */
521266043Smarius			drivename = create_drive(argv[i]);
522190507Slulf			if (drivename == NULL)
523190507Slulf				goto bad;
524190507Slulf			/* Then we add it to the request. */
525190507Slulf			gctl_ro_param(req, buf, -1, drivename);
526190507Slulf		}
527190507Slulf	}
528190507Slulf
529190507Slulf	gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize);
530190507Slulf
531190507Slulf	/* Find a free volume name. */
532190507Slulf	if (volname == NULL)
533190507Slulf		volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME);
534190507Slulf
535190507Slulf	/* Then we send a request to actually create the volumes. */
536190507Slulf	gctl_ro_param(req, "verb", -1, verb);
537266043Smarius	gctl_ro_param(req, "flags", sizeof(int), &flags);
538190507Slulf	gctl_ro_param(req, "drives", sizeof(int), &drives);
539190507Slulf	gctl_ro_param(req, "name", -1, volname);
540190507Slulf	errstr = gctl_issue(req);
541190507Slulf	if (errstr != NULL)
542190507Slulf		warnx("creating %s volume failed: %s", verb, errstr);
543190507Slulfbad:
544190507Slulf	gctl_free(req);
545190507Slulf}
546190507Slulf
547190507Slulf/* Parse a line of the config, return the word after <pattern>. */
548266043Smariusstatic const char *
549266043Smariusfind_pattern(char *line, const char *pattern)
550190507Slulf{
551190507Slulf	char *ptr;
552190507Slulf
553190507Slulf	ptr = strsep(&line, " ");
554190507Slulf	while (ptr != NULL) {
555190507Slulf		if (!strcmp(ptr, pattern)) {
556190507Slulf			/* Return the next. */
557190507Slulf			ptr = strsep(&line, " ");
558190507Slulf			return (ptr);
559190507Slulf		}
560190507Slulf		ptr = strsep(&line, " ");
561190507Slulf	}
562190507Slulf	return (NULL);
563190507Slulf}
564190507Slulf
565227489Seadler/* Find a free name for an object given a prefix. */
566266043Smariusstatic char *
567190507Slulffind_name(const char *prefix, int type, int namelen)
568190507Slulf{
569190507Slulf	struct gctl_req *req;
570266043Smarius	char comment[1], buf[GV_CFG_LEN - 1], *sname, *ptr;
571266043Smarius	const char *errstr, *name;
572190507Slulf	int i, n, begin, len, conflict;
573190507Slulf	char line[1024];
574190507Slulf
575190507Slulf	comment[0] = '\0';
576190507Slulf
577190507Slulf	/* Find a name. Fetch out configuration first. */
578190507Slulf	req = gctl_get_handle();
579190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
580190507Slulf	gctl_ro_param(req, "verb", -1, "getconfig");
581190507Slulf	gctl_ro_param(req, "comment", -1, comment);
582190507Slulf	gctl_rw_param(req, "config", sizeof(buf), buf);
583190507Slulf	errstr = gctl_issue(req);
584190507Slulf	if (errstr != NULL) {
585190507Slulf		warnx("can't get configuration: %s", errstr);
586190507Slulf		return (NULL);
587190507Slulf	}
588190507Slulf	gctl_free(req);
589190507Slulf
590190507Slulf	begin = 0;
591190507Slulf	len = strlen(buf);
592190507Slulf	i = 0;
593190507Slulf	sname = malloc(namelen + 1);
594190507Slulf
595190507Slulf	/* XXX: Max object setting? */
596190507Slulf	for (n = 0; n < 10000; n++) {
597190507Slulf		snprintf(sname, namelen, "%s%d", prefix, n);
598190507Slulf		conflict = 0;
599190507Slulf		begin = 0;
600190507Slulf		/* Loop through the configuration line by line. */
601190507Slulf		for (i = 0; i < len; i++) {
602190507Slulf			if (buf[i] == '\n' || buf[i] == '\0') {
603190507Slulf				ptr = buf + begin;
604190507Slulf				strlcpy(line, ptr, (i - begin) + 1);
605190507Slulf				begin = i + 1;
606190507Slulf				switch (type) {
607190507Slulf				case GV_TYPE_DRIVE:
608190507Slulf					name = find_pattern(line, "drive");
609190507Slulf					break;
610190507Slulf				case GV_TYPE_VOL:
611190507Slulf					name = find_pattern(line, "volume");
612190507Slulf					break;
613190507Slulf				case GV_TYPE_PLEX:
614190507Slulf				case GV_TYPE_SD:
615190507Slulf					name = find_pattern(line, "name");
616190507Slulf					break;
617190507Slulf				default:
618190507Slulf					printf("Invalid type given\n");
619190507Slulf					continue;
620190507Slulf				}
621190507Slulf				if (name == NULL)
622190507Slulf					continue;
623190507Slulf				if (!strcmp(sname, name)) {
624190507Slulf					conflict = 1;
625190507Slulf					/* XXX: Could quit the loop earlier. */
626190507Slulf				}
627190507Slulf			}
628190507Slulf		}
629190507Slulf		if (!conflict)
630190507Slulf			return (sname);
631190507Slulf	}
632190507Slulf	free(sname);
633190507Slulf	return (NULL);
634190507Slulf}
635190507Slulf
636266043Smariusstatic void
637204665Slulfcopy_device(struct gv_drive *d, const char *device)
638190882Slulf{
639266043Smarius
640190882Slulf	if (strncmp(device, "/dev/", 5) == 0)
641204665Slulf		strlcpy(d->device, (device + 5), sizeof(d->device));
642204665Slulf	else
643204665Slulf		strlcpy(d->device, device, sizeof(d->device));
644190882Slulf}
645190882Slulf
646190507Slulf/* Detach a plex or subdisk from its parent. */
647266043Smariusstatic void
648266043Smariusgvinum_detach(int argc, char * const *argv)
649190507Slulf{
650190507Slulf	const char *errstr;
651190507Slulf	struct gctl_req *req;
652190507Slulf	int flags, i;
653190507Slulf
654209051Suqs	flags = 0;
655190507Slulf	optreset = 1;
656190507Slulf	optind = 1;
657190507Slulf	while ((i = getopt(argc, argv, "f")) != -1) {
658266043Smarius		switch (i) {
659190507Slulf		case 'f':
660190507Slulf			flags |= GV_FLAG_F;
661190507Slulf			break;
662190507Slulf		default:
663190507Slulf			warn("invalid flag: %c", i);
664190507Slulf			return;
665190507Slulf		}
666190507Slulf	}
667190507Slulf	argc -= optind;
668190507Slulf	argv += optind;
669190507Slulf	if (argc != 1) {
670190507Slulf		warnx("usage: detach [-f] <subdisk> | <plex>");
671190507Slulf		return;
672190507Slulf	}
673190507Slulf
674190507Slulf	req = gctl_get_handle();
675190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
676190507Slulf	gctl_ro_param(req, "verb", -1, "detach");
677190507Slulf	gctl_ro_param(req, "object", -1, argv[0]);
678190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
679190507Slulf
680190507Slulf	errstr = gctl_issue(req);
681190507Slulf	if (errstr != NULL)
682190507Slulf		warnx("detach failed: %s", errstr);
683190507Slulf	gctl_free(req);
684190507Slulf}
685190507Slulf
686266043Smariusstatic void
687130391Slegvinum_help(void)
688130391Sle{
689266043Smarius
690130391Sle	printf("COMMANDS\n"
691152616Sle	    "checkparity [-f] plex\n"
692152616Sle	    "        Check the parity blocks of a RAID-5 plex.\n"
693190507Slulf	    "create [-f] description-file\n"
694152616Sle	    "        Create as per description-file or open editor.\n"
695190507Slulf	    "attach plex volume [rename]\n"
696190507Slulf	    "attach subdisk plex [offset] [rename]\n"
697190507Slulf	    "        Attach a plex to a volume, or a subdisk to a plex\n"
698190507Slulf	    "concat [-fv] [-n name] drives\n"
699190507Slulf	    "        Create a concatenated volume from the specified drives.\n"
700190507Slulf	    "detach [-f] [plex | subdisk]\n"
701190507Slulf	    "        Detach a plex or a subdisk from the volume or plex to\n"
702190507Slulf	    "        which it is attached.\n"
703190884Slulf	    "grow plex drive\n"
704190884Slulf	    "        Grow plex by creating a properly sized subdisk on drive\n"
705152616Sle	    "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
706130391Sle	    "        List information about specified objects.\n"
707152616Sle	    "ld [-r] [-v] [-V] [volume]\n"
708130391Sle	    "        List information about drives.\n"
709152616Sle	    "ls [-r] [-v] [-V] [subdisk]\n"
710130391Sle	    "        List information about subdisks.\n"
711152616Sle	    "lp [-r] [-v] [-V] [plex]\n"
712130391Sle	    "        List information about plexes.\n"
713152616Sle	    "lv [-r] [-v] [-V] [volume]\n"
714130391Sle	    "        List information about volumes.\n"
715190507Slulf	    "mirror [-fsv] [-n name] drives\n"
716190507Slulf	    "        Create a mirrored volume from the specified drives.\n"
717130391Sle	    "move | mv -f drive object ...\n"
718130391Sle	    "        Move the object(s) to the specified drive.\n"
719130391Sle	    "quit    Exit the vinum program when running in interactive mode."
720130391Sle	    "  Nor-\n"
721130391Sle	    "        mally this would be done by entering the EOF character.\n"
722190507Slulf	    "raid5 [-fv] [-s stripesize] [-n name] drives\n"
723190507Slulf	    "        Create a RAID-5 volume from the specified drives.\n"
724130391Sle	    "rename [-r] [drive | subdisk | plex | volume] newname\n"
725130391Sle	    "        Change the name of the specified object.\n"
726152616Sle	    "rebuildparity plex [-f]\n"
727152616Sle	    "        Rebuild the parity blocks of a RAID-5 plex.\n"
728266014Smarius	    "resetconfig [-f]\n"
729157052Sle	    "        Reset the complete gvinum configuration\n"
730190507Slulf	    "rm [-r] [-f] volume | plex | subdisk | drive\n"
731130391Sle	    "        Remove an object.\n"
732130391Sle	    "saveconfig\n"
733130391Sle	    "        Save vinum configuration to disk after configuration"
734130391Sle	    " failures.\n"
735152616Sle	    "setstate [-f] state [volume | plex | subdisk | drive]\n"
736130391Sle	    "        Set state without influencing other objects, for"
737130391Sle	    " diagnostic pur-\n"
738130391Sle	    "        poses only.\n"
739152616Sle	    "start [-S size] volume | plex | subdisk\n"
740130391Sle	    "        Allow the system to access the objects.\n"
741190507Slulf	    "stripe [-fv] [-n name] drives\n"
742190507Slulf	    "        Create a striped volume from the specified drives.\n"
743130391Sle	);
744130391Sle}
745130391Sle
746266043Smariusstatic void
747266043Smariusgvinum_setstate(int argc, char * const *argv)
748138112Sle{
749138112Sle	struct gctl_req *req;
750138112Sle	int flags, i;
751138112Sle	const char *errstr;
752138112Sle
753138112Sle	flags = 0;
754138112Sle
755138112Sle	optreset = 1;
756138112Sle	optind = 1;
757138112Sle
758138112Sle	while ((i = getopt(argc, argv, "f")) != -1) {
759138112Sle		switch (i) {
760138112Sle		case 'f':
761138112Sle			flags |= GV_FLAG_F;
762138112Sle			break;
763138112Sle		case '?':
764138112Sle		default:
765138112Sle			warn("invalid flag: %c", i);
766138112Sle			return;
767138112Sle		}
768138112Sle	}
769138112Sle
770138112Sle	argc -= optind;
771138112Sle	argv += optind;
772138112Sle
773138112Sle	if (argc != 2) {
774138112Sle		warnx("usage: setstate [-f] <state> <obj>");
775138112Sle		return;
776138112Sle	}
777138112Sle
778138112Sle	/*
779138112Sle	 * XXX: This hack is needed to avoid tripping over (now) invalid
780138112Sle	 * 'classic' vinum states and will go away later.
781138112Sle	 */
782138112Sle	if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
783138112Sle	    strcmp(argv[0], "stale")) {
784138112Sle		warnx("invalid state '%s'", argv[0]);
785138112Sle		return;
786138112Sle	}
787138112Sle
788138112Sle	req = gctl_get_handle();
789138112Sle	gctl_ro_param(req, "class", -1, "VINUM");
790138112Sle	gctl_ro_param(req, "verb", -1, "setstate");
791138112Sle	gctl_ro_param(req, "state", -1, argv[0]);
792138112Sle	gctl_ro_param(req, "object", -1, argv[1]);
793138112Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
794138112Sle
795138112Sle	errstr = gctl_issue(req);
796138112Sle	if (errstr != NULL)
797138112Sle		warnx("%s", errstr);
798138112Sle	gctl_free(req);
799138112Sle}
800138112Sle
801266043Smariusstatic void
802266043Smariusgvinum_list(int argc, char * const *argv)
803130391Sle{
804130391Sle	struct gctl_req *req;
805130391Sle	int flags, i, j;
806130391Sle	const char *errstr;
807130391Sle	char buf[20], *cmd, config[GV_CFG_LEN + 1];
808130391Sle
809130391Sle	flags = 0;
810130391Sle	cmd = "list";
811130391Sle
812130391Sle	if (argc) {
813130391Sle		optreset = 1;
814130391Sle		optind = 1;
815130391Sle		cmd = argv[0];
816130391Sle		while ((j = getopt(argc, argv, "rsvV")) != -1) {
817130391Sle			switch (j) {
818130391Sle			case 'r':
819130391Sle				flags |= GV_FLAG_R;
820130391Sle				break;
821130391Sle			case 's':
822130391Sle				flags |= GV_FLAG_S;
823130391Sle				break;
824130391Sle			case 'v':
825130391Sle				flags |= GV_FLAG_V;
826130391Sle				break;
827130391Sle			case 'V':
828130391Sle				flags |= GV_FLAG_V;
829130391Sle				flags |= GV_FLAG_VV;
830130391Sle				break;
831130391Sle			case '?':
832130391Sle			default:
833130391Sle				return;
834130391Sle			}
835130391Sle		}
836130391Sle		argc -= optind;
837130391Sle		argv += optind;
838130391Sle
839130391Sle	}
840130391Sle
841130391Sle	req = gctl_get_handle();
842130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
843130391Sle	gctl_ro_param(req, "verb", -1, "list");
844130391Sle	gctl_ro_param(req, "cmd", -1, cmd);
845130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
846130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
847130391Sle	gctl_rw_param(req, "config", sizeof(config), config);
848130391Sle	if (argc) {
849130391Sle		for (i = 0; i < argc; i++) {
850130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
851130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
852130391Sle		}
853130391Sle	}
854130391Sle	errstr = gctl_issue(req);
855130391Sle	if (errstr != NULL) {
856130391Sle		warnx("can't get configuration: %s", errstr);
857130391Sle		gctl_free(req);
858130391Sle		return;
859130391Sle	}
860130391Sle
861130391Sle	printf("%s", config);
862130391Sle	gctl_free(req);
863130391Sle}
864130391Sle
865190507Slulf/* Create a mirrored volume. */
866266043Smariusstatic void
867266043Smariusgvinum_mirror(int argc, char * const *argv)
868190507Slulf{
869190507Slulf
870190507Slulf	if (argc < 2) {
871190507Slulf		warnx("usage\tmirror [-fsv] [-n name] drives\n");
872190507Slulf		return;
873190507Slulf	}
874190507Slulf	create_volume(argc, argv, "mirror");
875190507Slulf}
876190507Slulf
877152616Sle/* Note that move is currently of form '[-r] target object [...]' */
878266043Smariusstatic void
879266043Smariusgvinum_move(int argc, char * const *argv)
880152616Sle{
881152616Sle	struct gctl_req *req;
882152616Sle	const char *errstr;
883152616Sle	char buf[20];
884152616Sle	int flags, i, j;
885152616Sle
886152616Sle	flags = 0;
887152616Sle	if (argc) {
888152616Sle		optreset = 1;
889152616Sle		optind = 1;
890152616Sle		while ((j = getopt(argc, argv, "f")) != -1) {
891152616Sle			switch (j) {
892152616Sle			case 'f':
893152616Sle				flags |= GV_FLAG_F;
894152616Sle				break;
895152616Sle			case '?':
896152616Sle			default:
897152616Sle				return;
898152616Sle			}
899152616Sle		}
900152616Sle		argc -= optind;
901152616Sle		argv += optind;
902152616Sle	}
903152616Sle
904152616Sle	switch (argc) {
905152616Sle		case 0:
906152616Sle			warnx("no destination or object(s) to move specified");
907152616Sle			return;
908152616Sle		case 1:
909152616Sle			warnx("no object(s) to move specified");
910152616Sle			return;
911152616Sle		default:
912152616Sle			break;
913152616Sle	}
914152616Sle
915152616Sle	req = gctl_get_handle();
916152616Sle	gctl_ro_param(req, "class", -1, "VINUM");
917152616Sle	gctl_ro_param(req, "verb", -1, "move");
918152616Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
919152616Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
920152616Sle	gctl_ro_param(req, "destination", -1, argv[0]);
921152616Sle	for (i = 1; i < argc; i++) {
922152616Sle		snprintf(buf, sizeof(buf), "argv%d", i);
923152616Sle		gctl_ro_param(req, buf, -1, argv[i]);
924152616Sle	}
925152631Sle	errstr = gctl_issue(req);
926152616Sle	if (errstr != NULL)
927152616Sle		warnx("can't move object(s):  %s", errstr);
928152616Sle	gctl_free(req);
929152616Sle}
930152616Sle
931266043Smariusstatic void
932266043Smariusgvinum_printconfig(int argc, char * const *argv)
933130391Sle{
934266043Smarius
935130391Sle	printconfig(stdout, "");
936130391Sle}
937130391Sle
938266043Smariusstatic void
939266043Smariusgvinum_parityop(int argc, char * const *argv, int rebuild)
940138110Sle{
941138110Sle	struct gctl_req *req;
942190507Slulf	int flags, i;
943138110Sle	const char *errstr;
944229915Seadler	char *op;
945138110Sle
946138110Sle	if (rebuild) {
947138110Sle		op = "rebuildparity";
948138110Sle	} else {
949138110Sle		op = "checkparity";
950138110Sle	}
951138110Sle
952138110Sle	optreset = 1;
953138110Sle	optind = 1;
954138110Sle	flags = 0;
955138110Sle	while ((i = getopt(argc, argv, "fv")) != -1) {
956138110Sle		switch (i) {
957138110Sle		case 'f':
958138110Sle			flags |= GV_FLAG_F;
959138110Sle			break;
960138110Sle		case 'v':
961138110Sle			flags |= GV_FLAG_V;
962138110Sle			break;
963138110Sle		default:
964138110Sle			warnx("invalid flag '%c'", i);
965138110Sle			return;
966138110Sle		}
967138110Sle	}
968138110Sle	argc -= optind;
969138110Sle	argv += optind;
970138110Sle
971138110Sle	if (argc != 1) {
972138110Sle		warn("usage: %s [-f] [-v] <plex>", op);
973138110Sle		return;
974138110Sle	}
975138110Sle
976190507Slulf	req = gctl_get_handle();
977190507Slulf	gctl_ro_param(req, "class", -1, "VINUM");
978190507Slulf	gctl_ro_param(req, "verb", -1, op);
979190507Slulf	gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
980190507Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
981190507Slulf	gctl_ro_param(req, "plex", -1, argv[0]);
982138110Sle
983190507Slulf	errstr = gctl_issue(req);
984190507Slulf	if (errstr)
985190507Slulf		warnx("%s\n", errstr);
986190507Slulf	gctl_free(req);
987190507Slulf}
988138110Sle
989190507Slulf/* Create a RAID-5 volume. */
990266043Smariusstatic void
991266043Smariusgvinum_raid5(int argc, char * const *argv)
992190507Slulf{
993190507Slulf
994190507Slulf	if (argc < 2) {
995190507Slulf		warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
996190507Slulf		return;
997138110Sle	}
998190507Slulf	create_volume(argc, argv, "raid5");
999138110Sle}
1000138110Sle
1001266043Smariusstatic void
1002266043Smariusgvinum_rename(int argc, char * const *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			default:
1019152616Sle				return;
1020152616Sle			}
1021152616Sle		}
1022152616Sle		argc -= optind;
1023152616Sle		argv += optind;
1024152616Sle	}
1025152616Sle
1026152616Sle	switch (argc) {
1027152616Sle		case 0:
1028152616Sle			warnx("no object to rename specified");
1029152616Sle			return;
1030152616Sle		case 1:
1031152616Sle			warnx("no new name specified");
1032152616Sle			return;
1033152616Sle		case 2:
1034152616Sle			break;
1035152616Sle		default:
1036152616Sle			warnx("more than one new name specified");
1037152616Sle			return;
1038152616Sle	}
1039152616Sle
1040152616Sle	req = gctl_get_handle();
1041152616Sle	gctl_ro_param(req, "class", -1, "VINUM");
1042152616Sle	gctl_ro_param(req, "verb", -1, "rename");
1043152616Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
1044152616Sle	gctl_ro_param(req, "object", -1, argv[0]);
1045152616Sle	gctl_ro_param(req, "newname", -1, argv[1]);
1046152631Sle	errstr = gctl_issue(req);
1047152616Sle	if (errstr != NULL)
1048152616Sle		warnx("can't rename object:  %s", errstr);
1049152616Sle	gctl_free(req);
1050152616Sle}
1051152616Sle
1052266043Smariusstatic void
1053266043Smariusgvinum_rm(int argc, char * const *argv)
1054130391Sle{
1055130391Sle	struct gctl_req *req;
1056130391Sle	int flags, i, j;
1057130391Sle	const char *errstr;
1058229915Seadler	char buf[20];
1059130391Sle
1060130391Sle	flags = 0;
1061130391Sle	optreset = 1;
1062130391Sle	optind = 1;
1063190507Slulf	while ((j = getopt(argc, argv, "rf")) != -1) {
1064130391Sle		switch (j) {
1065190507Slulf		case 'f':
1066190507Slulf			flags |= GV_FLAG_F;
1067190507Slulf			break;
1068130391Sle		case 'r':
1069130391Sle			flags |= GV_FLAG_R;
1070130391Sle			break;
1071130391Sle		default:
1072130391Sle			return;
1073130391Sle		}
1074130391Sle	}
1075130391Sle	argc -= optind;
1076130391Sle	argv += optind;
1077130391Sle
1078130391Sle	req = gctl_get_handle();
1079130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
1080130391Sle	gctl_ro_param(req, "verb", -1, "remove");
1081130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
1082130391Sle	gctl_ro_param(req, "flags", sizeof(int), &flags);
1083130391Sle	if (argc) {
1084130391Sle		for (i = 0; i < argc; i++) {
1085130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
1086130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
1087130391Sle		}
1088130391Sle	}
1089130391Sle	errstr = gctl_issue(req);
1090130391Sle	if (errstr != NULL) {
1091130391Sle		warnx("can't remove: %s", errstr);
1092130391Sle		gctl_free(req);
1093130391Sle		return;
1094130391Sle	}
1095130391Sle	gctl_free(req);
1096130391Sle}
1097130391Sle
1098266043Smariusstatic void
1099266043Smariusgvinum_resetconfig(int argc, char * const *argv)
1100157052Sle{
1101157052Sle	struct gctl_req *req;
1102157052Sle	const char *errstr;
1103157052Sle	char reply[32];
1104266014Smarius	int flags, i;
1105157052Sle
1106266014Smarius	flags = 0;
1107266014Smarius	while ((i = getopt(argc, argv, "f")) != -1) {
1108266014Smarius		switch (i) {
1109266014Smarius		case 'f':
1110266014Smarius			flags |= GV_FLAG_F;
1111266014Smarius			break;
1112266014Smarius		default:
1113266014Smarius			warn("invalid flag: %c", i);
1114266014Smarius			return;
1115266014Smarius		}
1116157052Sle	}
1117266014Smarius	if ((flags & GV_FLAG_F) == 0) {
1118266014Smarius		if (!isatty(STDIN_FILENO)) {
1119266014Smarius			warn("Please enter this command from a tty device\n");
1120266014Smarius			return;
1121266014Smarius		}
1122266014Smarius		printf(" WARNING!  This command will completely wipe out"
1123266014Smarius		    " your gvinum configuration.\n"
1124266014Smarius		    " All data will be lost.  If you really want to do this,"
1125266014Smarius		    " enter the text\n\n"
1126266014Smarius		    " NO FUTURE\n"
1127266014Smarius		    " Enter text -> ");
1128266014Smarius		fgets(reply, sizeof(reply), stdin);
1129266014Smarius		if (strcmp(reply, "NO FUTURE\n")) {
1130266014Smarius			printf("\n No change\n");
1131266014Smarius			return;
1132266014Smarius		}
1133157052Sle	}
1134157052Sle	req = gctl_get_handle();
1135157052Sle	gctl_ro_param(req, "class", -1, "VINUM");
1136157052Sle	gctl_ro_param(req, "verb", -1, "resetconfig");
1137157052Sle	errstr = gctl_issue(req);
1138157052Sle	if (errstr != NULL) {
1139157052Sle		warnx("can't reset config: %s", errstr);
1140157052Sle		gctl_free(req);
1141157052Sle		return;
1142157052Sle	}
1143157052Sle	gctl_free(req);
1144157052Sle	printf("gvinum configuration obliterated\n");
1145157052Sle}
1146157052Sle
1147266043Smariusstatic void
1148130391Slegvinum_saveconfig(void)
1149130391Sle{
1150130391Sle	struct gctl_req *req;
1151130391Sle	const char *errstr;
1152130391Sle
1153130391Sle	req = gctl_get_handle();
1154130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
1155130391Sle	gctl_ro_param(req, "verb", -1, "saveconfig");
1156130391Sle	errstr = gctl_issue(req);
1157130391Sle	if (errstr != NULL)
1158130391Sle		warnx("can't save configuration: %s", errstr);
1159130391Sle	gctl_free(req);
1160130391Sle}
1161130391Sle
1162266043Smariusstatic void
1163266043Smariusgvinum_start(int argc, char * const *argv)
1164130391Sle{
1165130391Sle	struct gctl_req *req;
1166130391Sle	int i, initsize, j;
1167130391Sle	const char *errstr;
1168130391Sle	char buf[20];
1169130391Sle
1170130391Sle	/* 'start' with no arguments is a no-op. */
1171130391Sle	if (argc == 1)
1172130391Sle		return;
1173130391Sle
1174130391Sle	initsize = 0;
1175130391Sle
1176130391Sle	optreset = 1;
1177130391Sle	optind = 1;
1178130391Sle	while ((j = getopt(argc, argv, "S")) != -1) {
1179130391Sle		switch (j) {
1180130391Sle		case 'S':
1181130391Sle			initsize = atoi(optarg);
1182130391Sle			break;
1183130391Sle		default:
1184130391Sle			return;
1185130391Sle		}
1186130391Sle	}
1187130391Sle	argc -= optind;
1188130391Sle	argv += optind;
1189130391Sle
1190130391Sle	if (!initsize)
1191130391Sle		initsize = 512;
1192130391Sle
1193130391Sle	req = gctl_get_handle();
1194130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
1195130391Sle	gctl_ro_param(req, "verb", -1, "start");
1196130391Sle	gctl_ro_param(req, "argc", sizeof(int), &argc);
1197130391Sle	gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1198130391Sle	if (argc) {
1199130391Sle		for (i = 0; i < argc; i++) {
1200130391Sle			snprintf(buf, sizeof(buf), "argv%d", i);
1201130391Sle			gctl_ro_param(req, buf, -1, argv[i]);
1202130391Sle		}
1203130391Sle	}
1204130391Sle	errstr = gctl_issue(req);
1205130391Sle	if (errstr != NULL) {
1206130391Sle		warnx("can't start: %s", errstr);
1207130391Sle		gctl_free(req);
1208130391Sle		return;
1209130391Sle	}
1210130391Sle
1211130391Sle	gctl_free(req);
1212130391Sle}
1213130391Sle
1214266043Smariusstatic void
1215266043Smariusgvinum_stop(int argc, char * const *argv)
1216130391Sle{
1217190507Slulf	int err, fileid;
1218130391Sle
1219265536Smarius	fileid = kldfind(GVINUMKLD);
1220130391Sle	if (fileid == -1) {
1221265536Smarius		if (modfind(GVINUMMOD) < 0)
1222265536Smarius			warn("cannot find " GVINUMKLD);
1223130391Sle		return;
1224130391Sle	}
1225190507Slulf
1226190507Slulf	/*
1227190507Slulf	 * This little hack prevents that we end up in an infinite loop in
1228190507Slulf	 * g_unload_class().  gv_unload() will return EAGAIN so that the GEOM
1229190507Slulf	 * event thread will be free for the g_wither_geom() call from
1230190507Slulf	 * gv_unload().  It's silly, but it works.
1231190507Slulf	 */
1232265536Smarius	printf("unloading " GVINUMKLD " kernel module... ");
1233190507Slulf	fflush(stdout);
1234190507Slulf	if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1235190507Slulf		sleep(1);
1236190507Slulf		err = kldunload(fileid);
1237190507Slulf	}
1238190507Slulf	if (err != 0) {
1239190507Slulf		printf(" failed!\n");
1240265536Smarius		warn("cannot unload " GVINUMKLD);
1241130391Sle		return;
1242130391Sle	}
1243130391Sle
1244190507Slulf	printf("done\n");
1245130391Sle	exit(0);
1246130391Sle}
1247130391Sle
1248190507Slulf/* Create a striped volume. */
1249266043Smariusstatic void
1250266043Smariusgvinum_stripe(int argc, char * const *argv)
1251190507Slulf{
1252190507Slulf
1253190507Slulf	if (argc < 2) {
1254190507Slulf		warnx("usage:\tstripe [-fv] [-n name] drives\n");
1255190507Slulf		return;
1256190507Slulf	}
1257190507Slulf	create_volume(argc, argv, "stripe");
1258190507Slulf}
1259190507Slulf
1260190884Slulf/* Grow a subdisk by adding disk backed by provider. */
1261266043Smariusstatic void
1262266043Smariusgvinum_grow(int argc, char * const *argv)
1263190884Slulf{
1264190884Slulf	struct gctl_req *req;
1265190884Slulf	char *drive, *sdname;
1266190884Slulf	char sdprefix[GV_MAXSDNAME];
1267190884Slulf	struct gv_drive *d;
1268190884Slulf	struct gv_sd *s;
1269190884Slulf	const char *errstr;
1270190884Slulf	int drives, volumes, plexes, subdisks, flags;
1271190884Slulf
1272266014Smarius	flags = 0;
1273190884Slulf	drives = volumes = plexes = subdisks = 0;
1274190884Slulf	if (argc < 3) {
1275190884Slulf		warnx("usage:\tgrow plex drive\n");
1276190884Slulf		return;
1277190884Slulf	}
1278190884Slulf
1279190884Slulf	s = gv_alloc_sd();
1280190884Slulf	if (s == NULL) {
1281190884Slulf		warn("unable to create subdisk");
1282190884Slulf		return;
1283190884Slulf	}
1284190884Slulf	d = gv_alloc_drive();
1285190884Slulf	if (d == NULL) {
1286190884Slulf		warn("unable to create drive");
1287190884Slulf		free(s);
1288190884Slulf		return;
1289190884Slulf	}
1290190884Slulf	/* Lookup device and set an appropriate drive name. */
1291204665Slulf	drive = find_drive();
1292190884Slulf	if (drive == NULL) {
1293190884Slulf		warn("unable to find an appropriate drive name");
1294190884Slulf		free(s);
1295190884Slulf		free(d);
1296190884Slulf		return;
1297190884Slulf	}
1298190884Slulf	strlcpy(d->name, drive, sizeof(d->name));
1299204665Slulf	copy_device(d, argv[2]);
1300204665Slulf
1301190884Slulf	drives = 1;
1302190884Slulf
1303190884Slulf	/* We try to use the plex name as basis for the subdisk name. */
1304190884Slulf	snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]);
1305190884Slulf	sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME);
1306190884Slulf	if (sdname == NULL) {
1307190884Slulf		warn("unable to find an appropriate subdisk name");
1308190884Slulf		free(s);
1309190884Slulf		free(d);
1310190884Slulf		free(drive);
1311190884Slulf		return;
1312190884Slulf	}
1313190884Slulf	strlcpy(s->name, sdname, sizeof(s->name));
1314190884Slulf	free(sdname);
1315190884Slulf	strlcpy(s->plex, argv[1], sizeof(s->plex));
1316190884Slulf	strlcpy(s->drive, d->name, sizeof(s->drive));
1317190884Slulf	subdisks = 1;
1318190884Slulf
1319190884Slulf	req = gctl_get_handle();
1320190884Slulf	gctl_ro_param(req, "class", -1, "VINUM");
1321190884Slulf	gctl_ro_param(req, "verb", -1, "create");
1322190884Slulf	gctl_ro_param(req, "flags", sizeof(int), &flags);
1323190884Slulf	gctl_ro_param(req, "volumes", sizeof(int), &volumes);
1324190884Slulf	gctl_ro_param(req, "plexes", sizeof(int), &plexes);
1325190884Slulf	gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
1326190884Slulf	gctl_ro_param(req, "drives", sizeof(int), &drives);
1327190884Slulf	gctl_ro_param(req, "drive0", sizeof(*d), d);
1328190884Slulf	gctl_ro_param(req, "sd0", sizeof(*s), s);
1329190884Slulf	errstr = gctl_issue(req);
1330190884Slulf	free(drive);
1331190884Slulf	if (errstr != NULL) {
1332190884Slulf		warnx("unable to grow plex: %s", errstr);
1333190884Slulf		free(s);
1334190884Slulf		free(d);
1335190884Slulf		return;
1336190884Slulf	}
1337190884Slulf	gctl_free(req);
1338190884Slulf}
1339190884Slulf
1340266043Smariusstatic void
1341266043Smariusparseline(int argc, char * const *argv)
1342130391Sle{
1343266043Smarius
1344130391Sle	if (argc <= 0)
1345130391Sle		return;
1346130391Sle
1347150044Sle	if (!strcmp(argv[0], "create"))
1348130391Sle		gvinum_create(argc, argv);
1349130391Sle	else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
1350130391Sle		exit(0);
1351190507Slulf	else if (!strcmp(argv[0], "attach"))
1352190507Slulf		gvinum_attach(argc, argv);
1353190507Slulf	else if (!strcmp(argv[0], "detach"))
1354190507Slulf		gvinum_detach(argc, argv);
1355190507Slulf	else if (!strcmp(argv[0], "concat"))
1356190507Slulf		gvinum_concat(argc, argv);
1357190884Slulf	else if (!strcmp(argv[0], "grow"))
1358190884Slulf		gvinum_grow(argc, argv);
1359130391Sle	else if (!strcmp(argv[0], "help"))
1360130391Sle		gvinum_help();
1361130391Sle	else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
1362130391Sle		gvinum_list(argc, argv);
1363130391Sle	else if (!strcmp(argv[0], "ld"))
1364130391Sle		gvinum_list(argc, argv);
1365130391Sle	else if (!strcmp(argv[0], "lp"))
1366130391Sle		gvinum_list(argc, argv);
1367130391Sle	else if (!strcmp(argv[0], "ls"))
1368130391Sle		gvinum_list(argc, argv);
1369130391Sle	else if (!strcmp(argv[0], "lv"))
1370130391Sle		gvinum_list(argc, argv);
1371190507Slulf	else if (!strcmp(argv[0], "mirror"))
1372190507Slulf		gvinum_mirror(argc, argv);
1373152616Sle	else if (!strcmp(argv[0], "move"))
1374152616Sle		gvinum_move(argc, argv);
1375152616Sle	else if (!strcmp(argv[0], "mv"))
1376152616Sle		gvinum_move(argc, argv);
1377130391Sle	else if (!strcmp(argv[0], "printconfig"))
1378130391Sle		gvinum_printconfig(argc, argv);
1379190507Slulf	else if (!strcmp(argv[0], "raid5"))
1380190507Slulf		gvinum_raid5(argc, argv);
1381152616Sle	else if (!strcmp(argv[0], "rename"))
1382152616Sle		gvinum_rename(argc, argv);
1383157052Sle	else if (!strcmp(argv[0], "resetconfig"))
1384266014Smarius		gvinum_resetconfig(argc, argv);
1385130391Sle	else if (!strcmp(argv[0], "rm"))
1386130391Sle		gvinum_rm(argc, argv);
1387130391Sle	else if (!strcmp(argv[0], "saveconfig"))
1388130391Sle		gvinum_saveconfig();
1389138112Sle	else if (!strcmp(argv[0], "setstate"))
1390138112Sle		gvinum_setstate(argc, argv);
1391130391Sle	else if (!strcmp(argv[0], "start"))
1392130391Sle		gvinum_start(argc, argv);
1393130391Sle	else if (!strcmp(argv[0], "stop"))
1394130391Sle		gvinum_stop(argc, argv);
1395190507Slulf	else if (!strcmp(argv[0], "stripe"))
1396190507Slulf		gvinum_stripe(argc, argv);
1397138110Sle	else if (!strcmp(argv[0], "checkparity"))
1398138110Sle		gvinum_parityop(argc, argv, 0);
1399138110Sle	else if (!strcmp(argv[0], "rebuildparity"))
1400138110Sle		gvinum_parityop(argc, argv, 1);
1401130391Sle	else
1402130391Sle		printf("unknown command '%s'\n", argv[0]);
1403130391Sle}
1404130391Sle
1405130391Sle/*
1406130391Sle * The guts of printconfig.  This is called from gvinum_printconfig and from
1407130391Sle * gvinum_create when called without an argument, in order to give the user
1408130391Sle * something to edit.
1409130391Sle */
1410266043Smariusstatic void
1411266043Smariusprintconfig(FILE *of, const char *comment)
1412130391Sle{
1413130391Sle	struct gctl_req *req;
1414130391Sle	struct utsname uname_s;
1415130391Sle	const char *errstr;
1416130391Sle	time_t now;
1417130391Sle	char buf[GV_CFG_LEN + 1];
1418130391Sle
1419130391Sle	uname(&uname_s);
1420130391Sle	time(&now);
1421130391Sle
1422130391Sle	req = gctl_get_handle();
1423130391Sle	gctl_ro_param(req, "class", -1, "VINUM");
1424130391Sle	gctl_ro_param(req, "verb", -1, "getconfig");
1425130391Sle	gctl_ro_param(req, "comment", -1, comment);
1426130391Sle	gctl_rw_param(req, "config", sizeof(buf), buf);
1427130391Sle	errstr = gctl_issue(req);
1428130391Sle	if (errstr != NULL) {
1429130391Sle		warnx("can't get configuration: %s", errstr);
1430130391Sle		return;
1431130391Sle	}
1432130391Sle	gctl_free(req);
1433130391Sle
1434130391Sle	fprintf(of, "# Vinum configuration of %s, saved at %s",
1435130391Sle	    uname_s.nodename,
1436130391Sle	    ctime(&now));
1437130391Sle
1438130391Sle	if (*comment != '\0')
1439130391Sle	    fprintf(of, "# Current configuration:\n");
1440130391Sle
1441215704Sbrucec	fprintf(of, "%s", buf);
1442130391Sle}
1443