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