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_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 *);
73 main(int argc, char **argv)
76 char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
78 /* Load the module if necessary. */
79 if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0)
80 err(1, GVINUMMOD ": Kernel module not available");
82 /* Arguments given on the command line. */
86 parseline(argc, argv);
88 /* Interactive mode. */
91 inputline = readline("gvinum -> ");
92 if (inputline == NULL) {
94 err(1, "can't read input");
99 } else if (*inputline) {
100 add_history(inputline);
101 strcpy(buffer, inputline);
103 line++; /* count the lines */
104 tokens = gv_tokenize(buffer, token, GV_MAXARGS);
106 parseline(tokens, token);
114 gvinum_create(int argc, char **argv)
116 struct gctl_req *req;
122 int drives, errors, fd, line, plexes, plex_in_volume;
123 int sd_in_plex, status, subdisks, tokens, volumes;
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];
130 if ((tmp = fopen(argv[1], "r")) == NULL) {
131 warn("can't open '%s' for reading", argv[1]);
135 snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
137 if ((fd = mkstemp(tmpfile)) == -1) {
138 warn("temporary file not accessible");
141 if ((tmp = fdopen(fd, "w")) == NULL) {
142 warn("can't open '%s' for writing", tmpfile);
145 printconfig(tmp, "# ");
148 ed = getenv("EDITOR");
152 snprintf(commandline, sizeof(commandline), "%s %s", ed,
154 status = system(commandline);
156 warn("couldn't exec %s; status: %d", ed, status);
160 if ((tmp = fopen(tmpfile, "r")) == NULL) {
161 warn("can't open '%s' for reading", tmpfile);
166 req = gctl_get_handle();
167 gctl_ro_param(req, "class", -1, "VINUM");
168 gctl_ro_param(req, "verb", -1, "create");
170 drives = volumes = plexes = subdisks = 0;
171 plex_in_volume = sd_in_plex = 0;
174 while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
176 /* Skip empty lines and comments. */
177 if (*buf == '\0' || *buf == '#') {
182 /* Kill off the newline. */
183 buf[strlen(buf) - 1] = '\0';
186 * Copy the original input line in case we need it for error
189 strncpy(original, buf, sizeof(buf));
191 tokens = gv_tokenize(buf, token, GV_MAXARGS);
197 /* Volume definition. */
198 if (!strcmp(token[0], "volume")) {
199 v = gv_new_volume(tokens, token);
201 warnx("line %d: invalid volume definition",
203 warnx("line %d: '%s'", line, original);
209 /* Reset plex count for this volume. */
213 * Set default volume name for following plex
216 strncpy(volume, v->name, sizeof(volume));
218 snprintf(buf1, sizeof(buf1), "volume%d", volumes);
219 gctl_ro_param(req, buf1, sizeof(*v), v);
222 /* Plex definition. */
223 } else if (!strcmp(token[0], "plex")) {
224 p = gv_new_plex(tokens, token);
226 warnx("line %d: invalid plex definition", line);
227 warnx("line %d: '%s'", line, original);
233 /* Reset subdisk count for this plex. */
237 if (strlen(p->name) == 0) {
238 snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d",
239 volume, plex_in_volume++);
242 /* Default volume. */
243 if (strlen(p->volume) == 0) {
244 snprintf(p->volume, GV_MAXVOLNAME, "%s",
249 * Set default plex name for following subdisk
252 strncpy(plex, p->name, GV_MAXPLEXNAME);
254 snprintf(buf1, sizeof(buf1), "plex%d", plexes);
255 gctl_ro_param(req, buf1, sizeof(*p), p);
258 /* Subdisk definition. */
259 } else if (!strcmp(token[0], "sd")) {
260 s = gv_new_sd(tokens, token);
262 warnx("line %d: invalid subdisk "
263 "definition:", line);
264 warnx("line %d: '%s'", line, original);
271 if (strlen(s->name) == 0) {
272 snprintf(s->name, GV_MAXSDNAME, "%s.s%d",
277 if (strlen(s->plex) == 0)
278 snprintf(s->plex, GV_MAXPLEXNAME, "%s", plex);
280 snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
281 gctl_ro_param(req, buf1, sizeof(*s), s);
284 /* Subdisk definition. */
285 } else if (!strcmp(token[0], "drive")) {
286 d = gv_new_drive(tokens, token);
288 warnx("line %d: invalid drive definition:",
290 warnx("line %d: '%s'", line, original);
296 snprintf(buf1, sizeof(buf1), "drive%d", drives);
297 gctl_ro_param(req, buf1, sizeof(*d), d);
300 /* Everything else is bogus. */
302 warnx("line %d: invalid definition:", line);
303 warnx("line %d: '%s'", line, original);
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);
319 warnx("create failed: %s", errstr);
322 gvinum_list(0, NULL);
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."
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"
355 " Save vinum configuration to disk after configuration"
357 "setstate [-f] state [volume | plex | subdisk | drive]\n"
358 " Set state without influencing other objects, for"
361 "start [-S size] volume | plex | subdisk\n"
362 " Allow the system to access the objects.\n"
369 gvinum_setstate(int argc, char **argv)
371 struct gctl_req *req;
380 while ((i = getopt(argc, argv, "f")) != -1) {
387 warn("invalid flag: %c", i);
396 warnx("usage: setstate [-f] <state> <obj>");
401 * XXX: This hack is needed to avoid tripping over (now) invalid
402 * 'classic' vinum states and will go away later.
404 if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
405 strcmp(argv[0], "stale")) {
406 warnx("invalid state '%s'", argv[0]);
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);
417 errstr = gctl_issue(req);
424 gvinum_list(int argc, char **argv)
426 struct gctl_req *req;
429 char buf[20], *cmd, config[GV_CFG_LEN + 1];
438 while ((j = getopt(argc, argv, "rsvV")) != -1) {
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);
471 for (i = 0; i < argc; i++) {
472 snprintf(buf, sizeof(buf), "argv%d", i);
473 gctl_ro_param(req, buf, -1, argv[i]);
476 errstr = gctl_issue(req);
477 if (errstr != NULL) {
478 warnx("can't get configuration: %s", errstr);
483 printf("%s", config);
488 /* Note that move is currently of form '[-r] target object [...]' */
490 gvinum_move(int argc, char **argv)
492 struct gctl_req *req;
501 while ((j = getopt(argc, argv, "f")) != -1) {
517 warnx("no destination or object(s) to move specified");
520 warnx("no object(s) to move specified");
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]);
536 errstr = gctl_issue(req);
538 warnx("can't move object(s): %s", errstr);
544 gvinum_printconfig(int argc, char **argv)
546 printconfig(stdout, "");
550 gvinum_parityop(int argc, char **argv, int rebuild)
552 struct gctl_req *req;
559 op = "rebuildparity";
569 while ((i = getopt(argc, argv, "fv")) != -1) {
579 warnx("invalid flag '%c'", i);
587 warn("usage: %s [-f] [-v] <plex>", op);
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);
603 warnx("%s\n", errstr);
608 if (flags & GV_FLAG_V) {
609 printf("\r%s at %s ... ", msg,
610 gv_roughlength(offset, 1));
613 printf("Parity incorrect at offset 0x%jx\n",
620 /* Clear the -f flag. */
624 if ((rv == 2) && (flags & GV_FLAG_V)) {
626 printf("Rebuilt parity on %s\n", argv[0]);
628 printf("%s has correct parity\n", argv[0]);
633 gvinum_rename(int argc, char **argv)
635 struct gctl_req *req;
644 while ((j = getopt(argc, argv, "r")) != -1) {
660 warnx("no object to rename specified");
663 warnx("no new name specified");
668 warnx("more than one new name specified");
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);
680 warnx("can't rename object: %s", errstr);
686 gvinum_rm(int argc, char **argv)
688 struct gctl_req *req;
697 while ((j = getopt(argc, argv, "r")) != -1) {
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);
716 for (i = 0; i < argc; i++) {
717 snprintf(buf, sizeof(buf), "argv%d", i);
718 gctl_ro_param(req, buf, -1, argv[i]);
721 errstr = gctl_issue(req);
722 if (errstr != NULL) {
723 warnx("can't remove: %s", errstr);
728 gvinum_list(0, NULL);
732 gvinum_saveconfig(void)
734 struct gctl_req *req;
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);
742 warnx("can't save configuration: %s", errstr);
747 gvinum_start(int argc, char **argv)
749 struct gctl_req *req;
754 /* 'start' with no arguments is a no-op. */
762 while ((j = getopt(argc, argv, "S")) != -1) {
765 initsize = atoi(optarg);
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);
784 for (i = 0; i < argc; i++) {
785 snprintf(buf, sizeof(buf), "argv%d", i);
786 gctl_ro_param(req, buf, -1, argv[i]);
789 errstr = gctl_issue(req);
790 if (errstr != NULL) {
791 warnx("can't start: %s", errstr);
797 gvinum_list(0, NULL);
801 gvinum_stop(int argc, char **argv)
805 fileid = kldfind(GVINUMMOD);
807 warn("cannot find " GVINUMMOD);
810 if (kldunload(fileid) != 0) {
811 warn("cannot unload " GVINUMMOD);
815 warnx(GVINUMMOD " unloaded");
820 parseline(int argc, char **argv)
825 if (!strcmp(argv[0], "create"))
826 gvinum_create(argc, argv);
827 else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
829 else if (!strcmp(argv[0], "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"))
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);
864 printf("unknown command '%s'\n", argv[0]);
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
875 printconfig(FILE *of, char *comment)
877 struct gctl_req *req;
878 struct utsname uname_s;
881 char buf[GV_CFG_LEN + 1];
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);
898 fprintf(of, "# Vinum configuration of %s, saved at %s",
902 if (*comment != '\0')
903 fprintf(of, "# Current configuration:\n");