2 * Copyright (c) 2004 Lukas Ertl
3 * Copyright (c) 2005 Chris Jones
4 * Copyright (c) 2007 Ulf Lilleengen
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.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
35 #include <sys/param.h>
36 #include <sys/linker.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40 #include <sys/queue.h>
41 #include <sys/utsname.h>
43 #include <geom/vinum/geom_vinum_var.h>
44 #include <geom/vinum/geom_vinum_share.h>
55 #include <readline/readline.h>
56 #include <readline/history.h>
61 static void gvinum_attach(int, char * const *);
62 static void gvinum_concat(int, char * const *);
63 static void gvinum_create(int, char * const *);
64 static void gvinum_detach(int, char * const *);
65 static void gvinum_grow(int, char * const *);
66 static void gvinum_help(void);
67 static void gvinum_list(int, char * const *);
68 static void gvinum_move(int, char * const *);
69 static void gvinum_mirror(int, char * const *);
70 static void gvinum_parityop(int, char * const * , int);
71 static void gvinum_printconfig(int, char * const *);
72 static void gvinum_raid5(int, char * const *);
73 static void gvinum_rename(int, char * const *);
74 static void gvinum_resetconfig(int, char * const *);
75 static void gvinum_rm(int, char * const *);
76 static void gvinum_saveconfig(void);
77 static void gvinum_setstate(int, char * const *);
78 static void gvinum_start(int, char * const *);
79 static void gvinum_stop(int, char * const *);
80 static void gvinum_stripe(int, char * const *);
81 static void parseline(int, char * const *);
82 static void printconfig(FILE *, const char *);
84 static char *create_drive(const char *);
85 static void create_volume(int, char * const * , const char *);
86 static char *find_name(const char *, int, int);
87 static const char *find_pattern(char *, const char *);
88 static void copy_device(struct gv_drive *, const char *);
89 #define find_drive() \
90 find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME)
93 main(int argc, char **argv)
96 char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
98 /* Load the module if necessary. */
99 if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
100 err(1, GVINUMMOD ": Kernel module not available");
102 /* Arguments given on the command line. */
106 parseline(argc, argv);
108 /* Interactive mode. */
111 inputline = readline("gvinum -> ");
112 if (inputline == NULL) {
114 err(1, "can't read input");
119 } else if (*inputline) {
120 add_history(inputline);
121 strcpy(buffer, inputline);
123 line++; /* count the lines */
124 tokens = gv_tokenize(buffer, token, GV_MAXARGS);
126 parseline(tokens, token);
133 /* Attach a plex to a volume or a subdisk to a plex. */
135 gvinum_attach(int argc, char * const *argv)
137 struct gctl_req *req;
145 warnx("usage:\tattach <subdisk> <plex> [rename] "
147 "\tattach <plex> <volume> [rename]");
151 if (!strcmp(argv[3], "rename")) {
154 offset = strtol(argv[4], NULL, 0);
156 offset = strtol(argv[3], NULL, 0);
158 req = gctl_get_handle();
159 gctl_ro_param(req, "class", -1, "VINUM");
160 gctl_ro_param(req, "verb", -1, "attach");
161 gctl_ro_param(req, "child", -1, argv[1]);
162 gctl_ro_param(req, "parent", -1, argv[2]);
163 gctl_ro_param(req, "offset", sizeof(off_t), &offset);
164 gctl_ro_param(req, "rename", sizeof(int), &rename);
165 errstr = gctl_issue(req);
167 warnx("attach failed: %s", errstr);
172 gvinum_create(int argc, char * const *argv)
174 struct gctl_req *req;
180 int drives, errors, fd, flags, i, line, plexes, plex_in_volume;
181 int sd_in_plex, status, subdisks, tokens, undeffd, volumes;
183 char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname;
184 char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
185 char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
189 for (i = 1; i < argc; i++) {
190 /* Force flag used to ignore already created drives. */
191 if (!strcmp(argv[i], "-f")) {
193 /* Else it must be a file. */
195 if ((tmp = fopen(argv[i], "r")) == NULL) {
196 warn("can't open '%s' for reading", argv[i]);
202 /* We didn't get a file. */
204 snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
206 if ((fd = mkstemp(tmpfile)) == -1) {
207 warn("temporary file not accessible");
210 if ((tmp = fdopen(fd, "w")) == NULL) {
211 warn("can't open '%s' for writing", tmpfile);
214 printconfig(tmp, "# ");
217 ed = getenv("EDITOR");
221 snprintf(commandline, sizeof(commandline), "%s %s", ed,
223 status = system(commandline);
225 warn("couldn't exec %s; status: %d", ed, status);
229 if ((tmp = fopen(tmpfile, "r")) == NULL) {
230 warn("can't open '%s' for reading", tmpfile);
235 req = gctl_get_handle();
236 gctl_ro_param(req, "class", -1, "VINUM");
237 gctl_ro_param(req, "verb", -1, "create");
238 gctl_ro_param(req, "flags", sizeof(int), &flags);
240 drives = volumes = plexes = subdisks = 0;
241 plex_in_volume = sd_in_plex = undeffd = 0;
245 while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
247 /* Skip empty lines and comments. */
248 if (*buf == '\0' || *buf == '#') {
253 /* Kill off the newline. */
254 buf[strlen(buf) - 1] = '\0';
257 * Copy the original input line in case we need it for error
260 strlcpy(original, buf, sizeof(original));
262 tokens = gv_tokenize(buf, token, GV_MAXARGS);
268 /* Volume definition. */
269 if (!strcmp(token[0], "volume")) {
270 v = gv_new_volume(tokens, token);
272 warnx("line %d: invalid volume definition",
274 warnx("line %d: '%s'", line, original);
280 /* Reset plex count for this volume. */
284 * Set default volume name for following plex
287 strlcpy(volume, v->name, sizeof(volume));
289 snprintf(buf1, sizeof(buf1), "volume%d", volumes);
290 gctl_ro_param(req, buf1, sizeof(*v), v);
293 /* Plex definition. */
294 } else if (!strcmp(token[0], "plex")) {
295 p = gv_new_plex(tokens, token);
297 warnx("line %d: invalid plex definition", line);
298 warnx("line %d: '%s'", line, original);
304 /* Reset subdisk count for this plex. */
308 if (strlen(p->name) == 0) {
309 snprintf(p->name, sizeof(p->name), "%s.p%d",
310 volume, plex_in_volume++);
313 /* Default volume. */
314 if (strlen(p->volume) == 0) {
315 snprintf(p->volume, sizeof(p->volume), "%s",
320 * Set default plex name for following subdisk
323 strlcpy(plex, p->name, sizeof(plex));
325 snprintf(buf1, sizeof(buf1), "plex%d", plexes);
326 gctl_ro_param(req, buf1, sizeof(*p), p);
329 /* Subdisk definition. */
330 } else if (!strcmp(token[0], "sd")) {
331 s = gv_new_sd(tokens, token);
333 warnx("line %d: invalid subdisk "
334 "definition:", line);
335 warnx("line %d: '%s'", line, original);
342 if (strlen(s->name) == 0) {
343 if (strlen(plex) == 0) {
344 sdname = find_name("gvinumsubdisk.p",
345 GV_TYPE_SD, GV_MAXSDNAME);
346 snprintf(s->name, sizeof(s->name),
347 "%s.s%d", sdname, undeffd++);
350 snprintf(s->name, sizeof(s->name),
351 "%s.s%d",plex, sd_in_plex++);
356 if (strlen(s->plex) == 0)
357 snprintf(s->plex, sizeof(s->plex), "%s", plex);
359 snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
360 gctl_ro_param(req, buf1, sizeof(*s), s);
363 /* Subdisk definition. */
364 } else if (!strcmp(token[0], "drive")) {
365 d = gv_new_drive(tokens, token);
367 warnx("line %d: invalid drive definition:",
369 warnx("line %d: '%s'", line, original);
375 snprintf(buf1, sizeof(buf1), "drive%d", drives);
376 gctl_ro_param(req, buf1, sizeof(*d), d);
379 /* Everything else is bogus. */
381 warnx("line %d: invalid definition:", line);
382 warnx("line %d: '%s'", line, original);
391 if (!errors && (volumes || plexes || subdisks || drives)) {
392 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
393 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
394 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
395 gctl_ro_param(req, "drives", sizeof(int), &drives);
396 errstr = gctl_issue(req);
398 warnx("create failed: %s", errstr);
403 /* Create a concatenated volume. */
405 gvinum_concat(int argc, char * const *argv)
409 warnx("usage:\tconcat [-fv] [-n name] drives\n");
412 create_volume(argc, argv, "concat");
415 /* Create a drive quick and dirty. */
417 create_drive(const char *device)
420 struct gctl_req *req;
422 char *drivename, *dname;
423 int drives, i, flags, volumes, subdisks, plexes;
426 flags = plexes = subdisks = volumes = 0;
430 drivename = find_drive();
431 if (drivename == NULL)
434 req = gctl_get_handle();
435 gctl_ro_param(req, "class", -1, "VINUM");
436 gctl_ro_param(req, "verb", -1, "create");
437 d = gv_alloc_drive();
439 err(1, "unable to allocate for gv_drive object");
441 strlcpy(d->name, drivename, sizeof(d->name));
442 copy_device(d, device);
443 gctl_ro_param(req, "drive0", sizeof(*d), d);
444 gctl_ro_param(req, "flags", sizeof(int), &flags);
445 gctl_ro_param(req, "drives", sizeof(int), &drives);
446 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
447 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
448 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
449 errstr = gctl_issue(req);
450 if (errstr != NULL) {
451 warnx("error creating drive: %s", errstr);
454 /* XXX: This is needed because we have to make sure the drives
455 * are created before we return. */
456 /* Loop until it's in the config. */
457 for (i = 0; i < 100000; i++) {
458 dname = find_name("gvinumdrive", GV_TYPE_DRIVE,
460 /* If we got a different name, quit. */
463 if (strcmp(dname, drivename))
469 usleep(100000); /* Sleep for 0.1s */
472 warnx("error creating drive");
481 * General routine for creating a volume. Mainly for use by concat, mirror,
482 * raid5 and stripe commands.
485 create_volume(int argc, char * const *argv, const char *verb)
487 struct gctl_req *req;
489 char buf[BUFSIZ], *drivename, *volname;
490 int drives, flags, i;
498 /* XXX: Should we check for argument length? */
500 req = gctl_get_handle();
501 gctl_ro_param(req, "class", -1, "VINUM");
503 for (i = 1; i < argc; i++) {
504 if (!strcmp(argv[i], "-f")) {
506 } else if (!strcmp(argv[i], "-n")) {
508 } else if (!strcmp(argv[i], "-v")) {
510 } else if (!strcmp(argv[i], "-s")) {
512 if (!strcmp(verb, "raid5"))
513 stripesize = gv_sizespec(argv[++i]);
515 /* Assume it's a drive. */
516 snprintf(buf, sizeof(buf), "drive%d", drives++);
518 /* First we create the drive. */
519 drivename = create_drive(argv[i]);
520 if (drivename == NULL)
522 /* Then we add it to the request. */
523 gctl_ro_param(req, buf, -1, drivename);
527 gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize);
529 /* Find a free volume name. */
531 volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME);
533 /* Then we send a request to actually create the volumes. */
534 gctl_ro_param(req, "verb", -1, verb);
535 gctl_ro_param(req, "flags", sizeof(int), &flags);
536 gctl_ro_param(req, "drives", sizeof(int), &drives);
537 gctl_ro_param(req, "name", -1, volname);
538 errstr = gctl_issue(req);
540 warnx("creating %s volume failed: %s", verb, errstr);
545 /* Parse a line of the config, return the word after <pattern>. */
547 find_pattern(char *line, const char *pattern)
551 ptr = strsep(&line, " ");
552 while (ptr != NULL) {
553 if (!strcmp(ptr, pattern)) {
554 /* Return the next. */
555 ptr = strsep(&line, " ");
558 ptr = strsep(&line, " ");
563 /* Find a free name for an object given a prefix. */
565 find_name(const char *prefix, int type, int namelen)
567 struct gctl_req *req;
568 char comment[1], buf[GV_CFG_LEN - 1], *sname, *ptr;
569 const char *errstr, *name;
570 int i, n, begin, len, conflict;
575 /* Find a name. Fetch out configuration first. */
576 req = gctl_get_handle();
577 gctl_ro_param(req, "class", -1, "VINUM");
578 gctl_ro_param(req, "verb", -1, "getconfig");
579 gctl_ro_param(req, "comment", -1, comment);
580 gctl_rw_param(req, "config", sizeof(buf), buf);
581 errstr = gctl_issue(req);
582 if (errstr != NULL) {
583 warnx("can't get configuration: %s", errstr);
591 sname = malloc(namelen + 1);
593 /* XXX: Max object setting? */
594 for (n = 0; n < 10000; n++) {
595 snprintf(sname, namelen, "%s%d", prefix, n);
598 /* Loop through the configuration line by line. */
599 for (i = 0; i < len; i++) {
600 if (buf[i] == '\n' || buf[i] == '\0') {
602 strlcpy(line, ptr, (i - begin) + 1);
606 name = find_pattern(line, "drive");
609 name = find_pattern(line, "volume");
613 name = find_pattern(line, "name");
616 printf("Invalid type given\n");
621 if (!strcmp(sname, name)) {
623 /* XXX: Could quit the loop earlier. */
635 copy_device(struct gv_drive *d, const char *device)
638 if (strncmp(device, "/dev/", 5) == 0)
639 strlcpy(d->device, (device + 5), sizeof(d->device));
641 strlcpy(d->device, device, sizeof(d->device));
644 /* Detach a plex or subdisk from its parent. */
646 gvinum_detach(int argc, char * const *argv)
649 struct gctl_req *req;
655 while ((i = getopt(argc, argv, "f")) != -1) {
661 warn("invalid flag: %c", i);
668 warnx("usage: detach [-f] <subdisk> | <plex>");
672 req = gctl_get_handle();
673 gctl_ro_param(req, "class", -1, "VINUM");
674 gctl_ro_param(req, "verb", -1, "detach");
675 gctl_ro_param(req, "object", -1, argv[0]);
676 gctl_ro_param(req, "flags", sizeof(int), &flags);
678 errstr = gctl_issue(req);
680 warnx("detach failed: %s", errstr);
689 "checkparity [-f] plex\n"
690 " Check the parity blocks of a RAID-5 plex.\n"
691 "create [-f] description-file\n"
692 " Create as per description-file or open editor.\n"
693 "attach plex volume [rename]\n"
694 "attach subdisk plex [offset] [rename]\n"
695 " Attach a plex to a volume, or a subdisk to a plex\n"
696 "concat [-fv] [-n name] drives\n"
697 " Create a concatenated volume from the specified drives.\n"
698 "detach [-f] [plex | subdisk]\n"
699 " Detach a plex or a subdisk from the volume or plex to\n"
700 " which it is attached.\n"
702 " Grow plex by creating a properly sized subdisk on drive\n"
703 "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
704 " List information about specified objects.\n"
705 "ld [-r] [-v] [-V] [volume]\n"
706 " List information about drives.\n"
707 "ls [-r] [-v] [-V] [subdisk]\n"
708 " List information about subdisks.\n"
709 "lp [-r] [-v] [-V] [plex]\n"
710 " List information about plexes.\n"
711 "lv [-r] [-v] [-V] [volume]\n"
712 " List information about volumes.\n"
713 "mirror [-fsv] [-n name] drives\n"
714 " Create a mirrored volume from the specified drives.\n"
715 "move | mv -f drive object ...\n"
716 " Move the object(s) to the specified drive.\n"
717 "quit Exit the vinum program when running in interactive mode."
719 " mally this would be done by entering the EOF character.\n"
720 "raid5 [-fv] [-s stripesize] [-n name] drives\n"
721 " Create a RAID-5 volume from the specified drives.\n"
722 "rename [-r] [drive | subdisk | plex | volume] newname\n"
723 " Change the name of the specified object.\n"
724 "rebuildparity plex [-f]\n"
725 " Rebuild the parity blocks of a RAID-5 plex.\n"
727 " Reset the complete gvinum configuration\n"
728 "rm [-r] [-f] volume | plex | subdisk | drive\n"
729 " Remove an object.\n"
731 " Save vinum configuration to disk after configuration"
733 "setstate [-f] state [volume | plex | subdisk | drive]\n"
734 " Set state without influencing other objects, for"
737 "start [-S size] volume | plex | subdisk\n"
738 " Allow the system to access the objects.\n"
739 "stripe [-fv] [-n name] drives\n"
740 " Create a striped volume from the specified drives.\n"
745 gvinum_setstate(int argc, char * const *argv)
747 struct gctl_req *req;
756 while ((i = getopt(argc, argv, "f")) != -1) {
763 warn("invalid flag: %c", i);
772 warnx("usage: setstate [-f] <state> <obj>");
777 * XXX: This hack is needed to avoid tripping over (now) invalid
778 * 'classic' vinum states and will go away later.
780 if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
781 strcmp(argv[0], "stale")) {
782 warnx("invalid state '%s'", argv[0]);
786 req = gctl_get_handle();
787 gctl_ro_param(req, "class", -1, "VINUM");
788 gctl_ro_param(req, "verb", -1, "setstate");
789 gctl_ro_param(req, "state", -1, argv[0]);
790 gctl_ro_param(req, "object", -1, argv[1]);
791 gctl_ro_param(req, "flags", sizeof(int), &flags);
793 errstr = gctl_issue(req);
800 gvinum_list(int argc, char * const *argv)
802 struct gctl_req *req;
805 char buf[20], *cmd, config[GV_CFG_LEN + 1];
814 while ((j = getopt(argc, argv, "rsvV")) != -1) {
839 req = gctl_get_handle();
840 gctl_ro_param(req, "class", -1, "VINUM");
841 gctl_ro_param(req, "verb", -1, "list");
842 gctl_ro_param(req, "cmd", -1, cmd);
843 gctl_ro_param(req, "argc", sizeof(int), &argc);
844 gctl_ro_param(req, "flags", sizeof(int), &flags);
845 gctl_rw_param(req, "config", sizeof(config), config);
847 for (i = 0; i < argc; i++) {
848 snprintf(buf, sizeof(buf), "argv%d", i);
849 gctl_ro_param(req, buf, -1, argv[i]);
852 errstr = gctl_issue(req);
853 if (errstr != NULL) {
854 warnx("can't get configuration: %s", errstr);
859 printf("%s", config);
863 /* Create a mirrored volume. */
865 gvinum_mirror(int argc, char * const *argv)
869 warnx("usage\tmirror [-fsv] [-n name] drives\n");
872 create_volume(argc, argv, "mirror");
875 /* Note that move is currently of form '[-r] target object [...]' */
877 gvinum_move(int argc, char * const *argv)
879 struct gctl_req *req;
888 while ((j = getopt(argc, argv, "f")) != -1) {
904 warnx("no destination or object(s) to move specified");
907 warnx("no object(s) to move specified");
913 req = gctl_get_handle();
914 gctl_ro_param(req, "class", -1, "VINUM");
915 gctl_ro_param(req, "verb", -1, "move");
916 gctl_ro_param(req, "argc", sizeof(int), &argc);
917 gctl_ro_param(req, "flags", sizeof(int), &flags);
918 gctl_ro_param(req, "destination", -1, argv[0]);
919 for (i = 1; i < argc; i++) {
920 snprintf(buf, sizeof(buf), "argv%d", i);
921 gctl_ro_param(req, buf, -1, argv[i]);
923 errstr = gctl_issue(req);
925 warnx("can't move object(s): %s", errstr);
930 gvinum_printconfig(int argc, char * const *argv)
933 printconfig(stdout, "");
937 gvinum_parityop(int argc, char * const *argv, int rebuild)
939 struct gctl_req *req;
945 op = "rebuildparity";
953 while ((i = getopt(argc, argv, "fv")) != -1) {
962 warnx("invalid flag '%c'", i);
970 warn("usage: %s [-f] [-v] <plex>", op);
974 req = gctl_get_handle();
975 gctl_ro_param(req, "class", -1, "VINUM");
976 gctl_ro_param(req, "verb", -1, op);
977 gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
978 gctl_ro_param(req, "flags", sizeof(int), &flags);
979 gctl_ro_param(req, "plex", -1, argv[0]);
981 errstr = gctl_issue(req);
983 warnx("%s\n", errstr);
987 /* Create a RAID-5 volume. */
989 gvinum_raid5(int argc, char * const *argv)
993 warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
996 create_volume(argc, argv, "raid5");
1000 gvinum_rename(int argc, char * const *argv)
1002 struct gctl_req *req;
1011 while ((j = getopt(argc, argv, "r")) != -1) {
1026 warnx("no object to rename specified");
1029 warnx("no new name specified");
1034 warnx("more than one new name specified");
1038 req = gctl_get_handle();
1039 gctl_ro_param(req, "class", -1, "VINUM");
1040 gctl_ro_param(req, "verb", -1, "rename");
1041 gctl_ro_param(req, "flags", sizeof(int), &flags);
1042 gctl_ro_param(req, "object", -1, argv[0]);
1043 gctl_ro_param(req, "newname", -1, argv[1]);
1044 errstr = gctl_issue(req);
1046 warnx("can't rename object: %s", errstr);
1051 gvinum_rm(int argc, char * const *argv)
1053 struct gctl_req *req;
1061 while ((j = getopt(argc, argv, "rf")) != -1) {
1076 req = gctl_get_handle();
1077 gctl_ro_param(req, "class", -1, "VINUM");
1078 gctl_ro_param(req, "verb", -1, "remove");
1079 gctl_ro_param(req, "argc", sizeof(int), &argc);
1080 gctl_ro_param(req, "flags", sizeof(int), &flags);
1082 for (i = 0; i < argc; i++) {
1083 snprintf(buf, sizeof(buf), "argv%d", i);
1084 gctl_ro_param(req, buf, -1, argv[i]);
1087 errstr = gctl_issue(req);
1088 if (errstr != NULL) {
1089 warnx("can't remove: %s", errstr);
1097 gvinum_resetconfig(int argc, char * const *argv)
1099 struct gctl_req *req;
1105 while ((i = getopt(argc, argv, "f")) != -1) {
1111 warn("invalid flag: %c", i);
1115 if ((flags & GV_FLAG_F) == 0) {
1116 if (!isatty(STDIN_FILENO)) {
1117 warn("Please enter this command from a tty device\n");
1120 printf(" WARNING! This command will completely wipe out"
1121 " your gvinum configuration.\n"
1122 " All data will be lost. If you really want to do this,"
1123 " enter the text\n\n"
1126 fgets(reply, sizeof(reply), stdin);
1127 if (strcmp(reply, "NO FUTURE\n")) {
1128 printf("\n No change\n");
1132 req = gctl_get_handle();
1133 gctl_ro_param(req, "class", -1, "VINUM");
1134 gctl_ro_param(req, "verb", -1, "resetconfig");
1135 errstr = gctl_issue(req);
1136 if (errstr != NULL) {
1137 warnx("can't reset config: %s", errstr);
1142 printf("gvinum configuration obliterated\n");
1146 gvinum_saveconfig(void)
1148 struct gctl_req *req;
1151 req = gctl_get_handle();
1152 gctl_ro_param(req, "class", -1, "VINUM");
1153 gctl_ro_param(req, "verb", -1, "saveconfig");
1154 errstr = gctl_issue(req);
1156 warnx("can't save configuration: %s", errstr);
1161 gvinum_start(int argc, char * const *argv)
1163 struct gctl_req *req;
1168 /* 'start' with no arguments is a no-op. */
1176 while ((j = getopt(argc, argv, "S")) != -1) {
1179 initsize = atoi(optarg);
1191 req = gctl_get_handle();
1192 gctl_ro_param(req, "class", -1, "VINUM");
1193 gctl_ro_param(req, "verb", -1, "start");
1194 gctl_ro_param(req, "argc", sizeof(int), &argc);
1195 gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1197 for (i = 0; i < argc; i++) {
1198 snprintf(buf, sizeof(buf), "argv%d", i);
1199 gctl_ro_param(req, buf, -1, argv[i]);
1202 errstr = gctl_issue(req);
1203 if (errstr != NULL) {
1204 warnx("can't start: %s", errstr);
1213 gvinum_stop(int argc, char * const *argv)
1217 fileid = kldfind(GVINUMMOD);
1219 warn("cannot find " GVINUMMOD);
1224 * This little hack prevents that we end up in an infinite loop in
1225 * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM
1226 * event thread will be free for the g_wither_geom() call from
1227 * gv_unload(). It's silly, but it works.
1229 printf("unloading " GVINUMMOD " kernel module... ");
1231 if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1233 err = kldunload(fileid);
1236 printf(" failed!\n");
1237 warn("cannot unload " GVINUMMOD);
1245 /* Create a striped volume. */
1247 gvinum_stripe(int argc, char * const *argv)
1251 warnx("usage:\tstripe [-fv] [-n name] drives\n");
1254 create_volume(argc, argv, "stripe");
1257 /* Grow a subdisk by adding disk backed by provider. */
1259 gvinum_grow(int argc, char * const *argv)
1261 struct gctl_req *req;
1262 char *drive, *sdname;
1263 char sdprefix[GV_MAXSDNAME];
1267 int drives, volumes, plexes, subdisks, flags;
1270 drives = volumes = plexes = subdisks = 0;
1272 warnx("usage:\tgrow plex drive\n");
1278 warn("unable to create subdisk");
1281 d = gv_alloc_drive();
1283 warn("unable to create drive");
1287 /* Lookup device and set an appropriate drive name. */
1288 drive = find_drive();
1289 if (drive == NULL) {
1290 warn("unable to find an appropriate drive name");
1295 strlcpy(d->name, drive, sizeof(d->name));
1296 copy_device(d, argv[2]);
1300 /* We try to use the plex name as basis for the subdisk name. */
1301 snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]);
1302 sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME);
1303 if (sdname == NULL) {
1304 warn("unable to find an appropriate subdisk name");
1310 strlcpy(s->name, sdname, sizeof(s->name));
1312 strlcpy(s->plex, argv[1], sizeof(s->plex));
1313 strlcpy(s->drive, d->name, sizeof(s->drive));
1316 req = gctl_get_handle();
1317 gctl_ro_param(req, "class", -1, "VINUM");
1318 gctl_ro_param(req, "verb", -1, "create");
1319 gctl_ro_param(req, "flags", sizeof(int), &flags);
1320 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
1321 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
1322 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
1323 gctl_ro_param(req, "drives", sizeof(int), &drives);
1324 gctl_ro_param(req, "drive0", sizeof(*d), d);
1325 gctl_ro_param(req, "sd0", sizeof(*s), s);
1326 errstr = gctl_issue(req);
1328 if (errstr != NULL) {
1329 warnx("unable to grow plex: %s", errstr);
1338 parseline(int argc, char * const *argv)
1344 if (!strcmp(argv[0], "create"))
1345 gvinum_create(argc, argv);
1346 else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
1348 else if (!strcmp(argv[0], "attach"))
1349 gvinum_attach(argc, argv);
1350 else if (!strcmp(argv[0], "detach"))
1351 gvinum_detach(argc, argv);
1352 else if (!strcmp(argv[0], "concat"))
1353 gvinum_concat(argc, argv);
1354 else if (!strcmp(argv[0], "grow"))
1355 gvinum_grow(argc, argv);
1356 else if (!strcmp(argv[0], "help"))
1358 else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
1359 gvinum_list(argc, argv);
1360 else if (!strcmp(argv[0], "ld"))
1361 gvinum_list(argc, argv);
1362 else if (!strcmp(argv[0], "lp"))
1363 gvinum_list(argc, argv);
1364 else if (!strcmp(argv[0], "ls"))
1365 gvinum_list(argc, argv);
1366 else if (!strcmp(argv[0], "lv"))
1367 gvinum_list(argc, argv);
1368 else if (!strcmp(argv[0], "mirror"))
1369 gvinum_mirror(argc, argv);
1370 else if (!strcmp(argv[0], "move"))
1371 gvinum_move(argc, argv);
1372 else if (!strcmp(argv[0], "mv"))
1373 gvinum_move(argc, argv);
1374 else if (!strcmp(argv[0], "printconfig"))
1375 gvinum_printconfig(argc, argv);
1376 else if (!strcmp(argv[0], "raid5"))
1377 gvinum_raid5(argc, argv);
1378 else if (!strcmp(argv[0], "rename"))
1379 gvinum_rename(argc, argv);
1380 else if (!strcmp(argv[0], "resetconfig"))
1381 gvinum_resetconfig(argc, argv);
1382 else if (!strcmp(argv[0], "rm"))
1383 gvinum_rm(argc, argv);
1384 else if (!strcmp(argv[0], "saveconfig"))
1385 gvinum_saveconfig();
1386 else if (!strcmp(argv[0], "setstate"))
1387 gvinum_setstate(argc, argv);
1388 else if (!strcmp(argv[0], "start"))
1389 gvinum_start(argc, argv);
1390 else if (!strcmp(argv[0], "stop"))
1391 gvinum_stop(argc, argv);
1392 else if (!strcmp(argv[0], "stripe"))
1393 gvinum_stripe(argc, argv);
1394 else if (!strcmp(argv[0], "checkparity"))
1395 gvinum_parityop(argc, argv, 0);
1396 else if (!strcmp(argv[0], "rebuildparity"))
1397 gvinum_parityop(argc, argv, 1);
1399 printf("unknown command '%s'\n", argv[0]);
1403 * The guts of printconfig. This is called from gvinum_printconfig and from
1404 * gvinum_create when called without an argument, in order to give the user
1405 * something to edit.
1408 printconfig(FILE *of, const char *comment)
1410 struct gctl_req *req;
1411 struct utsname uname_s;
1414 char buf[GV_CFG_LEN + 1];
1419 req = gctl_get_handle();
1420 gctl_ro_param(req, "class", -1, "VINUM");
1421 gctl_ro_param(req, "verb", -1, "getconfig");
1422 gctl_ro_param(req, "comment", -1, comment);
1423 gctl_rw_param(req, "config", sizeof(buf), buf);
1424 errstr = gctl_issue(req);
1425 if (errstr != NULL) {
1426 warnx("can't get configuration: %s", errstr);
1431 fprintf(of, "# Vinum configuration of %s, saved at %s",
1435 if (*comment != '\0')
1436 fprintf(of, "# Current configuration:\n");
1438 fprintf(of, "%s", buf);