2 * Copyright (c) 2004 Lukas Ertl, 2005 Chris Jones
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.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
33 #include <sys/param.h>
34 #include <sys/linker.h>
36 #include <sys/module.h>
37 #include <sys/mutex.h>
38 #include <sys/queue.h>
39 #include <sys/utsname.h>
41 #include <geom/vinum/geom_vinum_var.h>
42 #include <geom/vinum/geom_vinum_share.h>
51 #include <readline/readline.h>
52 #include <readline/history.h>
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 *);
74 main(int argc, char **argv)
77 char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
79 /* Load the module if necessary. */
80 if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
81 err(1, GVINUMMOD ": Kernel module not available");
83 /* Arguments given on the command line. */
87 parseline(argc, argv);
89 /* Interactive mode. */
92 inputline = readline("gvinum -> ");
93 if (inputline == NULL) {
95 err(1, "can't read input");
100 } else if (*inputline) {
101 add_history(inputline);
102 strcpy(buffer, inputline);
104 line++; /* count the lines */
105 tokens = gv_tokenize(buffer, token, GV_MAXARGS);
107 parseline(tokens, token);
115 gvinum_create(int argc, char **argv)
117 struct gctl_req *req;
123 int drives, errors, fd, line, plexes, plex_in_volume;
124 int sd_in_plex, status, subdisks, tokens, volumes;
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];
131 if ((tmp = fopen(argv[1], "r")) == NULL) {
132 warn("can't open '%s' for reading", argv[1]);
136 snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
138 if ((fd = mkstemp(tmpfile)) == -1) {
139 warn("temporary file not accessible");
142 if ((tmp = fdopen(fd, "w")) == NULL) {
143 warn("can't open '%s' for writing", tmpfile);
146 printconfig(tmp, "# ");
149 ed = getenv("EDITOR");
153 snprintf(commandline, sizeof(commandline), "%s %s", ed,
155 status = system(commandline);
157 warn("couldn't exec %s; status: %d", ed, status);
161 if ((tmp = fopen(tmpfile, "r")) == NULL) {
162 warn("can't open '%s' for reading", tmpfile);
167 req = gctl_get_handle();
168 gctl_ro_param(req, "class", -1, "VINUM");
169 gctl_ro_param(req, "verb", -1, "create");
171 drives = volumes = plexes = subdisks = 0;
172 plex_in_volume = sd_in_plex = 0;
175 while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
177 /* Skip empty lines and comments. */
178 if (*buf == '\0' || *buf == '#') {
183 /* Kill off the newline. */
184 buf[strlen(buf) - 1] = '\0';
187 * Copy the original input line in case we need it for error
190 strncpy(original, buf, sizeof(buf));
192 tokens = gv_tokenize(buf, token, GV_MAXARGS);
198 /* Volume definition. */
199 if (!strcmp(token[0], "volume")) {
200 v = gv_new_volume(tokens, token);
202 warnx("line %d: invalid volume definition",
204 warnx("line %d: '%s'", line, original);
210 /* Reset plex count for this volume. */
214 * Set default volume name for following plex
217 strncpy(volume, v->name, sizeof(volume));
219 snprintf(buf1, sizeof(buf1), "volume%d", volumes);
220 gctl_ro_param(req, buf1, sizeof(*v), v);
223 /* Plex definition. */
224 } else if (!strcmp(token[0], "plex")) {
225 p = gv_new_plex(tokens, token);
227 warnx("line %d: invalid plex definition", line);
228 warnx("line %d: '%s'", line, original);
234 /* Reset subdisk count for this plex. */
238 if (strlen(p->name) == 0) {
239 snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d",
240 volume, plex_in_volume++);
243 /* Default volume. */
244 if (strlen(p->volume) == 0) {
245 snprintf(p->volume, GV_MAXVOLNAME, "%s",
250 * Set default plex name for following subdisk
253 strncpy(plex, p->name, GV_MAXPLEXNAME);
255 snprintf(buf1, sizeof(buf1), "plex%d", plexes);
256 gctl_ro_param(req, buf1, sizeof(*p), p);
259 /* Subdisk definition. */
260 } else if (!strcmp(token[0], "sd")) {
261 s = gv_new_sd(tokens, token);
263 warnx("line %d: invalid subdisk "
264 "definition:", line);
265 warnx("line %d: '%s'", line, original);
272 if (strlen(s->name) == 0) {
273 snprintf(s->name, GV_MAXSDNAME, "%s.s%d",
278 if (strlen(s->plex) == 0)
279 snprintf(s->plex, GV_MAXPLEXNAME, "%s", plex);
281 snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
282 gctl_ro_param(req, buf1, sizeof(*s), s);
285 /* Subdisk definition. */
286 } else if (!strcmp(token[0], "drive")) {
287 d = gv_new_drive(tokens, token);
289 warnx("line %d: invalid drive definition:",
291 warnx("line %d: '%s'", line, original);
297 snprintf(buf1, sizeof(buf1), "drive%d", drives);
298 gctl_ro_param(req, buf1, sizeof(*d), d);
301 /* Everything else is bogus. */
303 warnx("line %d: invalid definition:", line);
304 warnx("line %d: '%s'", line, original);
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);
320 warnx("create failed: %s", errstr);
323 gvinum_list(0, NULL);
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."
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"
354 " Reset the complete gvinum configuration\n"
355 "rm [-r] volume | plex | subdisk | drive\n"
356 " Remove an object.\n"
358 " Save vinum configuration to disk after configuration"
360 "setstate [-f] state [volume | plex | subdisk | drive]\n"
361 " Set state without influencing other objects, for"
364 "start [-S size] volume | plex | subdisk\n"
365 " Allow the system to access the objects.\n"
372 gvinum_setstate(int argc, char **argv)
374 struct gctl_req *req;
383 while ((i = getopt(argc, argv, "f")) != -1) {
390 warn("invalid flag: %c", i);
399 warnx("usage: setstate [-f] <state> <obj>");
404 * XXX: This hack is needed to avoid tripping over (now) invalid
405 * 'classic' vinum states and will go away later.
407 if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
408 strcmp(argv[0], "stale")) {
409 warnx("invalid state '%s'", argv[0]);
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);
420 errstr = gctl_issue(req);
427 gvinum_list(int argc, char **argv)
429 struct gctl_req *req;
432 char buf[20], *cmd, config[GV_CFG_LEN + 1];
441 while ((j = getopt(argc, argv, "rsvV")) != -1) {
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);
474 for (i = 0; i < argc; i++) {
475 snprintf(buf, sizeof(buf), "argv%d", i);
476 gctl_ro_param(req, buf, -1, argv[i]);
479 errstr = gctl_issue(req);
480 if (errstr != NULL) {
481 warnx("can't get configuration: %s", errstr);
486 printf("%s", config);
491 /* Note that move is currently of form '[-r] target object [...]' */
493 gvinum_move(int argc, char **argv)
495 struct gctl_req *req;
504 while ((j = getopt(argc, argv, "f")) != -1) {
520 warnx("no destination or object(s) to move specified");
523 warnx("no object(s) to move specified");
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]);
539 errstr = gctl_issue(req);
541 warnx("can't move object(s): %s", errstr);
547 gvinum_printconfig(int argc, char **argv)
549 printconfig(stdout, "");
553 gvinum_parityop(int argc, char **argv, int rebuild)
555 struct gctl_req *req;
562 op = "rebuildparity";
572 while ((i = getopt(argc, argv, "fv")) != -1) {
582 warnx("invalid flag '%c'", i);
590 warn("usage: %s [-f] [-v] <plex>", op);
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);
606 warnx("%s\n", errstr);
611 if (flags & GV_FLAG_V) {
612 printf("\r%s at %s ... ", msg,
613 gv_roughlength(offset, 1));
616 printf("Parity incorrect at offset 0x%jx\n",
623 /* Clear the -f flag. */
627 if ((rv == 2) && (flags & GV_FLAG_V)) {
629 printf("Rebuilt parity on %s\n", argv[0]);
631 printf("%s has correct parity\n", argv[0]);
636 gvinum_rename(int argc, char **argv)
638 struct gctl_req *req;
647 while ((j = getopt(argc, argv, "r")) != -1) {
663 warnx("no object to rename specified");
666 warnx("no new name specified");
671 warnx("more than one new name specified");
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);
683 warnx("can't rename object: %s", errstr);
689 gvinum_rm(int argc, char **argv)
691 struct gctl_req *req;
700 while ((j = getopt(argc, argv, "r")) != -1) {
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);
719 for (i = 0; i < argc; i++) {
720 snprintf(buf, sizeof(buf), "argv%d", i);
721 gctl_ro_param(req, buf, -1, argv[i]);
724 errstr = gctl_issue(req);
725 if (errstr != NULL) {
726 warnx("can't remove: %s", errstr);
731 gvinum_list(0, NULL);
735 gvinum_resetconfig(void)
737 struct gctl_req *req;
741 if (!isatty(STDIN_FILENO)) {
742 warn("Please enter this command from a tty device\n");
745 printf(" WARNING! This command will completely wipe out your gvinum"
747 " All data will be lost. If you really want to do this,"
748 " enter the text\n\n"
751 fgets(reply, sizeof(reply), stdin);
752 if (strcmp(reply, "NO FUTURE\n")) {
753 printf("\n No change\n");
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);
766 gvinum_list(0, NULL);
767 printf("gvinum configuration obliterated\n");
771 gvinum_saveconfig(void)
773 struct gctl_req *req;
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);
781 warnx("can't save configuration: %s", errstr);
786 gvinum_start(int argc, char **argv)
788 struct gctl_req *req;
793 /* 'start' with no arguments is a no-op. */
801 while ((j = getopt(argc, argv, "S")) != -1) {
804 initsize = atoi(optarg);
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);
823 for (i = 0; i < argc; i++) {
824 snprintf(buf, sizeof(buf), "argv%d", i);
825 gctl_ro_param(req, buf, -1, argv[i]);
828 errstr = gctl_issue(req);
829 if (errstr != NULL) {
830 warnx("can't start: %s", errstr);
836 gvinum_list(0, NULL);
840 gvinum_stop(int argc, char **argv)
844 fileid = kldfind(GVINUMMOD);
846 warn("cannot find " GVINUMMOD);
849 if (kldunload(fileid) != 0) {
850 warn("cannot unload " GVINUMMOD);
854 warnx(GVINUMMOD " unloaded");
859 parseline(int argc, char **argv)
864 if (!strcmp(argv[0], "create"))
865 gvinum_create(argc, argv);
866 else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
868 else if (!strcmp(argv[0], "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"))
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);
905 printf("unknown command '%s'\n", argv[0]);
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
916 printconfig(FILE *of, char *comment)
918 struct gctl_req *req;
919 struct utsname uname_s;
922 char buf[GV_CFG_LEN + 1];
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);
939 fprintf(of, "# Vinum configuration of %s, saved at %s",
943 if (*comment != '\0')
944 fprintf(of, "# Current configuration:\n");