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