]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sbin/gvinum/gvinum.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sbin / gvinum / gvinum.c
1 /*
2  *  Copyright (c) 2004 Lukas Ertl, 2005 Chris Jones
3  *  All rights reserved.
4  *
5  * Portions of this software were developed for the FreeBSD Project
6  * by Chris Jones thanks to the support of Google's Summer of Code
7  * program and mentoring by Lukas Ertl.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32
33 #include <sys/param.h>
34 #include <sys/linker.h>
35 #include <sys/lock.h>
36 #include <sys/module.h>
37 #include <sys/mutex.h>
38 #include <sys/queue.h>
39 #include <sys/utsname.h>
40
41 #include <geom/vinum/geom_vinum_var.h>
42 #include <geom/vinum/geom_vinum_share.h>
43
44 #include <ctype.h>
45 #include <err.h>
46 #include <libgeom.h>
47 #include <stdint.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <paths.h>
51 #include <readline/readline.h>
52 #include <readline/history.h>
53 #include <unistd.h>
54
55 #include "gvinum.h"
56
57 void    gvinum_create(int, char **);
58 void    gvinum_help(void);
59 void    gvinum_list(int, char **);
60 void    gvinum_move(int, char **);
61 void    gvinum_parityop(int, char **, int);
62 void    gvinum_printconfig(int, char **);
63 void    gvinum_rename(int, char **);
64 void    gvinum_resetconfig(void);
65 void    gvinum_rm(int, char **);
66 void    gvinum_saveconfig(void);
67 void    gvinum_setstate(int, char **);
68 void    gvinum_start(int, char **);
69 void    gvinum_stop(int, char **);
70 void    parseline(int, char **);
71 void    printconfig(FILE *, char *);
72
73 int
74 main(int argc, char **argv)
75 {
76         int line, tokens;
77         char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
78
79         /* Load the module if necessary. */
80         if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
81                 err(1, GVINUMMOD ": Kernel module not available");
82
83         /* Arguments given on the command line. */
84         if (argc > 1) {
85                 argc--;
86                 argv++;
87                 parseline(argc, argv);
88
89         /* Interactive mode. */
90         } else {
91                 for (;;) {
92                         inputline = readline("gvinum -> ");
93                         if (inputline == NULL) {
94                                 if (ferror(stdin)) {
95                                         err(1, "can't read input");
96                                 } else {
97                                         printf("\n");
98                                         exit(0);
99                                 }
100                         } else if (*inputline) {
101                                 add_history(inputline);
102                                 strcpy(buffer, inputline);
103                                 free(inputline);
104                                 line++;             /* count the lines */
105                                 tokens = gv_tokenize(buffer, token, GV_MAXARGS);
106                                 if (tokens)
107                                         parseline(tokens, token);
108                         }
109                 }
110         }
111         exit(0);
112 }
113
114 void
115 gvinum_create(int argc, char **argv)
116 {
117         struct gctl_req *req;
118         struct gv_drive *d;
119         struct gv_plex *p;
120         struct gv_sd *s;
121         struct gv_volume *v;
122         FILE *tmp;
123         int drives, errors, fd, line, plexes, plex_in_volume;
124         int sd_in_plex, status, subdisks, tokens, volumes;
125         const char *errstr;
126         char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed;
127         char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
128         char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
129
130         if (argc == 2) {
131                 if ((tmp = fopen(argv[1], "r")) == NULL) {
132                         warn("can't open '%s' for reading", argv[1]);
133                         return;
134                 }
135         } else {
136                 snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
137                 
138                 if ((fd = mkstemp(tmpfile)) == -1) {
139                         warn("temporary file not accessible");
140                         return;
141                 }
142                 if ((tmp = fdopen(fd, "w")) == NULL) {
143                         warn("can't open '%s' for writing", tmpfile);
144                         return;
145                 }
146                 printconfig(tmp, "# ");
147                 fclose(tmp);
148                 
149                 ed = getenv("EDITOR");
150                 if (ed == NULL)
151                         ed = _PATH_VI;
152                 
153                 snprintf(commandline, sizeof(commandline), "%s %s", ed,
154                     tmpfile);
155                 status = system(commandline);
156                 if (status != 0) {
157                         warn("couldn't exec %s; status: %d", ed, status);
158                         return;
159                 }
160                 
161                 if ((tmp = fopen(tmpfile, "r")) == NULL) {
162                         warn("can't open '%s' for reading", tmpfile);
163                         return;
164                 }
165         }
166
167         req = gctl_get_handle();
168         gctl_ro_param(req, "class", -1, "VINUM");
169         gctl_ro_param(req, "verb", -1, "create");
170
171         drives = volumes = plexes = subdisks = 0;
172         plex_in_volume = sd_in_plex = 0;
173         errors = 0;
174         line = 1;
175         while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
176
177                 /* Skip empty lines and comments. */
178                 if (*buf == '\0' || *buf == '#') {
179                         line++;
180                         continue;
181                 }
182
183                 /* Kill off the newline. */
184                 buf[strlen(buf) - 1] = '\0';
185
186                 /*
187                  * Copy the original input line in case we need it for error
188                  * output.
189                  */
190                 strncpy(original, buf, sizeof(buf));
191
192                 tokens = gv_tokenize(buf, token, GV_MAXARGS);
193                 if (tokens <= 0) {
194                         line++;
195                         continue;
196                 }
197
198                 /* Volume definition. */
199                 if (!strcmp(token[0], "volume")) {
200                         v = gv_new_volume(tokens, token);
201                         if (v == NULL) {
202                                 warnx("line %d: invalid volume definition",
203                                     line);
204                                 warnx("line %d: '%s'", line, original);
205                                 errors++;
206                                 line++;
207                                 continue;
208                         }
209
210                         /* Reset plex count for this volume. */
211                         plex_in_volume = 0;
212
213                         /*
214                          * Set default volume name for following plex
215                          * definitions.
216                          */
217                         strncpy(volume, v->name, sizeof(volume));
218
219                         snprintf(buf1, sizeof(buf1), "volume%d", volumes);
220                         gctl_ro_param(req, buf1, sizeof(*v), v);
221                         volumes++;
222
223                 /* Plex definition. */
224                 } else if (!strcmp(token[0], "plex")) {
225                         p = gv_new_plex(tokens, token);
226                         if (p == NULL) {
227                                 warnx("line %d: invalid plex definition", line);
228                                 warnx("line %d: '%s'", line, original);
229                                 errors++;
230                                 line++;
231                                 continue;
232                         }
233
234                         /* Reset subdisk count for this plex. */
235                         sd_in_plex = 0;
236
237                         /* Default name. */
238                         if (strlen(p->name) == 0) {
239                                 snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d",
240                                     volume, plex_in_volume++);
241                         }
242
243                         /* Default volume. */
244                         if (strlen(p->volume) == 0) {
245                                 snprintf(p->volume, GV_MAXVOLNAME, "%s",
246                                     volume);
247                         }
248
249                         /*
250                          * Set default plex name for following subdisk
251                          * definitions.
252                          */
253                         strncpy(plex, p->name, GV_MAXPLEXNAME);
254
255                         snprintf(buf1, sizeof(buf1), "plex%d", plexes);
256                         gctl_ro_param(req, buf1, sizeof(*p), p);
257                         plexes++;
258
259                 /* Subdisk definition. */
260                 } else if (!strcmp(token[0], "sd")) {
261                         s = gv_new_sd(tokens, token);
262                         if (s == NULL) {
263                                 warnx("line %d: invalid subdisk "
264                                     "definition:", line);
265                                 warnx("line %d: '%s'", line, original);
266                                 errors++;
267                                 line++;
268                                 continue;
269                         }
270
271                         /* Default name. */
272                         if (strlen(s->name) == 0) {
273                                 snprintf(s->name, GV_MAXSDNAME, "%s.s%d",
274                                     plex, sd_in_plex++);
275                         }
276
277                         /* Default plex. */
278                         if (strlen(s->plex) == 0)
279                                 snprintf(s->plex, GV_MAXPLEXNAME, "%s", plex);
280
281                         snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
282                         gctl_ro_param(req, buf1, sizeof(*s), s);
283                         subdisks++;
284
285                 /* Subdisk definition. */
286                 } else if (!strcmp(token[0], "drive")) {
287                         d = gv_new_drive(tokens, token);
288                         if (d == NULL) {
289                                 warnx("line %d: invalid drive definition:",
290                                     line);
291                                 warnx("line %d: '%s'", line, original);
292                                 errors++;
293                                 line++;
294                                 continue;
295                         }
296
297                         snprintf(buf1, sizeof(buf1), "drive%d", drives);
298                         gctl_ro_param(req, buf1, sizeof(*d), d);
299                         drives++;
300
301                 /* Everything else is bogus. */
302                 } else {
303                         warnx("line %d: invalid definition:", line);
304                         warnx("line %d: '%s'", line, original);
305                         errors++;
306                 }
307                 line++;
308         }
309
310         fclose(tmp);
311         unlink(tmpfile);
312
313         if (!errors && (volumes || plexes || subdisks || drives)) {
314                 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
315                 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
316                 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
317                 gctl_ro_param(req, "drives", sizeof(int), &drives);
318                 errstr = gctl_issue(req);
319                 if (errstr != NULL)
320                         warnx("create failed: %s", errstr);
321         }
322         gctl_free(req);
323         gvinum_list(0, NULL);
324 }
325
326 void
327 gvinum_help(void)
328 {
329         printf("COMMANDS\n"
330             "checkparity [-f] plex\n"
331             "        Check the parity blocks of a RAID-5 plex.\n"
332             "create description-file\n"
333             "        Create as per description-file or open editor.\n"
334             "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
335             "        List information about specified objects.\n"
336             "ld [-r] [-v] [-V] [volume]\n"
337             "        List information about drives.\n"
338             "ls [-r] [-v] [-V] [subdisk]\n"
339             "        List information about subdisks.\n"
340             "lp [-r] [-v] [-V] [plex]\n"
341             "        List information about plexes.\n"
342             "lv [-r] [-v] [-V] [volume]\n"
343             "        List information about volumes.\n"
344             "move | mv -f drive object ...\n"
345             "        Move the object(s) to the specified drive.\n"
346             "quit    Exit the vinum program when running in interactive mode."
347             "  Nor-\n"
348             "        mally this would be done by entering the EOF character.\n"
349             "rename [-r] [drive | subdisk | plex | volume] newname\n"
350             "        Change the name of the specified object.\n"
351             "rebuildparity plex [-f]\n"
352             "        Rebuild the parity blocks of a RAID-5 plex.\n"
353             "resetconfig\n"
354             "        Reset the complete gvinum configuration\n"
355             "rm [-r] volume | plex | subdisk | drive\n"
356             "        Remove an object.\n"
357             "saveconfig\n"
358             "        Save vinum configuration to disk after configuration"
359             " failures.\n"
360             "setstate [-f] state [volume | plex | subdisk | drive]\n"
361             "        Set state without influencing other objects, for"
362             " diagnostic pur-\n"
363             "        poses only.\n"
364             "start [-S size] volume | plex | subdisk\n"
365             "        Allow the system to access the objects.\n"
366         );
367
368         return;
369 }
370
371 void
372 gvinum_setstate(int argc, char **argv)
373 {
374         struct gctl_req *req;
375         int flags, i;
376         const char *errstr;
377
378         flags = 0;
379
380         optreset = 1;
381         optind = 1;
382
383         while ((i = getopt(argc, argv, "f")) != -1) {
384                 switch (i) {
385                 case 'f':
386                         flags |= GV_FLAG_F;
387                         break;
388                 case '?':
389                 default:
390                         warn("invalid flag: %c", i);
391                         return;
392                 }
393         }
394
395         argc -= optind;
396         argv += optind;
397
398         if (argc != 2) {
399                 warnx("usage: setstate [-f] <state> <obj>");
400                 return;
401         }
402
403         /*
404          * XXX: This hack is needed to avoid tripping over (now) invalid
405          * 'classic' vinum states and will go away later.
406          */
407         if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
408             strcmp(argv[0], "stale")) {
409                 warnx("invalid state '%s'", argv[0]);
410                 return;
411         }
412
413         req = gctl_get_handle();
414         gctl_ro_param(req, "class", -1, "VINUM");
415         gctl_ro_param(req, "verb", -1, "setstate");
416         gctl_ro_param(req, "state", -1, argv[0]);
417         gctl_ro_param(req, "object", -1, argv[1]);
418         gctl_ro_param(req, "flags", sizeof(int), &flags);
419
420         errstr = gctl_issue(req);
421         if (errstr != NULL)
422                 warnx("%s", errstr);
423         gctl_free(req);
424 }
425
426 void
427 gvinum_list(int argc, char **argv)
428 {
429         struct gctl_req *req;
430         int flags, i, j;
431         const char *errstr;
432         char buf[20], *cmd, config[GV_CFG_LEN + 1];
433
434         flags = 0;
435         cmd = "list";
436
437         if (argc) {
438                 optreset = 1;
439                 optind = 1;
440                 cmd = argv[0];
441                 while ((j = getopt(argc, argv, "rsvV")) != -1) {
442                         switch (j) {
443                         case 'r':
444                                 flags |= GV_FLAG_R;
445                                 break;
446                         case 's':
447                                 flags |= GV_FLAG_S;
448                                 break;
449                         case 'v':
450                                 flags |= GV_FLAG_V;
451                                 break;
452                         case 'V':
453                                 flags |= GV_FLAG_V;
454                                 flags |= GV_FLAG_VV;
455                                 break;
456                         case '?':
457                         default:
458                                 return;
459                         }
460                 }
461                 argc -= optind;
462                 argv += optind;
463
464         }
465
466         req = gctl_get_handle();
467         gctl_ro_param(req, "class", -1, "VINUM");
468         gctl_ro_param(req, "verb", -1, "list");
469         gctl_ro_param(req, "cmd", -1, cmd);
470         gctl_ro_param(req, "argc", sizeof(int), &argc);
471         gctl_ro_param(req, "flags", sizeof(int), &flags);
472         gctl_rw_param(req, "config", sizeof(config), config);
473         if (argc) {
474                 for (i = 0; i < argc; i++) {
475                         snprintf(buf, sizeof(buf), "argv%d", i);
476                         gctl_ro_param(req, buf, -1, argv[i]);
477                 }
478         }
479         errstr = gctl_issue(req);
480         if (errstr != NULL) {
481                 warnx("can't get configuration: %s", errstr);
482                 gctl_free(req);
483                 return;
484         }
485
486         printf("%s", config);
487         gctl_free(req);
488         return;
489 }
490
491 /* Note that move is currently of form '[-r] target object [...]' */
492 void
493 gvinum_move(int argc, char **argv)
494 {
495         struct gctl_req *req;
496         const char *errstr;
497         char buf[20];
498         int flags, i, j;
499
500         flags = 0;
501         if (argc) {
502                 optreset = 1;
503                 optind = 1;
504                 while ((j = getopt(argc, argv, "f")) != -1) {
505                         switch (j) {
506                         case 'f':
507                                 flags |= GV_FLAG_F;
508                                 break;
509                         case '?':
510                         default:
511                                 return;
512                         }
513                 }
514                 argc -= optind;
515                 argv += optind;
516         }
517
518         switch (argc) {
519                 case 0:
520                         warnx("no destination or object(s) to move specified");
521                         return;
522                 case 1:
523                         warnx("no object(s) to move specified");
524                         return;
525                 default:
526                         break;
527         }
528
529         req = gctl_get_handle();
530         gctl_ro_param(req, "class", -1, "VINUM");
531         gctl_ro_param(req, "verb", -1, "move");
532         gctl_ro_param(req, "argc", sizeof(int), &argc);
533         gctl_ro_param(req, "flags", sizeof(int), &flags);
534         gctl_ro_param(req, "destination", -1, argv[0]);
535         for (i = 1; i < argc; i++) {
536                 snprintf(buf, sizeof(buf), "argv%d", i);
537                 gctl_ro_param(req, buf, -1, argv[i]);
538         }
539         errstr = gctl_issue(req);
540         if (errstr != NULL)
541                 warnx("can't move object(s):  %s", errstr);
542         gctl_free(req);
543         return;
544 }
545
546 void
547 gvinum_printconfig(int argc, char **argv)
548 {
549         printconfig(stdout, "");
550 }
551
552 void
553 gvinum_parityop(int argc, char **argv, int rebuild)
554 {
555         struct gctl_req *req;
556         int flags, i, rv;
557         off_t offset;
558         const char *errstr;
559         char *op, *msg;
560
561         if (rebuild) {
562                 op = "rebuildparity";
563                 msg = "Rebuilding";
564         } else {
565                 op = "checkparity";
566                 msg = "Checking";
567         }
568
569         optreset = 1;
570         optind = 1;
571         flags = 0;
572         while ((i = getopt(argc, argv, "fv")) != -1) {
573                 switch (i) {
574                 case 'f':
575                         flags |= GV_FLAG_F;
576                         break;
577                 case 'v':
578                         flags |= GV_FLAG_V;
579                         break;
580                 case '?':
581                 default:
582                         warnx("invalid flag '%c'", i);
583                         return;
584                 }
585         }
586         argc -= optind;
587         argv += optind;
588
589         if (argc != 1) {
590                 warn("usage: %s [-f] [-v] <plex>", op);
591                 return;
592         }
593
594         do {
595                 rv = 0;
596                 req = gctl_get_handle();
597                 gctl_ro_param(req, "class", -1, "VINUM");
598                 gctl_ro_param(req, "verb", -1, "parityop");
599                 gctl_ro_param(req, "flags", sizeof(int), &flags);
600                 gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
601                 gctl_rw_param(req, "rv", sizeof(int), &rv);
602                 gctl_rw_param(req, "offset", sizeof(off_t), &offset);
603                 gctl_ro_param(req, "plex", -1, argv[0]);
604                 errstr = gctl_issue(req);
605                 if (errstr) {
606                         warnx("%s\n", errstr);
607                         gctl_free(req);
608                         break;
609                 }
610                 gctl_free(req);
611                 if (flags & GV_FLAG_V) {
612                         printf("\r%s at %s ... ", msg,
613                             gv_roughlength(offset, 1));
614                 }
615                 if (rv == 1) {
616                         printf("Parity incorrect at offset 0x%jx\n",
617                             (intmax_t)offset);
618                         if (!rebuild)
619                                 break;
620                 }
621                 fflush(stdout);
622
623                 /* Clear the -f flag. */
624                 flags &= ~GV_FLAG_F;
625         } while (rv >= 0);
626
627         if ((rv == 2) && (flags & GV_FLAG_V)) {
628                 if (rebuild)
629                         printf("Rebuilt parity on %s\n", argv[0]);
630                 else
631                         printf("%s has correct parity\n", argv[0]);
632         }
633 }
634
635 void
636 gvinum_rename(int argc, char **argv)
637 {
638         struct gctl_req *req;
639         const char *errstr;
640         int flags, j;
641
642         flags = 0;
643
644         if (argc) {
645                 optreset = 1;
646                 optind = 1;
647                 while ((j = getopt(argc, argv, "r")) != -1) {
648                         switch (j) {
649                         case 'r':
650                                 flags |= GV_FLAG_R;
651                                 break;
652                         case '?':
653                         default:
654                                 return;
655                         }
656                 }
657                 argc -= optind;
658                 argv += optind;
659         }
660
661         switch (argc) {
662                 case 0:
663                         warnx("no object to rename specified");
664                         return;
665                 case 1:
666                         warnx("no new name specified");
667                         return;
668                 case 2:
669                         break;
670                 default:
671                         warnx("more than one new name specified");
672                         return;
673         }
674
675         req = gctl_get_handle();
676         gctl_ro_param(req, "class", -1, "VINUM");
677         gctl_ro_param(req, "verb", -1, "rename");
678         gctl_ro_param(req, "flags", sizeof(int), &flags);
679         gctl_ro_param(req, "object", -1, argv[0]);
680         gctl_ro_param(req, "newname", -1, argv[1]);
681         errstr = gctl_issue(req);
682         if (errstr != NULL)
683                 warnx("can't rename object:  %s", errstr);
684         gctl_free(req);
685         return;
686 }
687
688 void
689 gvinum_rm(int argc, char **argv)
690 {
691         struct gctl_req *req;
692         int flags, i, j;
693         const char *errstr;
694         char buf[20], *cmd;
695
696         cmd = argv[0];
697         flags = 0;
698         optreset = 1;
699         optind = 1;
700         while ((j = getopt(argc, argv, "r")) != -1) {
701                 switch (j) {
702                 case 'r':
703                         flags |= GV_FLAG_R;
704                         break;
705                 case '?':
706                 default:
707                         return;
708                 }
709         }
710         argc -= optind;
711         argv += optind;
712
713         req = gctl_get_handle();
714         gctl_ro_param(req, "class", -1, "VINUM");
715         gctl_ro_param(req, "verb", -1, "remove");
716         gctl_ro_param(req, "argc", sizeof(int), &argc);
717         gctl_ro_param(req, "flags", sizeof(int), &flags);
718         if (argc) {
719                 for (i = 0; i < argc; i++) {
720                         snprintf(buf, sizeof(buf), "argv%d", i);
721                         gctl_ro_param(req, buf, -1, argv[i]);
722                 }
723         }
724         errstr = gctl_issue(req);
725         if (errstr != NULL) {
726                 warnx("can't remove: %s", errstr);
727                 gctl_free(req);
728                 return;
729         }
730         gctl_free(req);
731         gvinum_list(0, NULL);
732 }
733
734 void
735 gvinum_resetconfig(void)
736 {
737         struct gctl_req *req;
738         const char *errstr;
739         char reply[32];
740
741         if (!isatty(STDIN_FILENO)) {
742                 warn("Please enter this command from a tty device\n");
743                 return;
744         }
745         printf(" WARNING!  This command will completely wipe out your gvinum"
746             "configuration.\n"
747             " All data will be lost.  If you really want to do this,"
748             " enter the text\n\n"
749             " NO FUTURE\n"
750             " Enter text -> ");
751         fgets(reply, sizeof(reply), stdin);
752         if (strcmp(reply, "NO FUTURE\n")) {
753                 printf("\n No change\n");
754                 return;
755         }
756         req = gctl_get_handle();
757         gctl_ro_param(req, "class", -1, "VINUM");
758         gctl_ro_param(req, "verb", -1, "resetconfig");
759         errstr = gctl_issue(req);
760         if (errstr != NULL) {
761                 warnx("can't reset config: %s", errstr);
762                 gctl_free(req);
763                 return;
764         }
765         gctl_free(req);
766         gvinum_list(0, NULL);
767         printf("gvinum configuration obliterated\n");
768 }
769
770 void
771 gvinum_saveconfig(void)
772 {
773         struct gctl_req *req;
774         const char *errstr;
775
776         req = gctl_get_handle();
777         gctl_ro_param(req, "class", -1, "VINUM");
778         gctl_ro_param(req, "verb", -1, "saveconfig");
779         errstr = gctl_issue(req);
780         if (errstr != NULL)
781                 warnx("can't save configuration: %s", errstr);
782         gctl_free(req);
783 }
784
785 void
786 gvinum_start(int argc, char **argv)
787 {
788         struct gctl_req *req;
789         int i, initsize, j;
790         const char *errstr;
791         char buf[20];
792
793         /* 'start' with no arguments is a no-op. */
794         if (argc == 1)
795                 return;
796
797         initsize = 0;
798
799         optreset = 1;
800         optind = 1;
801         while ((j = getopt(argc, argv, "S")) != -1) {
802                 switch (j) {
803                 case 'S':
804                         initsize = atoi(optarg);
805                         break;
806                 case '?':
807                 default:
808                         return;
809                 }
810         }
811         argc -= optind;
812         argv += optind;
813
814         if (!initsize)
815                 initsize = 512;
816
817         req = gctl_get_handle();
818         gctl_ro_param(req, "class", -1, "VINUM");
819         gctl_ro_param(req, "verb", -1, "start");
820         gctl_ro_param(req, "argc", sizeof(int), &argc);
821         gctl_ro_param(req, "initsize", sizeof(int), &initsize);
822         if (argc) {
823                 for (i = 0; i < argc; i++) {
824                         snprintf(buf, sizeof(buf), "argv%d", i);
825                         gctl_ro_param(req, buf, -1, argv[i]);
826                 }
827         }
828         errstr = gctl_issue(req);
829         if (errstr != NULL) {
830                 warnx("can't start: %s", errstr);
831                 gctl_free(req);
832                 return;
833         }
834
835         gctl_free(req);
836         gvinum_list(0, NULL);
837 }
838
839 void
840 gvinum_stop(int argc, char **argv)
841 {
842         int fileid;
843
844         fileid = kldfind(GVINUMMOD);
845         if (fileid == -1) {
846                 warn("cannot find " GVINUMMOD);
847                 return;
848         }
849         if (kldunload(fileid) != 0) {
850                 warn("cannot unload " GVINUMMOD);
851                 return;
852         }
853
854         warnx(GVINUMMOD " unloaded");
855         exit(0);
856 }
857
858 void
859 parseline(int argc, char **argv)
860 {
861         if (argc <= 0)
862                 return;
863
864         if (!strcmp(argv[0], "create"))
865                 gvinum_create(argc, argv);
866         else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
867                 exit(0);
868         else if (!strcmp(argv[0], "help"))
869                 gvinum_help();
870         else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
871                 gvinum_list(argc, argv);
872         else if (!strcmp(argv[0], "ld"))
873                 gvinum_list(argc, argv);
874         else if (!strcmp(argv[0], "lp"))
875                 gvinum_list(argc, argv);
876         else if (!strcmp(argv[0], "ls"))
877                 gvinum_list(argc, argv);
878         else if (!strcmp(argv[0], "lv"))
879                 gvinum_list(argc, argv);
880         else if (!strcmp(argv[0], "move"))
881                 gvinum_move(argc, argv);
882         else if (!strcmp(argv[0], "mv"))
883                 gvinum_move(argc, argv);
884         else if (!strcmp(argv[0], "printconfig"))
885                 gvinum_printconfig(argc, argv);
886         else if (!strcmp(argv[0], "rename"))
887                 gvinum_rename(argc, argv);
888         else if (!strcmp(argv[0], "resetconfig"))
889                 gvinum_resetconfig();
890         else if (!strcmp(argv[0], "rm"))
891                 gvinum_rm(argc, argv);
892         else if (!strcmp(argv[0], "saveconfig"))
893                 gvinum_saveconfig();
894         else if (!strcmp(argv[0], "setstate"))
895                 gvinum_setstate(argc, argv);
896         else if (!strcmp(argv[0], "start"))
897                 gvinum_start(argc, argv);
898         else if (!strcmp(argv[0], "stop"))
899                 gvinum_stop(argc, argv);
900         else if (!strcmp(argv[0], "checkparity"))
901                 gvinum_parityop(argc, argv, 0);
902         else if (!strcmp(argv[0], "rebuildparity"))
903                 gvinum_parityop(argc, argv, 1);
904         else
905                 printf("unknown command '%s'\n", argv[0]);
906
907         return;
908 }
909
910 /*
911  * The guts of printconfig.  This is called from gvinum_printconfig and from
912  * gvinum_create when called without an argument, in order to give the user
913  * something to edit.
914  */
915 void
916 printconfig(FILE *of, char *comment)
917 {
918         struct gctl_req *req;
919         struct utsname uname_s;
920         const char *errstr;
921         time_t now;
922         char buf[GV_CFG_LEN + 1];
923         
924         uname(&uname_s);
925         time(&now);
926
927         req = gctl_get_handle();
928         gctl_ro_param(req, "class", -1, "VINUM");
929         gctl_ro_param(req, "verb", -1, "getconfig");
930         gctl_ro_param(req, "comment", -1, comment);
931         gctl_rw_param(req, "config", sizeof(buf), buf);
932         errstr = gctl_issue(req);
933         if (errstr != NULL) {
934                 warnx("can't get configuration: %s", errstr);
935                 return;
936         }
937         gctl_free(req);
938
939         fprintf(of, "# Vinum configuration of %s, saved at %s",
940             uname_s.nodename,
941             ctime(&now));
942         
943         if (*comment != '\0')
944             fprintf(of, "# Current configuration:\n");
945
946         fprintf(of, buf);
947 }