1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2004 Lukas Ertl
5 * Copyright (c) 2005 Chris Jones
6 * Copyright (c) 2007 Ulf Lilleengen
7 * All rights reserved.
8 *
9 * Portions of this software were developed for the FreeBSD Project
10 * by Chris Jones thanks to the support of Google's Summer of Code
11 * program and mentoring by Lukas Ertl.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $FreeBSD: stable/12/sbin/gvinum/gvinum.c 335267 2018-06-16 23:45:59Z eadler $
35 */
36
37 #include <sys/param.h>
38 #include <sys/linker.h>
39 #include <sys/lock.h>
40 #include <sys/module.h>
41 #include <sys/mutex.h>
42 #include <sys/queue.h>
43 #include <sys/utsname.h>
44
45 #include <geom/vinum/geom_vinum_var.h>
46 #include <geom/vinum/geom_vinum_share.h>
47
48 #include <ctype.h>
49 #include <err.h>
50 #include <errno.h>
51 #include <libgeom.h>
52 #include <stdint.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <paths.h>
57 #include <readline/readline.h>
58 #include <readline/history.h>
59 #include <unistd.h>
60
61 #include "gvinum.h"
62
63 static void gvinum_attach(int, char * const *);
64 static void gvinum_concat(int, char * const *);
65 static void gvinum_create(int, char * const *);
66 static void gvinum_detach(int, char * const *);
67 static void gvinum_grow(int, char * const *);
68 static void gvinum_help(void);
69 static void gvinum_list(int, char * const *);
70 static void gvinum_move(int, char * const *);
71 static void gvinum_mirror(int, char * const *);
72 static void gvinum_parityop(int, char * const * , int);
73 static void gvinum_printconfig(int, char * const *);
74 static void gvinum_raid5(int, char * const *);
75 static void gvinum_rename(int, char * const *);
76 static void gvinum_resetconfig(int, char * const *);
77 static void gvinum_rm(int, char * const *);
78 static void gvinum_saveconfig(void);
79 static void gvinum_setstate(int, char * const *);
80 static void gvinum_start(int, char * const *);
81 static void gvinum_stop(int, char * const *);
82 static void gvinum_stripe(int, char * const *);
83 static void parseline(int, char * const *);
84 static void printconfig(FILE *, const char *);
85
86 static char *create_drive(const char *);
87 static void create_volume(int, char * const * , const char *);
88 static char *find_name(const char *, int, int);
89 static const char *find_pattern(char *, const char *);
90 static void copy_device(struct gv_drive *, const char *);
91 #define find_drive() \
92 find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME)
93
94 int
main(int argc,char ** argv)95 main(int argc, char **argv)
96 {
97 int tokens;
98 char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS];
99
100 /* Load the module if necessary. */
101 if (modfind(GVINUMMOD) < 0) {
102 if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0)
103 err(1, GVINUMKLD ": Kernel module not available");
104 }
105
106 /* Arguments given on the command line. */
107 if (argc > 1) {
108 argc--;
109 argv++;
110 parseline(argc, argv);
111
112 /* Interactive mode. */
113 } else {
114 for (;;) {
115 inputline = readline("gvinum -> ");
116 if (inputline == NULL) {
117 if (ferror(stdin)) {
118 err(1, "can't read input");
119 } else {
120 printf("\n");
121 exit(0);
122 }
123 } else if (*inputline) {
124 add_history(inputline);
125 strcpy(buffer, inputline);
126 free(inputline);
127 tokens = gv_tokenize(buffer, token, GV_MAXARGS);
128 if (tokens)
129 parseline(tokens, token);
130 }
131 }
132 }
133 exit(0);
134 }
135
136 /* Attach a plex to a volume or a subdisk to a plex. */
137 static void
gvinum_attach(int argc,char * const * argv)138 gvinum_attach(int argc, char * const *argv)
139 {
140 struct gctl_req *req;
141 const char *errstr;
142 int rename;
143 off_t offset;
144
145 rename = 0;
146 offset = -1;
147 if (argc < 3) {
148 warnx("usage:\tattach <subdisk> <plex> [rename] "
149 "[<plexoffset>]\n"
150 "\tattach <plex> <volume> [rename]");
151 return;
152 }
153 if (argc > 3) {
154 if (!strcmp(argv[3], "rename")) {
155 rename = 1;
156 if (argc == 5)
157 offset = strtol(argv[4], NULL, 0);
158 } else
159 offset = strtol(argv[3], NULL, 0);
160 }
161 req = gctl_get_handle();
162 gctl_ro_param(req, "class", -1, "VINUM");
163 gctl_ro_param(req, "verb", -1, "attach");
164 gctl_ro_param(req, "child", -1, argv[1]);
165 gctl_ro_param(req, "parent", -1, argv[2]);
166 gctl_ro_param(req, "offset", sizeof(off_t), &offset);
167 gctl_ro_param(req, "rename", sizeof(int), &rename);
168 errstr = gctl_issue(req);
169 if (errstr != NULL)
170 warnx("attach failed: %s", errstr);
171 gctl_free(req);
172 }
173
174 static void
gvinum_create(int argc,char * const * argv)175 gvinum_create(int argc, char * const *argv)
176 {
177 struct gctl_req *req;
178 struct gv_drive *d;
179 struct gv_plex *p;
180 struct gv_sd *s;
181 struct gv_volume *v;
182 FILE *tmp;
183 int drives, errors, fd, flags, i, line, plexes, plex_in_volume;
184 int sd_in_plex, status, subdisks, tokens, undeffd, volumes;
185 const char *errstr;
186 char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *sdname;
187 const char *ed;
188 char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS];
189 char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME];
190
191 tmp = NULL;
192 flags = 0;
193 for (i = 1; i < argc; i++) {
194 /* Force flag used to ignore already created drives. */
195 if (!strcmp(argv[i], "-f")) {
196 flags |= GV_FLAG_F;
197 /* Else it must be a file. */
198 } else {
199 if ((tmp = fopen(argv[i], "r")) == NULL) {
200 warn("can't open '%s' for reading", argv[i]);
201 return;
202 }
203 }
204 }
205
206 /* We didn't get a file. */
207 if (tmp == NULL) {
208 snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX");
209
210 if ((fd = mkstemp(tmpfile)) == -1) {
211 warn("temporary file not accessible");
212 return;
213 }
214 if ((tmp = fdopen(fd, "w")) == NULL) {
215 warn("can't open '%s' for writing", tmpfile);
216 return;
217 }
218 printconfig(tmp, "# ");
219 fclose(tmp);
220
221 ed = getenv("EDITOR");
222 if (ed == NULL)
223 ed = _PATH_VI;
224
225 snprintf(commandline, sizeof(commandline), "%s %s", ed,
226 tmpfile);
227 status = system(commandline);
228 if (status != 0) {
229 warn("couldn't exec %s; status: %d", ed, status);
230 return;
231 }
232
233 if ((tmp = fopen(tmpfile, "r")) == NULL) {
234 warn("can't open '%s' for reading", tmpfile);
235 return;
236 }
237 }
238
239 req = gctl_get_handle();
240 gctl_ro_param(req, "class", -1, "VINUM");
241 gctl_ro_param(req, "verb", -1, "create");
242 gctl_ro_param(req, "flags", sizeof(int), &flags);
243
244 drives = volumes = plexes = subdisks = 0;
245 plex_in_volume = sd_in_plex = undeffd = 0;
246 plex[0] = '\0';
247 errors = 0;
248 line = 1;
249 while ((fgets(buf, BUFSIZ, tmp)) != NULL) {
250
251 /* Skip empty lines and comments. */
252 if (*buf == '\0' || *buf == '#') {
253 line++;
254 continue;
255 }
256
257 /* Kill off the newline. */
258 buf[strlen(buf) - 1] = '\0';
259
260 /*
261 * Copy the original input line in case we need it for error
262 * output.
263 */
264 strlcpy(original, buf, sizeof(original));
265
266 tokens = gv_tokenize(buf, token, GV_MAXARGS);
267 if (tokens <= 0) {
268 line++;
269 continue;
270 }
271
272 /* Volume definition. */
273 if (!strcmp(token[0], "volume")) {
274 v = gv_new_volume(tokens, token);
275 if (v == NULL) {
276 warnx("line %d: invalid volume definition",
277 line);
278 warnx("line %d: '%s'", line, original);
279 errors++;
280 line++;
281 continue;
282 }
283
284 /* Reset plex count for this volume. */
285 plex_in_volume = 0;
286
287 /*
288 * Set default volume name for following plex
289 * definitions.
290 */
291 strlcpy(volume, v->name, sizeof(volume));
292
293 snprintf(buf1, sizeof(buf1), "volume%d", volumes);
294 gctl_ro_param(req, buf1, sizeof(*v), v);
295 volumes++;
296
297 /* Plex definition. */
298 } else if (!strcmp(token[0], "plex")) {
299 p = gv_new_plex(tokens, token);
300 if (p == NULL) {
301 warnx("line %d: invalid plex definition", line);
302 warnx("line %d: '%s'", line, original);
303 errors++;
304 line++;
305 continue;
306 }
307
308 /* Reset subdisk count for this plex. */
309 sd_in_plex = 0;
310
311 /* Default name. */
312 if (strlen(p->name) == 0) {
313 snprintf(p->name, sizeof(p->name), "%s.p%d",
314 volume, plex_in_volume++);
315 }
316
317 /* Default volume. */
318 if (strlen(p->volume) == 0) {
319 snprintf(p->volume, sizeof(p->volume), "%s",
320 volume);
321 }
322
323 /*
324 * Set default plex name for following subdisk
325 * definitions.
326 */
327 strlcpy(plex, p->name, sizeof(plex));
328
329 snprintf(buf1, sizeof(buf1), "plex%d", plexes);
330 gctl_ro_param(req, buf1, sizeof(*p), p);
331 plexes++;
332
333 /* Subdisk definition. */
334 } else if (!strcmp(token[0], "sd")) {
335 s = gv_new_sd(tokens, token);
336 if (s == NULL) {
337 warnx("line %d: invalid subdisk "
338 "definition:", line);
339 warnx("line %d: '%s'", line, original);
340 errors++;
341 line++;
342 continue;
343 }
344
345 /* Default name. */
346 if (strlen(s->name) == 0) {
347 if (strlen(plex) == 0) {
348 sdname = find_name("gvinumsubdisk.p",
349 GV_TYPE_SD, GV_MAXSDNAME);
350 snprintf(s->name, sizeof(s->name),
351 "%s.s%d", sdname, undeffd++);
352 free(sdname);
353 } else {
354 snprintf(s->name, sizeof(s->name),
355 "%s.s%d",plex, sd_in_plex++);
356 }
357 }
358
359 /* Default plex. */
360 if (strlen(s->plex) == 0)
361 snprintf(s->plex, sizeof(s->plex), "%s", plex);
362
363 snprintf(buf1, sizeof(buf1), "sd%d", subdisks);
364 gctl_ro_param(req, buf1, sizeof(*s), s);
365 subdisks++;
366
367 /* Subdisk definition. */
368 } else if (!strcmp(token[0], "drive")) {
369 d = gv_new_drive(tokens, token);
370 if (d == NULL) {
371 warnx("line %d: invalid drive definition:",
372 line);
373 warnx("line %d: '%s'", line, original);
374 errors++;
375 line++;
376 continue;
377 }
378
379 snprintf(buf1, sizeof(buf1), "drive%d", drives);
380 gctl_ro_param(req, buf1, sizeof(*d), d);
381 drives++;
382
383 /* Everything else is bogus. */
384 } else {
385 warnx("line %d: invalid definition:", line);
386 warnx("line %d: '%s'", line, original);
387 errors++;
388 }
389 line++;
390 }
391
392 fclose(tmp);
393 unlink(tmpfile);
394
395 if (!errors && (volumes || plexes || subdisks || drives)) {
396 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
397 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
398 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
399 gctl_ro_param(req, "drives", sizeof(int), &drives);
400 errstr = gctl_issue(req);
401 if (errstr != NULL)
402 warnx("create failed: %s", errstr);
403 }
404 gctl_free(req);
405 }
406
407 /* Create a concatenated volume. */
408 static void
gvinum_concat(int argc,char * const * argv)409 gvinum_concat(int argc, char * const *argv)
410 {
411
412 if (argc < 2) {
413 warnx("usage:\tconcat [-fv] [-n name] drives\n");
414 return;
415 }
416 create_volume(argc, argv, "concat");
417 }
418
419 /* Create a drive quick and dirty. */
420 static char *
create_drive(const char * device)421 create_drive(const char *device)
422 {
423 struct gv_drive *d;
424 struct gctl_req *req;
425 const char *errstr;
426 char *drivename, *dname;
427 int drives, i, flags, volumes, subdisks, plexes;
428 int found = 0;
429
430 flags = plexes = subdisks = volumes = 0;
431 drives = 1;
432 dname = NULL;
433
434 drivename = find_drive();
435 if (drivename == NULL)
436 return (NULL);
437
438 req = gctl_get_handle();
439 gctl_ro_param(req, "class", -1, "VINUM");
440 gctl_ro_param(req, "verb", -1, "create");
441 d = gv_alloc_drive();
442 if (d == NULL)
443 err(1, "unable to allocate for gv_drive object");
444
445 strlcpy(d->name, drivename, sizeof(d->name));
446 copy_device(d, device);
447 gctl_ro_param(req, "drive0", sizeof(*d), d);
448 gctl_ro_param(req, "flags", sizeof(int), &flags);
449 gctl_ro_param(req, "drives", sizeof(int), &drives);
450 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
451 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
452 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
453 errstr = gctl_issue(req);
454 if (errstr != NULL) {
455 warnx("error creating drive: %s", errstr);
456 drivename = NULL;
457 } else {
458 /* XXX: This is needed because we have to make sure the drives
459 * are created before we return. */
460 /* Loop until it's in the config. */
461 for (i = 0; i < 100000; i++) {
462 dname = find_name("gvinumdrive", GV_TYPE_DRIVE,
463 GV_MAXDRIVENAME);
464 /* If we got a different name, quit. */
465 if (dname == NULL)
466 continue;
467 if (strcmp(dname, drivename))
468 found = 1;
469 free(dname);
470 dname = NULL;
471 if (found)
472 break;
473 usleep(100000); /* Sleep for 0.1s */
474 }
475 if (found == 0) {
476 warnx("error creating drive");
477 drivename = NULL;
478 }
479 }
480 gctl_free(req);
481 return (drivename);
482 }
483
484 /*
485 * General routine for creating a volume. Mainly for use by concat, mirror,
486 * raid5 and stripe commands.
487 */
488 static void
create_volume(int argc,char * const * argv,const char * verb)489 create_volume(int argc, char * const *argv, const char *verb)
490 {
491 struct gctl_req *req;
492 const char *errstr;
493 char buf[BUFSIZ], *drivename, *volname;
494 int drives, flags, i;
495 off_t stripesize;
496
497 flags = 0;
498 drives = 0;
499 volname = NULL;
500 stripesize = 262144;
501
502 /* XXX: Should we check for argument length? */
503
504 req = gctl_get_handle();
505 gctl_ro_param(req, "class", -1, "VINUM");
506
507 for (i = 1; i < argc; i++) {
508 if (!strcmp(argv[i], "-f")) {
509 flags |= GV_FLAG_F;
510 } else if (!strcmp(argv[i], "-n")) {
511 volname = argv[++i];
512 } else if (!strcmp(argv[i], "-v")) {
513 flags |= GV_FLAG_V;
514 } else if (!strcmp(argv[i], "-s")) {
515 flags |= GV_FLAG_S;
516 if (!strcmp(verb, "raid5"))
517 stripesize = gv_sizespec(argv[++i]);
518 } else {
519 /* Assume it's a drive. */
520 snprintf(buf, sizeof(buf), "drive%d", drives++);
521
522 /* First we create the drive. */
523 drivename = create_drive(argv[i]);
524 if (drivename == NULL)
525 goto bad;
526 /* Then we add it to the request. */
527 gctl_ro_param(req, buf, -1, drivename);
528 }
529 }
530
531 gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize);
532
533 /* Find a free volume name. */
534 if (volname == NULL)
535 volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME);
536
537 /* Then we send a request to actually create the volumes. */
538 gctl_ro_param(req, "verb", -1, verb);
539 gctl_ro_param(req, "flags", sizeof(int), &flags);
540 gctl_ro_param(req, "drives", sizeof(int), &drives);
541 gctl_ro_param(req, "name", -1, volname);
542 errstr = gctl_issue(req);
543 if (errstr != NULL)
544 warnx("creating %s volume failed: %s", verb, errstr);
545 bad:
546 gctl_free(req);
547 }
548
549 /* Parse a line of the config, return the word after <pattern>. */
550 static const char *
find_pattern(char * line,const char * pattern)551 find_pattern(char *line, const char *pattern)
552 {
553 char *ptr;
554
555 ptr = strsep(&line, " ");
556 while (ptr != NULL) {
557 if (!strcmp(ptr, pattern)) {
558 /* Return the next. */
559 ptr = strsep(&line, " ");
560 return (ptr);
561 }
562 ptr = strsep(&line, " ");
563 }
564 return (NULL);
565 }
566
567 /* Find a free name for an object given a prefix. */
568 static char *
find_name(const char * prefix,int type,int namelen)569 find_name(const char *prefix, int type, int namelen)
570 {
571 struct gctl_req *req;
572 char comment[1], buf[GV_CFG_LEN - 1], *sname, *ptr;
573 const char *errstr, *name;
574 int i, n, begin, len, conflict;
575 char line[1024];
576
577 comment[0] = '\0';
578
579 /* Find a name. Fetch out configuration first. */
580 req = gctl_get_handle();
581 gctl_ro_param(req, "class", -1, "VINUM");
582 gctl_ro_param(req, "verb", -1, "getconfig");
583 gctl_ro_param(req, "comment", -1, comment);
584 gctl_rw_param(req, "config", sizeof(buf), buf);
585 errstr = gctl_issue(req);
586 if (errstr != NULL) {
587 warnx("can't get configuration: %s", errstr);
588 return (NULL);
589 }
590 gctl_free(req);
591
592 begin = 0;
593 len = strlen(buf);
594 i = 0;
595 sname = malloc(namelen + 1);
596
597 /* XXX: Max object setting? */
598 for (n = 0; n < 10000; n++) {
599 snprintf(sname, namelen, "%s%d", prefix, n);
600 conflict = 0;
601 begin = 0;
602 /* Loop through the configuration line by line. */
603 for (i = 0; i < len; i++) {
604 if (buf[i] == '\n' || buf[i] == '\0') {
605 ptr = buf + begin;
606 strlcpy(line, ptr, (i - begin) + 1);
607 begin = i + 1;
608 switch (type) {
609 case GV_TYPE_DRIVE:
610 name = find_pattern(line, "drive");
611 break;
612 case GV_TYPE_VOL:
613 name = find_pattern(line, "volume");
614 break;
615 case GV_TYPE_PLEX:
616 case GV_TYPE_SD:
617 name = find_pattern(line, "name");
618 break;
619 default:
620 printf("Invalid type given\n");
621 continue;
622 }
623 if (name == NULL)
624 continue;
625 if (!strcmp(sname, name)) {
626 conflict = 1;
627 /* XXX: Could quit the loop earlier. */
628 }
629 }
630 }
631 if (!conflict)
632 return (sname);
633 }
634 free(sname);
635 return (NULL);
636 }
637
638 static void
copy_device(struct gv_drive * d,const char * device)639 copy_device(struct gv_drive *d, const char *device)
640 {
641
642 if (strncmp(device, "/dev/", 5) == 0)
643 strlcpy(d->device, (device + 5), sizeof(d->device));
644 else
645 strlcpy(d->device, device, sizeof(d->device));
646 }
647
648 /* Detach a plex or subdisk from its parent. */
649 static void
gvinum_detach(int argc,char * const * argv)650 gvinum_detach(int argc, char * const *argv)
651 {
652 const char *errstr;
653 struct gctl_req *req;
654 int flags, i;
655
656 flags = 0;
657 optreset = 1;
658 optind = 1;
659 while ((i = getopt(argc, argv, "f")) != -1) {
660 switch (i) {
661 case 'f':
662 flags |= GV_FLAG_F;
663 break;
664 default:
665 warn("invalid flag: %c", i);
666 return;
667 }
668 }
669 argc -= optind;
670 argv += optind;
671 if (argc != 1) {
672 warnx("usage: detach [-f] <subdisk> | <plex>");
673 return;
674 }
675
676 req = gctl_get_handle();
677 gctl_ro_param(req, "class", -1, "VINUM");
678 gctl_ro_param(req, "verb", -1, "detach");
679 gctl_ro_param(req, "object", -1, argv[0]);
680 gctl_ro_param(req, "flags", sizeof(int), &flags);
681
682 errstr = gctl_issue(req);
683 if (errstr != NULL)
684 warnx("detach failed: %s", errstr);
685 gctl_free(req);
686 }
687
688 static void
gvinum_help(void)689 gvinum_help(void)
690 {
691
692 printf("COMMANDS\n"
693 "checkparity [-f] plex\n"
694 " Check the parity blocks of a RAID-5 plex.\n"
695 "create [-f] description-file\n"
696 " Create as per description-file or open editor.\n"
697 "attach plex volume [rename]\n"
698 "attach subdisk plex [offset] [rename]\n"
699 " Attach a plex to a volume, or a subdisk to a plex\n"
700 "concat [-fv] [-n name] drives\n"
701 " Create a concatenated volume from the specified drives.\n"
702 "detach [-f] [plex | subdisk]\n"
703 " Detach a plex or a subdisk from the volume or plex to\n"
704 " which it is attached.\n"
705 "grow plex drive\n"
706 " Grow plex by creating a properly sized subdisk on drive\n"
707 "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n"
708 " List information about specified objects.\n"
709 "ld [-r] [-v] [-V] [volume]\n"
710 " List information about drives.\n"
711 "ls [-r] [-v] [-V] [subdisk]\n"
712 " List information about subdisks.\n"
713 "lp [-r] [-v] [-V] [plex]\n"
714 " List information about plexes.\n"
715 "lv [-r] [-v] [-V] [volume]\n"
716 " List information about volumes.\n"
717 "mirror [-fsv] [-n name] drives\n"
718 " Create a mirrored volume from the specified drives.\n"
719 "move | mv -f drive object ...\n"
720 " Move the object(s) to the specified drive.\n"
721 "quit Exit the vinum program when running in interactive mode."
722 " Nor-\n"
723 " mally this would be done by entering the EOF character.\n"
724 "raid5 [-fv] [-s stripesize] [-n name] drives\n"
725 " Create a RAID-5 volume from the specified drives.\n"
726 "rename [-r] [drive | subdisk | plex | volume] newname\n"
727 " Change the name of the specified object.\n"
728 "rebuildparity plex [-f]\n"
729 " Rebuild the parity blocks of a RAID-5 plex.\n"
730 "resetconfig [-f]\n"
731 " Reset the complete gvinum configuration\n"
732 "rm [-r] [-f] volume | plex | subdisk | drive\n"
733 " Remove an object.\n"
734 "saveconfig\n"
735 " Save vinum configuration to disk after configuration"
736 " failures.\n"
737 "setstate [-f] state [volume | plex | subdisk | drive]\n"
738 " Set state without influencing other objects, for"
739 " diagnostic pur-\n"
740 " poses only.\n"
741 "start [-S size] volume | plex | subdisk\n"
742 " Allow the system to access the objects.\n"
743 "stripe [-fv] [-n name] drives\n"
744 " Create a striped volume from the specified drives.\n"
745 );
746 }
747
748 static void
gvinum_setstate(int argc,char * const * argv)749 gvinum_setstate(int argc, char * const *argv)
750 {
751 struct gctl_req *req;
752 int flags, i;
753 const char *errstr;
754
755 flags = 0;
756
757 optreset = 1;
758 optind = 1;
759
760 while ((i = getopt(argc, argv, "f")) != -1) {
761 switch (i) {
762 case 'f':
763 flags |= GV_FLAG_F;
764 break;
765 case '?':
766 default:
767 warn("invalid flag: %c", i);
768 return;
769 }
770 }
771
772 argc -= optind;
773 argv += optind;
774
775 if (argc != 2) {
776 warnx("usage: setstate [-f] <state> <obj>");
777 return;
778 }
779
780 /*
781 * XXX: This hack is needed to avoid tripping over (now) invalid
782 * 'classic' vinum states and will go away later.
783 */
784 if (strcmp(argv[0], "up") && strcmp(argv[0], "down") &&
785 strcmp(argv[0], "stale")) {
786 warnx("invalid state '%s'", argv[0]);
787 return;
788 }
789
790 req = gctl_get_handle();
791 gctl_ro_param(req, "class", -1, "VINUM");
792 gctl_ro_param(req, "verb", -1, "setstate");
793 gctl_ro_param(req, "state", -1, argv[0]);
794 gctl_ro_param(req, "object", -1, argv[1]);
795 gctl_ro_param(req, "flags", sizeof(int), &flags);
796
797 errstr = gctl_issue(req);
798 if (errstr != NULL)
799 warnx("%s", errstr);
800 gctl_free(req);
801 }
802
803 static void
gvinum_list(int argc,char * const * argv)804 gvinum_list(int argc, char * const *argv)
805 {
806 struct gctl_req *req;
807 int flags, i, j;
808 const char *errstr;
809 char buf[20], config[GV_CFG_LEN + 1];
810 const char *cmd;
811
812 flags = 0;
813 cmd = "list";
814
815 if (argc) {
816 optreset = 1;
817 optind = 1;
818 cmd = argv[0];
819 while ((j = getopt(argc, argv, "rsvV")) != -1) {
820 switch (j) {
821 case 'r':
822 flags |= GV_FLAG_R;
823 break;
824 case 's':
825 flags |= GV_FLAG_S;
826 break;
827 case 'v':
828 flags |= GV_FLAG_V;
829 break;
830 case 'V':
831 flags |= GV_FLAG_V;
832 flags |= GV_FLAG_VV;
833 break;
834 case '?':
835 default:
836 return;
837 }
838 }
839 argc -= optind;
840 argv += optind;
841
842 }
843
844 req = gctl_get_handle();
845 gctl_ro_param(req, "class", -1, "VINUM");
846 gctl_ro_param(req, "verb", -1, "list");
847 gctl_ro_param(req, "cmd", -1, cmd);
848 gctl_ro_param(req, "argc", sizeof(int), &argc);
849 gctl_ro_param(req, "flags", sizeof(int), &flags);
850 gctl_rw_param(req, "config", sizeof(config), config);
851 if (argc) {
852 for (i = 0; i < argc; i++) {
853 snprintf(buf, sizeof(buf), "argv%d", i);
854 gctl_ro_param(req, buf, -1, argv[i]);
855 }
856 }
857 errstr = gctl_issue(req);
858 if (errstr != NULL) {
859 warnx("can't get configuration: %s", errstr);
860 gctl_free(req);
861 return;
862 }
863
864 printf("%s", config);
865 gctl_free(req);
866 }
867
868 /* Create a mirrored volume. */
869 static void
gvinum_mirror(int argc,char * const * argv)870 gvinum_mirror(int argc, char * const *argv)
871 {
872
873 if (argc < 2) {
874 warnx("usage\tmirror [-fsv] [-n name] drives\n");
875 return;
876 }
877 create_volume(argc, argv, "mirror");
878 }
879
880 /* Note that move is currently of form '[-r] target object [...]' */
881 static void
gvinum_move(int argc,char * const * argv)882 gvinum_move(int argc, char * const *argv)
883 {
884 struct gctl_req *req;
885 const char *errstr;
886 char buf[20];
887 int flags, i, j;
888
889 flags = 0;
890 if (argc) {
891 optreset = 1;
892 optind = 1;
893 while ((j = getopt(argc, argv, "f")) != -1) {
894 switch (j) {
895 case 'f':
896 flags |= GV_FLAG_F;
897 break;
898 case '?':
899 default:
900 return;
901 }
902 }
903 argc -= optind;
904 argv += optind;
905 }
906
907 switch (argc) {
908 case 0:
909 warnx("no destination or object(s) to move specified");
910 return;
911 case 1:
912 warnx("no object(s) to move specified");
913 return;
914 default:
915 break;
916 }
917
918 req = gctl_get_handle();
919 gctl_ro_param(req, "class", -1, "VINUM");
920 gctl_ro_param(req, "verb", -1, "move");
921 gctl_ro_param(req, "argc", sizeof(int), &argc);
922 gctl_ro_param(req, "flags", sizeof(int), &flags);
923 gctl_ro_param(req, "destination", -1, argv[0]);
924 for (i = 1; i < argc; i++) {
925 snprintf(buf, sizeof(buf), "argv%d", i);
926 gctl_ro_param(req, buf, -1, argv[i]);
927 }
928 errstr = gctl_issue(req);
929 if (errstr != NULL)
930 warnx("can't move object(s): %s", errstr);
931 gctl_free(req);
932 }
933
934 static void
gvinum_printconfig(int argc __unused,char * const * argv __unused)935 gvinum_printconfig(int argc __unused, char * const *argv __unused)
936 {
937
938 printconfig(stdout, "");
939 }
940
941 static void
gvinum_parityop(int argc,char * const * argv,int rebuild)942 gvinum_parityop(int argc, char * const *argv, int rebuild)
943 {
944 struct gctl_req *req;
945 int flags, i;
946 const char *errstr;
947 const char *op;
948
949 if (rebuild) {
950 op = "rebuildparity";
951 } else {
952 op = "checkparity";
953 }
954
955 optreset = 1;
956 optind = 1;
957 flags = 0;
958 while ((i = getopt(argc, argv, "fv")) != -1) {
959 switch (i) {
960 case 'f':
961 flags |= GV_FLAG_F;
962 break;
963 case 'v':
964 flags |= GV_FLAG_V;
965 break;
966 default:
967 warnx("invalid flag '%c'", i);
968 return;
969 }
970 }
971 argc -= optind;
972 argv += optind;
973
974 if (argc != 1) {
975 warn("usage: %s [-f] [-v] <plex>", op);
976 return;
977 }
978
979 req = gctl_get_handle();
980 gctl_ro_param(req, "class", -1, "VINUM");
981 gctl_ro_param(req, "verb", -1, op);
982 gctl_ro_param(req, "rebuild", sizeof(int), &rebuild);
983 gctl_ro_param(req, "flags", sizeof(int), &flags);
984 gctl_ro_param(req, "plex", -1, argv[0]);
985
986 errstr = gctl_issue(req);
987 if (errstr)
988 warnx("%s\n", errstr);
989 gctl_free(req);
990 }
991
992 /* Create a RAID-5 volume. */
993 static void
gvinum_raid5(int argc,char * const * argv)994 gvinum_raid5(int argc, char * const *argv)
995 {
996
997 if (argc < 2) {
998 warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n");
999 return;
1000 }
1001 create_volume(argc, argv, "raid5");
1002 }
1003
1004 static void
gvinum_rename(int argc,char * const * argv)1005 gvinum_rename(int argc, char * const *argv)
1006 {
1007 struct gctl_req *req;
1008 const char *errstr;
1009 int flags, j;
1010
1011 flags = 0;
1012
1013 if (argc) {
1014 optreset = 1;
1015 optind = 1;
1016 while ((j = getopt(argc, argv, "r")) != -1) {
1017 switch (j) {
1018 case 'r':
1019 flags |= GV_FLAG_R;
1020 break;
1021 default:
1022 return;
1023 }
1024 }
1025 argc -= optind;
1026 argv += optind;
1027 }
1028
1029 switch (argc) {
1030 case 0:
1031 warnx("no object to rename specified");
1032 return;
1033 case 1:
1034 warnx("no new name specified");
1035 return;
1036 case 2:
1037 break;
1038 default:
1039 warnx("more than one new name specified");
1040 return;
1041 }
1042
1043 req = gctl_get_handle();
1044 gctl_ro_param(req, "class", -1, "VINUM");
1045 gctl_ro_param(req, "verb", -1, "rename");
1046 gctl_ro_param(req, "flags", sizeof(int), &flags);
1047 gctl_ro_param(req, "object", -1, argv[0]);
1048 gctl_ro_param(req, "newname", -1, argv[1]);
1049 errstr = gctl_issue(req);
1050 if (errstr != NULL)
1051 warnx("can't rename object: %s", errstr);
1052 gctl_free(req);
1053 }
1054
1055 static void
gvinum_rm(int argc,char * const * argv)1056 gvinum_rm(int argc, char * const *argv)
1057 {
1058 struct gctl_req *req;
1059 int flags, i, j;
1060 const char *errstr;
1061 char buf[20];
1062
1063 flags = 0;
1064 optreset = 1;
1065 optind = 1;
1066 while ((j = getopt(argc, argv, "rf")) != -1) {
1067 switch (j) {
1068 case 'f':
1069 flags |= GV_FLAG_F;
1070 break;
1071 case 'r':
1072 flags |= GV_FLAG_R;
1073 break;
1074 default:
1075 return;
1076 }
1077 }
1078 argc -= optind;
1079 argv += optind;
1080
1081 req = gctl_get_handle();
1082 gctl_ro_param(req, "class", -1, "VINUM");
1083 gctl_ro_param(req, "verb", -1, "remove");
1084 gctl_ro_param(req, "argc", sizeof(int), &argc);
1085 gctl_ro_param(req, "flags", sizeof(int), &flags);
1086 if (argc) {
1087 for (i = 0; i < argc; i++) {
1088 snprintf(buf, sizeof(buf), "argv%d", i);
1089 gctl_ro_param(req, buf, -1, argv[i]);
1090 }
1091 }
1092 errstr = gctl_issue(req);
1093 if (errstr != NULL) {
1094 warnx("can't remove: %s", errstr);
1095 gctl_free(req);
1096 return;
1097 }
1098 gctl_free(req);
1099 }
1100
1101 static void
gvinum_resetconfig(int argc,char * const * argv)1102 gvinum_resetconfig(int argc, char * const *argv)
1103 {
1104 struct gctl_req *req;
1105 const char *errstr;
1106 char reply[32];
1107 int flags, i;
1108
1109 flags = 0;
1110 while ((i = getopt(argc, argv, "f")) != -1) {
1111 switch (i) {
1112 case 'f':
1113 flags |= GV_FLAG_F;
1114 break;
1115 default:
1116 warn("invalid flag: %c", i);
1117 return;
1118 }
1119 }
1120 if ((flags & GV_FLAG_F) == 0) {
1121 if (!isatty(STDIN_FILENO)) {
1122 warn("Please enter this command from a tty device\n");
1123 return;
1124 }
1125 printf(" WARNING! This command will completely wipe out"
1126 " your gvinum configuration.\n"
1127 " All data will be lost. If you really want to do this,"
1128 " enter the text\n\n"
1129 " NO FUTURE\n"
1130 " Enter text -> ");
1131 fgets(reply, sizeof(reply), stdin);
1132 if (strcmp(reply, "NO FUTURE\n")) {
1133 printf("\n No change\n");
1134 return;
1135 }
1136 }
1137 req = gctl_get_handle();
1138 gctl_ro_param(req, "class", -1, "VINUM");
1139 gctl_ro_param(req, "verb", -1, "resetconfig");
1140 errstr = gctl_issue(req);
1141 if (errstr != NULL) {
1142 warnx("can't reset config: %s", errstr);
1143 gctl_free(req);
1144 return;
1145 }
1146 gctl_free(req);
1147 printf("gvinum configuration obliterated\n");
1148 }
1149
1150 static void
gvinum_saveconfig(void)1151 gvinum_saveconfig(void)
1152 {
1153 struct gctl_req *req;
1154 const char *errstr;
1155
1156 req = gctl_get_handle();
1157 gctl_ro_param(req, "class", -1, "VINUM");
1158 gctl_ro_param(req, "verb", -1, "saveconfig");
1159 errstr = gctl_issue(req);
1160 if (errstr != NULL)
1161 warnx("can't save configuration: %s", errstr);
1162 gctl_free(req);
1163 }
1164
1165 static void
gvinum_start(int argc,char * const * argv)1166 gvinum_start(int argc, char * const *argv)
1167 {
1168 struct gctl_req *req;
1169 int i, initsize, j;
1170 const char *errstr;
1171 char buf[20];
1172
1173 /* 'start' with no arguments is a no-op. */
1174 if (argc == 1)
1175 return;
1176
1177 initsize = 0;
1178
1179 optreset = 1;
1180 optind = 1;
1181 while ((j = getopt(argc, argv, "S")) != -1) {
1182 switch (j) {
1183 case 'S':
1184 initsize = atoi(optarg);
1185 break;
1186 default:
1187 return;
1188 }
1189 }
1190 argc -= optind;
1191 argv += optind;
1192
1193 if (!initsize)
1194 initsize = 512;
1195
1196 req = gctl_get_handle();
1197 gctl_ro_param(req, "class", -1, "VINUM");
1198 gctl_ro_param(req, "verb", -1, "start");
1199 gctl_ro_param(req, "argc", sizeof(int), &argc);
1200 gctl_ro_param(req, "initsize", sizeof(int), &initsize);
1201 if (argc) {
1202 for (i = 0; i < argc; i++) {
1203 snprintf(buf, sizeof(buf), "argv%d", i);
1204 gctl_ro_param(req, buf, -1, argv[i]);
1205 }
1206 }
1207 errstr = gctl_issue(req);
1208 if (errstr != NULL) {
1209 warnx("can't start: %s", errstr);
1210 gctl_free(req);
1211 return;
1212 }
1213
1214 gctl_free(req);
1215 }
1216
1217 static void
gvinum_stop(int argc __unused,char * const * argv __unused)1218 gvinum_stop(int argc __unused, char * const *argv __unused)
1219 {
1220 int err, fileid;
1221
1222 fileid = kldfind(GVINUMKLD);
1223 if (fileid == -1) {
1224 if (modfind(GVINUMMOD) < 0)
1225 warn("cannot find " GVINUMKLD);
1226 return;
1227 }
1228
1229 /*
1230 * This little hack prevents that we end up in an infinite loop in
1231 * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM
1232 * event thread will be free for the g_wither_geom() call from
1233 * gv_unload(). It's silly, but it works.
1234 */
1235 printf("unloading " GVINUMKLD " kernel module... ");
1236 fflush(stdout);
1237 if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) {
1238 sleep(1);
1239 err = kldunload(fileid);
1240 }
1241 if (err != 0) {
1242 printf(" failed!\n");
1243 warn("cannot unload " GVINUMKLD);
1244 return;
1245 }
1246
1247 printf("done\n");
1248 exit(0);
1249 }
1250
1251 /* Create a striped volume. */
1252 static void
gvinum_stripe(int argc,char * const * argv)1253 gvinum_stripe(int argc, char * const *argv)
1254 {
1255
1256 if (argc < 2) {
1257 warnx("usage:\tstripe [-fv] [-n name] drives\n");
1258 return;
1259 }
1260 create_volume(argc, argv, "stripe");
1261 }
1262
1263 /* Grow a subdisk by adding disk backed by provider. */
1264 static void
gvinum_grow(int argc,char * const * argv)1265 gvinum_grow(int argc, char * const *argv)
1266 {
1267 struct gctl_req *req;
1268 char *drive, *sdname;
1269 char sdprefix[GV_MAXSDNAME];
1270 struct gv_drive *d;
1271 struct gv_sd *s;
1272 const char *errstr;
1273 int drives, volumes, plexes, subdisks, flags;
1274
1275 flags = 0;
1276 drives = volumes = plexes = subdisks = 0;
1277 if (argc < 3) {
1278 warnx("usage:\tgrow plex drive\n");
1279 return;
1280 }
1281
1282 s = gv_alloc_sd();
1283 if (s == NULL) {
1284 warn("unable to create subdisk");
1285 return;
1286 }
1287 d = gv_alloc_drive();
1288 if (d == NULL) {
1289 warn("unable to create drive");
1290 free(s);
1291 return;
1292 }
1293 /* Lookup device and set an appropriate drive name. */
1294 drive = find_drive();
1295 if (drive == NULL) {
1296 warn("unable to find an appropriate drive name");
1297 free(s);
1298 free(d);
1299 return;
1300 }
1301 strlcpy(d->name, drive, sizeof(d->name));
1302 copy_device(d, argv[2]);
1303
1304 drives = 1;
1305
1306 /* We try to use the plex name as basis for the subdisk name. */
1307 snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]);
1308 sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME);
1309 if (sdname == NULL) {
1310 warn("unable to find an appropriate subdisk name");
1311 free(s);
1312 free(d);
1313 free(drive);
1314 return;
1315 }
1316 strlcpy(s->name, sdname, sizeof(s->name));
1317 free(sdname);
1318 strlcpy(s->plex, argv[1], sizeof(s->plex));
1319 strlcpy(s->drive, d->name, sizeof(s->drive));
1320 subdisks = 1;
1321
1322 req = gctl_get_handle();
1323 gctl_ro_param(req, "class", -1, "VINUM");
1324 gctl_ro_param(req, "verb", -1, "create");
1325 gctl_ro_param(req, "flags", sizeof(int), &flags);
1326 gctl_ro_param(req, "volumes", sizeof(int), &volumes);
1327 gctl_ro_param(req, "plexes", sizeof(int), &plexes);
1328 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks);
1329 gctl_ro_param(req, "drives", sizeof(int), &drives);
1330 gctl_ro_param(req, "drive0", sizeof(*d), d);
1331 gctl_ro_param(req, "sd0", sizeof(*s), s);
1332 errstr = gctl_issue(req);
1333 free(drive);
1334 if (errstr != NULL) {
1335 warnx("unable to grow plex: %s", errstr);
1336 free(s);
1337 free(d);
1338 return;
1339 }
1340 gctl_free(req);
1341 }
1342
1343 static void
parseline(int argc,char * const * argv)1344 parseline(int argc, char * const *argv)
1345 {
1346
1347 if (argc <= 0)
1348 return;
1349
1350 if (!strcmp(argv[0], "create"))
1351 gvinum_create(argc, argv);
1352 else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit"))
1353 exit(0);
1354 else if (!strcmp(argv[0], "attach"))
1355 gvinum_attach(argc, argv);
1356 else if (!strcmp(argv[0], "detach"))
1357 gvinum_detach(argc, argv);
1358 else if (!strcmp(argv[0], "concat"))
1359 gvinum_concat(argc, argv);
1360 else if (!strcmp(argv[0], "grow"))
1361 gvinum_grow(argc, argv);
1362 else if (!strcmp(argv[0], "help"))
1363 gvinum_help();
1364 else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l"))
1365 gvinum_list(argc, argv);
1366 else if (!strcmp(argv[0], "ld"))
1367 gvinum_list(argc, argv);
1368 else if (!strcmp(argv[0], "lp"))
1369 gvinum_list(argc, argv);
1370 else if (!strcmp(argv[0], "ls"))
1371 gvinum_list(argc, argv);
1372 else if (!strcmp(argv[0], "lv"))
1373 gvinum_list(argc, argv);
1374 else if (!strcmp(argv[0], "mirror"))
1375 gvinum_mirror(argc, argv);
1376 else if (!strcmp(argv[0], "move"))
1377 gvinum_move(argc, argv);
1378 else if (!strcmp(argv[0], "mv"))
1379 gvinum_move(argc, argv);
1380 else if (!strcmp(argv[0], "printconfig"))
1381 gvinum_printconfig(argc, argv);
1382 else if (!strcmp(argv[0], "raid5"))
1383 gvinum_raid5(argc, argv);
1384 else if (!strcmp(argv[0], "rename"))
1385 gvinum_rename(argc, argv);
1386 else if (!strcmp(argv[0], "resetconfig"))
1387 gvinum_resetconfig(argc, argv);
1388 else if (!strcmp(argv[0], "rm"))
1389 gvinum_rm(argc, argv);
1390 else if (!strcmp(argv[0], "saveconfig"))
1391 gvinum_saveconfig();
1392 else if (!strcmp(argv[0], "setstate"))
1393 gvinum_setstate(argc, argv);
1394 else if (!strcmp(argv[0], "start"))
1395 gvinum_start(argc, argv);
1396 else if (!strcmp(argv[0], "stop"))
1397 gvinum_stop(argc, argv);
1398 else if (!strcmp(argv[0], "stripe"))
1399 gvinum_stripe(argc, argv);
1400 else if (!strcmp(argv[0], "checkparity"))
1401 gvinum_parityop(argc, argv, 0);
1402 else if (!strcmp(argv[0], "rebuildparity"))
1403 gvinum_parityop(argc, argv, 1);
1404 else
1405 printf("unknown command '%s'\n", argv[0]);
1406 }
1407
1408 /*
1409 * The guts of printconfig. This is called from gvinum_printconfig and from
1410 * gvinum_create when called without an argument, in order to give the user
1411 * something to edit.
1412 */
1413 static void
printconfig(FILE * of,const char * comment)1414 printconfig(FILE *of, const char *comment)
1415 {
1416 struct gctl_req *req;
1417 struct utsname uname_s;
1418 const char *errstr;
1419 time_t now;
1420 char buf[GV_CFG_LEN + 1];
1421
1422 uname(&uname_s);
1423 time(&now);
1424
1425 req = gctl_get_handle();
1426 gctl_ro_param(req, "class", -1, "VINUM");
1427 gctl_ro_param(req, "verb", -1, "getconfig");
1428 gctl_ro_param(req, "comment", -1, comment);
1429 gctl_rw_param(req, "config", sizeof(buf), buf);
1430 errstr = gctl_issue(req);
1431 if (errstr != NULL) {
1432 warnx("can't get configuration: %s", errstr);
1433 return;
1434 }
1435 gctl_free(req);
1436
1437 fprintf(of, "# Vinum configuration of %s, saved at %s",
1438 uname_s.nodename,
1439 ctime(&now));
1440
1441 if (*comment != '\0')
1442 fprintf(of, "# Current configuration:\n");
1443
1444 fprintf(of, "%s", buf);
1445 }
1446