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