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