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>
54 #include <readline/readline.h>
55 #include <readline/history.h>
60 void gvinum_attach(int, char **);
61 void gvinum_concat(int, char **);
62 void gvinum_create(int, char **);
63 void gvinum_detach(int, char **);
64 void gvinum_grow(int, char **);
65 void gvinum_help(void);
66 void gvinum_list(int, char **);
67 void gvinum_move(int, char **);
68 void gvinum_mirror(int, char **);
69 void gvinum_parityop(int, char **, int);
70 void gvinum_printconfig(int, char **);
71 void gvinum_raid5(int, char **);
72 void gvinum_rename(int, char **);
73 void gvinum_resetconfig(void);
74 void gvinum_rm(int, char **);
75 void gvinum_saveconfig(void);
76 void gvinum_setstate(int, char **);
77 void gvinum_start(int, char **);
78 void gvinum_stop(int, char **);
79 void gvinum_stripe(int, char **);
80 void parseline(int, char **);
81 void printconfig(FILE *, char *);
83 char *create_drive(char *);
84 void create_volume(int, char **, char *);
85 char *find_name(const char *, int, int);
86 char *find_drive(const char *);
87 char *find_pattern(char *, char *);
90 main(int argc, char **argv)
93 char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
95 /* Load the module if necessary. */
96 if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
97 err(1, GVINUMMOD ": Kernel module not available");
99 /* Arguments given on the command line. */
103 parseline(argc, argv);
105 /* Interactive mode. */
108 inputline = readline("gvinum -> ");
109 if (inputline == NULL) {
111 err(1, "can't read input");
116 } else if (*inputline) {
117 add_history(inputline);
118 strcpy(buffer, inputline);
120 line++; /* count the lines */
121 tokens = gv_tokenize(buffer, token, GV_MAXARGS);
123 parseline(tokens, token);
130 /* Attach a plex to a volume or a subdisk to a plex. */
132 gvinum_attach(int argc, char **argv)
134 struct gctl_req *req;
142 warnx("usage:\tattach <subdisk> <plex> [rename] "
144 "\tattach <plex> <volume> [rename]");
148 if (!strcmp(argv[3], "rename")) {
151 offset = strtol(argv[4], NULL, 0);
153 offset = strtol(argv[3], NULL, 0);
155 req = gctl_get_handle();
156 gctl_ro_param(req, "class", -1, "VINUM");
157 gctl_ro_param(req, "verb", -1, "attach");
158 gctl_ro_param(req, "child", -1, argv[1]);
159 gctl_ro_param(req, "parent", -1, argv[2]);
160 gctl_ro_param(req, "offset", sizeof(off_t), &offset);
161 gctl_ro_param(req, "rename", sizeof(int), &rename);
162 errstr = gctl_issue(req);
164 warnx("attach failed: %s", errstr);
169 gvinum_create(int argc, char **argv)
171 struct gctl_req *req;
177 int drives, errors, fd, flags, i, line, plexes, plex_in_volume;
178 int sd_in_plex, status, subdisks, tokens, undeffd, volumes;
180 char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname;
181 char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
182 char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
186 for (i = 1; i < argc; i++) {
187 /* Force flag used to ignore already created drives. */
188 if (!strcmp(argv[i], "-f")) {
190 /* Else it must be a file. */
192 if ((tmp = fopen(argv[1], "r")) == NULL) {
193 warn("can't open '%s' for reading", argv[1]);
199 /* We didn't get a file. */
201 snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
203 if ((fd = mkstemp(tmpfile)) == -1) {
204 warn("temporary file not accessible");
207 if ((tmp = fdopen(fd, "w")) == NULL) {
208 warn("can't open '%s' for writing", tmpfile);
211 printconfig(tmp, "# ");
214 ed = getenv("EDITOR");
218 snprintf(commandline, sizeof(commandline), "%s %s", ed,
220 status = system(commandline);
222 warn("couldn't exec %s; status: %d", ed, status);
226 if ((tmp = fopen(tmpfile, "r")) == NULL) {
227 warn("can't open '%s' for reading", tmpfile);
232 req = gctl_get_handle();
233 gctl_ro_param(req, "class", -1, "VINUM");
234 gctl_ro_param(req, "verb", -1, "create");
235 gctl_ro_param(req, "flags", sizeof(int), &flags);
237 drives = volumes = plexes = subdisks = 0;
238 plex_in_volume = sd_in_plex = undeffd = 0;
242 while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
244 /* Skip empty lines and comments. */
245 if (*buf == '\0' || *buf == '#') {
250 /* Kill off the newline. */
251 buf[strlen(buf) - 1] = '\0';
254 * Copy the original input line in case we need it for error
257 strlcpy(original, buf, sizeof(original));
259 tokens = gv_tokenize(buf, token, GV_MAXARGS);
265 /* Volume definition. */
266 if (!strcmp(token[0], "volume")) {
267 v = gv_new_volume(tokens, token);
269 warnx("line %d: invalid volume definition",
271 warnx("line %d: '%s'", line, original);
277 /* Reset plex count for this volume. */
281 * Set default volume name for following plex
284 strlcpy(volume, v->name, sizeof(volume));
286 snprintf(buf1, sizeof(buf1), "volume%d", volumes);
287 gctl_ro_param(req, buf1, sizeof(*v), v);
290 /* Plex definition. */
291 } else if (!strcmp(token[0], "plex")) {
292 p = gv_new_plex(tokens, token);
294 warnx("line %d: invalid plex definition", line);
295 warnx("line %d: '%s'", line, original);
301 /* Reset subdisk count for this plex. */
305 if (strlen(p->name) == 0) {
306 snprintf(p->name, sizeof(p->name), "%s.p%d",
307 volume, plex_in_volume++);
310 /* Default volume. */
311 if (strlen(p->volume) == 0) {
312 snprintf(p->volume, sizeof(p->volume), "%s",
317 * Set default plex name for following subdisk
320 strlcpy(plex, p->name, sizeof(plex));
322 snprintf(buf1, sizeof(buf1), "plex%d", plexes);
323 gctl_ro_param(req, buf1, sizeof(*p), p);
326 /* Subdisk definition. */
327 } else if (!strcmp(token[0], "sd")) {
328 s = gv_new_sd(tokens, token);
330 warnx("line %d: invalid subdisk "
331 "definition:", line);
332 warnx("line %d: '%s'", line, original);
339 if (strlen(s->name) == 0) {
340 if (strlen(plex) == 0) {
341 sdname = find_name("gvinumsubdisk.p",
342 GV_TYPE_SD, GV_MAXSDNAME);
343 snprintf(s->name, sizeof(s->name),
344 "%s.s%d", sdname, undeffd++);
347 snprintf(s->name, sizeof(s->name),
348 "%s.s%d",plex, sd_in_plex++);
353 if (strlen(s->plex) == 0)
354 snprintf(s->plex, sizeof(s->plex), "%s", plex);
356 snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
357 gctl_ro_param(req, buf1, sizeof(*s), s);
360 /* Subdisk definition. */
361 } else if (!strcmp(token[0], "drive")) {
362 d = gv_new_drive(tokens, token);
364 warnx("line %d: invalid drive definition:",
366 warnx("line %d: '%s'", line, original);
372 snprintf(buf1, sizeof(buf1), "drive%d", drives);
373 gctl_ro_param(req, buf1, sizeof(*d), d);
376 /* Everything else is bogus. */
378 warnx("line %d: invalid definition:", line);
379 warnx("line %d: '%s'", line, original);
388 if (!errors && (volumes || plexes || subdisks || drives)) {
389 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
390 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
391 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
392 gctl_ro_param(req, "drives", sizeof(int), &drives);
393 errstr = gctl_issue(req);
395 warnx("create failed: %s", errstr);
400 /* Create a concatenated volume. */
402 gvinum_concat(int argc, char **argv)
406 warnx("usage:\tconcat [-fv] [-n name] drives\n");
409 create_volume(argc, argv, "concat");
413 /* Create a drive quick and dirty. */
415 create_drive(char *device)
418 struct gctl_req *req;
420 char *drivename, *dname;
421 int drives, i, flags, volumes, subdisks, plexes;
423 flags = plexes = subdisks = volumes = 0;
427 drivename = find_drive(device);
428 if (drivename == NULL)
431 req = gctl_get_handle();
432 gctl_ro_param(req, "class", -1, "VINUM");
433 gctl_ro_param(req, "verb", -1, "create");
434 d = gv_alloc_drive();
436 err(1, "unable to allocate for gv_drive object");
438 strlcpy(d->name, drivename, sizeof(d->name));
439 strlcpy(d->device, device, sizeof(d->device));
440 gctl_ro_param(req, "drive0", sizeof(*d), d);
441 gctl_ro_param(req, "flags", sizeof(int), &flags);
442 gctl_ro_param(req, "drives", sizeof(int), &drives);
443 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
444 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
445 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
446 errstr = gctl_issue(req);
447 if (errstr != NULL) {
448 warnx("error creating drive: %s", errstr);
453 /* XXX: This is needed because we have to make sure the drives
454 * are created before we return. */
455 /* Loop until it's in the config. */
456 for (i = 0; i < 100000; i++) {
457 dname = find_name("gvinumdrive", GV_TYPE_DRIVE,
459 /* If we got a different name, quit. */
462 if (strcmp(dname, drivename)) {
468 usleep(100000); /* Sleep for 0.1s */
476 * General routine for creating a volume. Mainly for use by concat, mirror,
477 * raid5 and stripe commands.
480 create_volume(int argc, char **argv, char *verb)
482 struct gctl_req *req;
484 char buf[BUFSIZ], *drivename, *volname;
485 int drives, flags, i;
493 /* XXX: Should we check for argument length? */
495 req = gctl_get_handle();
496 gctl_ro_param(req, "class", -1, "VINUM");
498 for (i = 1; i < argc; i++) {
499 if (!strcmp(argv[i], "-f")) {
501 } else if (!strcmp(argv[i], "-n")) {
503 } else if (!strcmp(argv[i], "-v")) {
505 } else if (!strcmp(argv[i], "-s")) {
507 if (!strcmp(verb, "raid5"))
508 stripesize = gv_sizespec(argv[++i]);
510 /* Assume it's a drive. */
511 snprintf(buf, sizeof(buf), "drive%d", drives++);
513 /* First we create the drive. */
514 drivename = create_drive(argv[i]);
515 if (drivename == NULL)
517 /* Then we add it to the request. */
518 gctl_ro_param(req, buf, -1, drivename);
522 gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize);
524 /* Find a free volume name. */
526 volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME);
528 /* Then we send a request to actually create the volumes. */
529 gctl_ro_param(req, "verb", -1, verb);
530 gctl_ro_param(req, "flags", sizeof(int), &flags);
531 gctl_ro_param(req, "drives", sizeof(int), &drives);
532 gctl_ro_param(req, "name", -1, volname);
533 errstr = gctl_issue(req);
535 warnx("creating %s volume failed: %s", verb, errstr);
540 /* Parse a line of the config, return the word after <pattern>. */
542 find_pattern(char *line, char *pattern)
546 ptr = strsep(&line, " ");
547 while (ptr != NULL) {
548 if (!strcmp(ptr, pattern)) {
549 /* Return the next. */
550 ptr = strsep(&line, " ");
553 ptr = strsep(&line, " ");
558 /* Find a free name for an object given a a prefix. */
560 find_name(const char *prefix, int type, int namelen)
562 struct gctl_req *req;
563 char comment[1], buf[GV_CFG_LEN - 1], *name, *sname, *ptr;
565 int i, n, begin, len, conflict;
570 /* Find a name. Fetch out configuration first. */
571 req = gctl_get_handle();
572 gctl_ro_param(req, "class", -1, "VINUM");
573 gctl_ro_param(req, "verb", -1, "getconfig");
574 gctl_ro_param(req, "comment", -1, comment);
575 gctl_rw_param(req, "config", sizeof(buf), buf);
576 errstr = gctl_issue(req);
577 if (errstr != NULL) {
578 warnx("can't get configuration: %s", errstr);
586 sname = malloc(namelen + 1);
588 /* XXX: Max object setting? */
589 for (n = 0; n < 10000; n++) {
590 snprintf(sname, namelen, "%s%d", prefix, n);
593 /* Loop through the configuration line by line. */
594 for (i = 0; i < len; i++) {
595 if (buf[i] == '\n' || buf[i] == '\0') {
597 strlcpy(line, ptr, (i - begin) + 1);
601 name = find_pattern(line, "drive");
604 name = find_pattern(line, "volume");
608 name = find_pattern(line, "name");
611 printf("Invalid type given\n");
616 if (!strcmp(sname, name)) {
618 /* XXX: Could quit the loop earlier. */
630 find_drive(const char *device)
633 /* Strip possible /dev/ in front. */
634 if (strncmp(device, "/dev/", 5) == 0)
636 return (find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME));
639 /* Detach a plex or subdisk from its parent. */
641 gvinum_detach(int argc, char **argv)
644 struct gctl_req *req;
649 while ((i = getopt(argc, argv, "f")) != -1) {
655 warn("invalid flag: %c", i);
662 warnx("usage: detach [-f] <subdisk> | <plex>");
666 req = gctl_get_handle();
667 gctl_ro_param(req, "class", -1, "VINUM");
668 gctl_ro_param(req, "verb", -1, "detach");
669 gctl_ro_param(req, "object", -1, argv[0]);
670 gctl_ro_param(req, "flags", sizeof(int), &flags);
672 errstr = gctl_issue(req);
674 warnx("detach failed: %s", errstr);
682 "checkparity [-f] plex\n"
683 " Check the parity blocks of a RAID-5 plex.\n"
684 "create [-f] description-file\n"
685 " Create as per description-file or open editor.\n"
686 "attach plex volume [rename]\n"
687 "attach subdisk plex [offset] [rename]\n"
688 " Attach a plex to a volume, or a subdisk to a plex\n"
689 "concat [-fv] [-n name] drives\n"
690 " Create a concatenated volume from the specified drives.\n"
691 "detach [-f] [plex | subdisk]\n"
692 " Detach a plex or a subdisk from the volume or plex to\n"
693 " which it is attached.\n"
695 " Grow plex by creating a properly sized subdisk on drive\n"
696 "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
697 " List information about specified objects.\n"
698 "ld [-r] [-v] [-V] [volume]\n"
699 " List information about drives.\n"
700 "ls [-r] [-v] [-V] [subdisk]\n"
701 " List information about subdisks.\n"
702 "lp [-r] [-v] [-V] [plex]\n"
703 " List information about plexes.\n"
704 "lv [-r] [-v] [-V] [volume]\n"
705 " List information about volumes.\n"
706 "mirror [-fsv] [-n name] drives\n"
707 " Create a mirrored volume from the specified drives.\n"
708 "move | mv -f drive object ...\n"
709 " Move the object(s) to the specified drive.\n"
710 "quit Exit the vinum program when running in interactive mode."
712 " mally this would be done by entering the EOF character.\n"
713 "raid5 [-fv] [-s stripesize] [-n name] drives\n"
714 " Create a RAID-5 volume from the specified drives.\n"
715 "rename [-r] [drive | subdisk | plex | volume] newname\n"
716 " Change the name of the specified object.\n"
717 "rebuildparity plex [-f]\n"
718 " Rebuild the parity blocks of a RAID-5 plex.\n"
720 " Reset the complete gvinum configuration\n"
721 "rm [-r] [-f] volume | plex | subdisk | drive\n"
722 " Remove an object.\n"
724 " Save vinum configuration to disk after configuration"
726 "setstate [-f] state [volume | plex | subdisk | drive]\n"
727 " Set state without influencing other objects, for"
730 "start [-S size] volume | plex | subdisk\n"
731 " Allow the system to access the objects.\n"
732 "stripe [-fv] [-n name] drives\n"
733 " Create a striped volume from the specified drives.\n"
740 gvinum_setstate(int argc, char **argv)
742 struct gctl_req *req;
751 while ((i = getopt(argc, argv, "f")) != -1) {
758 warn("invalid flag: %c", i);
767 warnx("usage: setstate [-f] <state> <obj>");
772 * XXX: This hack is needed to avoid tripping over (now) invalid
773 * 'classic' vinum states and will go away later.
775 if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
776 strcmp(argv[0], "stale")) {
777 warnx("invalid state '%s'", argv[0]);
781 req = gctl_get_handle();
782 gctl_ro_param(req, "class", -1, "VINUM");
783 gctl_ro_param(req, "verb", -1, "setstate");
784 gctl_ro_param(req, "state", -1, argv[0]);
785 gctl_ro_param(req, "object", -1, argv[1]);
786 gctl_ro_param(req, "flags", sizeof(int), &flags);
788 errstr = gctl_issue(req);
795 gvinum_list(int argc, char **argv)
797 struct gctl_req *req;
800 char buf[20], *cmd, config[GV_CFG_LEN + 1];
809 while ((j = getopt(argc, argv, "rsvV")) != -1) {
834 req = gctl_get_handle();
835 gctl_ro_param(req, "class", -1, "VINUM");
836 gctl_ro_param(req, "verb", -1, "list");
837 gctl_ro_param(req, "cmd", -1, cmd);
838 gctl_ro_param(req, "argc", sizeof(int), &argc);
839 gctl_ro_param(req, "flags", sizeof(int), &flags);
840 gctl_rw_param(req, "config", sizeof(config), config);
842 for (i = 0; i < argc; i++) {
843 snprintf(buf, sizeof(buf), "argv%d", i);
844 gctl_ro_param(req, buf, -1, argv[i]);
847 errstr = gctl_issue(req);
848 if (errstr != NULL) {
849 warnx("can't get configuration: %s", errstr);
854 printf("%s", config);
859 /* Create a mirrored volume. */
861 gvinum_mirror(int argc, char **argv)
865 warnx("usage\tmirror [-fsv] [-n name] drives\n");
868 create_volume(argc, argv, "mirror");
871 /* Note that move is currently of form '[-r] target object [...]' */
873 gvinum_move(int argc, char **argv)
875 struct gctl_req *req;
884 while ((j = getopt(argc, argv, "f")) != -1) {
900 warnx("no destination or object(s) to move specified");
903 warnx("no object(s) to move specified");
909 req = gctl_get_handle();
910 gctl_ro_param(req, "class", -1, "VINUM");
911 gctl_ro_param(req, "verb", -1, "move");
912 gctl_ro_param(req, "argc", sizeof(int), &argc);
913 gctl_ro_param(req, "flags", sizeof(int), &flags);
914 gctl_ro_param(req, "destination", -1, argv[0]);
915 for (i = 1; i < argc; i++) {
916 snprintf(buf, sizeof(buf), "argv%d", i);
917 gctl_ro_param(req, buf, -1, argv[i]);
919 errstr = gctl_issue(req);
921 warnx("can't move object(s): %s", errstr);
927 gvinum_printconfig(int argc, char **argv)
929 printconfig(stdout, "");
933 gvinum_parityop(int argc, char **argv, int rebuild)
935 struct gctl_req *req;
941 op = "rebuildparity";
951 while ((i = getopt(argc, argv, "fv")) != -1) {
961 warnx("invalid flag '%c'", i);
969 warn("usage: %s [-f] [-v] <plex>", op);
973 req = gctl_get_handle();
974 gctl_ro_param(req, "class", -1, "VINUM");
975 gctl_ro_param(req, "verb", -1, op);
976 gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
977 gctl_ro_param(req, "flags", sizeof(int), &flags);
978 gctl_ro_param(req, "plex", -1, argv[0]);
980 errstr = gctl_issue(req);
982 warnx("%s\n", errstr);
986 /* Create a RAID-5 volume. */
988 gvinum_raid5(int argc, char **argv)
992 warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
995 create_volume(argc, argv, "raid5");
1000 gvinum_rename(int argc, char **argv)
1002 struct gctl_req *req;
1011 while ((j = getopt(argc, argv, "r")) != -1) {
1027 warnx("no object to rename specified");
1030 warnx("no new name specified");
1035 warnx("more than one new name specified");
1039 req = gctl_get_handle();
1040 gctl_ro_param(req, "class", -1, "VINUM");
1041 gctl_ro_param(req, "verb", -1, "rename");
1042 gctl_ro_param(req, "flags", sizeof(int), &flags);
1043 gctl_ro_param(req, "object", -1, argv[0]);
1044 gctl_ro_param(req, "newname", -1, argv[1]);
1045 errstr = gctl_issue(req);
1047 warnx("can't rename object: %s", errstr);
1053 gvinum_rm(int argc, char **argv)
1055 struct gctl_req *req;
1064 while ((j = getopt(argc, argv, "rf")) != -1) {
1080 req = gctl_get_handle();
1081 gctl_ro_param(req, "class", -1, "VINUM");
1082 gctl_ro_param(req, "verb", -1, "remove");
1083 gctl_ro_param(req, "argc", sizeof(int), &argc);
1084 gctl_ro_param(req, "flags", sizeof(int), &flags);
1086 for (i = 0; i < argc; i++) {
1087 snprintf(buf, sizeof(buf), "argv%d", i);
1088 gctl_ro_param(req, buf, -1, argv[i]);
1091 errstr = gctl_issue(req);
1092 if (errstr != NULL) {
1093 warnx("can't remove: %s", errstr);
1101 gvinum_resetconfig(void)
1103 struct gctl_req *req;
1107 if (!isatty(STDIN_FILENO)) {
1108 warn("Please enter this command from a tty device\n");
1111 printf(" WARNING! This command will completely wipe out your gvinum"
1113 " All data will be lost. If you really want to do this,"
1114 " enter the text\n\n"
1117 fgets(reply, sizeof(reply), stdin);
1118 if (strcmp(reply, "NO FUTURE\n")) {
1119 printf("\n No change\n");
1122 req = gctl_get_handle();
1123 gctl_ro_param(req, "class", -1, "VINUM");
1124 gctl_ro_param(req, "verb", -1, "resetconfig");
1125 errstr = gctl_issue(req);
1126 if (errstr != NULL) {
1127 warnx("can't reset config: %s", errstr);
1132 printf("gvinum configuration obliterated\n");
1136 gvinum_saveconfig(void)
1138 struct gctl_req *req;
1141 req = gctl_get_handle();
1142 gctl_ro_param(req, "class", -1, "VINUM");
1143 gctl_ro_param(req, "verb", -1, "saveconfig");
1144 errstr = gctl_issue(req);
1146 warnx("can't save configuration: %s", errstr);
1151 gvinum_start(int argc, char **argv)
1153 struct gctl_req *req;
1158 /* 'start' with no arguments is a no-op. */
1166 while ((j = getopt(argc, argv, "S")) != -1) {
1169 initsize = atoi(optarg);
1182 req = gctl_get_handle();
1183 gctl_ro_param(req, "class", -1, "VINUM");
1184 gctl_ro_param(req, "verb", -1, "start");
1185 gctl_ro_param(req, "argc", sizeof(int), &argc);
1186 gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1188 for (i = 0; i < argc; i++) {
1189 snprintf(buf, sizeof(buf), "argv%d", i);
1190 gctl_ro_param(req, buf, -1, argv[i]);
1193 errstr = gctl_issue(req);
1194 if (errstr != NULL) {
1195 warnx("can't start: %s", errstr);
1204 gvinum_stop(int argc, char **argv)
1208 fileid = kldfind(GVINUMMOD);
1210 warn("cannot find " GVINUMMOD);
1215 * This little hack prevents that we end up in an infinite loop in
1216 * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM
1217 * event thread will be free for the g_wither_geom() call from
1218 * gv_unload(). It's silly, but it works.
1220 printf("unloading " GVINUMMOD " kernel module... ");
1222 if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1224 err = kldunload(fileid);
1227 printf(" failed!\n");
1228 warn("cannot unload " GVINUMMOD);
1236 /* Create a striped volume. */
1238 gvinum_stripe(int argc, char **argv)
1242 warnx("usage:\tstripe [-fv] [-n name] drives\n");
1245 create_volume(argc, argv, "stripe");
1248 /* Grow a subdisk by adding disk backed by provider. */
1250 gvinum_grow(int argc, char **argv)
1252 struct gctl_req *req;
1253 char *drive, *sdname;
1254 char sdprefix[GV_MAXSDNAME];
1258 int drives, volumes, plexes, subdisks, flags;
1260 drives = volumes = plexes = subdisks = 0;
1262 warnx("usage:\tgrow plex drive\n");
1268 warn("unable to create subdisk");
1271 d = gv_alloc_drive();
1273 warn("unable to create drive");
1277 /* Lookup device and set an appropriate drive name. */
1278 drive = find_drive(argv[2]);
1279 if (drive == NULL) {
1280 warn("unable to find an appropriate drive name");
1285 strlcpy(d->name, drive, sizeof(d->name));
1286 if (strncmp(argv[2], "/dev/", 5) == 0)
1287 strlcpy(d->device, (argv[2] + 5), sizeof(d->device));
1289 strlcpy(d->device, argv[2], sizeof(d->device));
1292 /* We try to use the plex name as basis for the subdisk name. */
1293 snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]);
1294 sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME);
1295 if (sdname == NULL) {
1296 warn("unable to find an appropriate subdisk name");
1302 strlcpy(s->name, sdname, sizeof(s->name));
1304 strlcpy(s->plex, argv[1], sizeof(s->plex));
1305 strlcpy(s->drive, d->name, sizeof(s->drive));
1308 req = gctl_get_handle();
1309 gctl_ro_param(req, "class", -1, "VINUM");
1310 gctl_ro_param(req, "verb", -1, "create");
1311 gctl_ro_param(req, "flags", sizeof(int), &flags);
1312 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
1313 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
1314 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
1315 gctl_ro_param(req, "drives", sizeof(int), &drives);
1316 gctl_ro_param(req, "drive0", sizeof(*d), d);
1317 gctl_ro_param(req, "sd0", sizeof(*s), s);
1318 errstr = gctl_issue(req);
1320 if (errstr != NULL) {
1321 warnx("unable to grow plex: %s", errstr);
1330 parseline(int argc, char **argv)
1335 if (!strcmp(argv[0], "create"))
1336 gvinum_create(argc, argv);
1337 else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
1339 else if (!strcmp(argv[0], "attach"))
1340 gvinum_attach(argc, argv);
1341 else if (!strcmp(argv[0], "detach"))
1342 gvinum_detach(argc, argv);
1343 else if (!strcmp(argv[0], "concat"))
1344 gvinum_concat(argc, argv);
1345 else if (!strcmp(argv[0], "grow"))
1346 gvinum_grow(argc, argv);
1347 else if (!strcmp(argv[0], "help"))
1349 else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
1350 gvinum_list(argc, argv);
1351 else if (!strcmp(argv[0], "ld"))
1352 gvinum_list(argc, argv);
1353 else if (!strcmp(argv[0], "lp"))
1354 gvinum_list(argc, argv);
1355 else if (!strcmp(argv[0], "ls"))
1356 gvinum_list(argc, argv);
1357 else if (!strcmp(argv[0], "lv"))
1358 gvinum_list(argc, argv);
1359 else if (!strcmp(argv[0], "mirror"))
1360 gvinum_mirror(argc, argv);
1361 else if (!strcmp(argv[0], "move"))
1362 gvinum_move(argc, argv);
1363 else if (!strcmp(argv[0], "mv"))
1364 gvinum_move(argc, argv);
1365 else if (!strcmp(argv[0], "printconfig"))
1366 gvinum_printconfig(argc, argv);
1367 else if (!strcmp(argv[0], "raid5"))
1368 gvinum_raid5(argc, argv);
1369 else if (!strcmp(argv[0], "rename"))
1370 gvinum_rename(argc, argv);
1371 else if (!strcmp(argv[0], "resetconfig"))
1372 gvinum_resetconfig();
1373 else if (!strcmp(argv[0], "rm"))
1374 gvinum_rm(argc, argv);
1375 else if (!strcmp(argv[0], "saveconfig"))
1376 gvinum_saveconfig();
1377 else if (!strcmp(argv[0], "setstate"))
1378 gvinum_setstate(argc, argv);
1379 else if (!strcmp(argv[0], "start"))
1380 gvinum_start(argc, argv);
1381 else if (!strcmp(argv[0], "stop"))
1382 gvinum_stop(argc, argv);
1383 else if (!strcmp(argv[0], "stripe"))
1384 gvinum_stripe(argc, argv);
1385 else if (!strcmp(argv[0], "checkparity"))
1386 gvinum_parityop(argc, argv, 0);
1387 else if (!strcmp(argv[0], "rebuildparity"))
1388 gvinum_parityop(argc, argv, 1);
1390 printf("unknown command '%s'\n", argv[0]);
1396 * The guts of printconfig. This is called from gvinum_printconfig and from
1397 * gvinum_create when called without an argument, in order to give the user
1398 * something to edit.
1401 printconfig(FILE *of, char *comment)
1403 struct gctl_req *req;
1404 struct utsname uname_s;
1407 char buf[GV_CFG_LEN + 1];
1412 req = gctl_get_handle();
1413 gctl_ro_param(req, "class", -1, "VINUM");
1414 gctl_ro_param(req, "verb", -1, "getconfig");
1415 gctl_ro_param(req, "comment", -1, comment);
1416 gctl_rw_param(req, "config", sizeof(buf), buf);
1417 errstr = gctl_issue(req);
1418 if (errstr != NULL) {
1419 warnx("can't get configuration: %s", errstr);
1424 fprintf(of, "# Vinum configuration of %s, saved at %s",
1428 if (*comment != '\0')
1429 fprintf(of, "# Current configuration:\n");