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$ 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 95 main(int argc, char **argv) 96 { 97 int line, 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 line++; /* count the lines */ 128 tokens = gv_tokenize(buffer, token, GV_MAXARGS); 129 if (tokens) 130 parseline(tokens, token); 131 } 132 } 133 } 134 exit(0); 135 } 136 137 /* Attach a plex to a volume or a subdisk to a plex. */ 138 static void 139 gvinum_attach(int argc, char * const *argv) 140 { 141 struct gctl_req *req; 142 const char *errstr; 143 int rename; 144 off_t offset; 145 146 rename = 0; 147 offset = -1; 148 if (argc < 3) { 149 warnx("usage:\tattach <subdisk> <plex> [rename] " 150 "[<plexoffset>]\n" 151 "\tattach <plex> <volume> [rename]"); 152 return; 153 } 154 if (argc > 3) { 155 if (!strcmp(argv[3], "rename")) { 156 rename = 1; 157 if (argc == 5) 158 offset = strtol(argv[4], NULL, 0); 159 } else 160 offset = strtol(argv[3], NULL, 0); 161 } 162 req = gctl_get_handle(); 163 gctl_ro_param(req, "class", -1, "VINUM"); 164 gctl_ro_param(req, "verb", -1, "attach"); 165 gctl_ro_param(req, "child", -1, argv[1]); 166 gctl_ro_param(req, "parent", -1, argv[2]); 167 gctl_ro_param(req, "offset", sizeof(off_t), &offset); 168 gctl_ro_param(req, "rename", sizeof(int), &rename); 169 errstr = gctl_issue(req); 170 if (errstr != NULL) 171 warnx("attach failed: %s", errstr); 172 gctl_free(req); 173 } 174 175 static void 176 gvinum_create(int argc, char * const *argv) 177 { 178 struct gctl_req *req; 179 struct gv_drive *d; 180 struct gv_plex *p; 181 struct gv_sd *s; 182 struct gv_volume *v; 183 FILE *tmp; 184 int drives, errors, fd, flags, i, line, plexes, plex_in_volume; 185 int sd_in_plex, status, subdisks, tokens, undeffd, volumes; 186 const char *errstr; 187 char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname; 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 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 * 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 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 * 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 * 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 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 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 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 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 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], *cmd, config[GV_CFG_LEN + 1]; 810 811 flags = 0; 812 cmd = "list"; 813 814 if (argc) { 815 optreset = 1; 816 optind = 1; 817 cmd = argv[0]; 818 while ((j = getopt(argc, argv, "rsvV")) != -1) { 819 switch (j) { 820 case 'r': 821 flags |= GV_FLAG_R; 822 break; 823 case 's': 824 flags |= GV_FLAG_S; 825 break; 826 case 'v': 827 flags |= GV_FLAG_V; 828 break; 829 case 'V': 830 flags |= GV_FLAG_V; 831 flags |= GV_FLAG_VV; 832 break; 833 case '?': 834 default: 835 return; 836 } 837 } 838 argc -= optind; 839 argv += optind; 840 841 } 842 843 req = gctl_get_handle(); 844 gctl_ro_param(req, "class", -1, "VINUM"); 845 gctl_ro_param(req, "verb", -1, "list"); 846 gctl_ro_param(req, "cmd", -1, cmd); 847 gctl_ro_param(req, "argc", sizeof(int), &argc); 848 gctl_ro_param(req, "flags", sizeof(int), &flags); 849 gctl_rw_param(req, "config", sizeof(config), config); 850 if (argc) { 851 for (i = 0; i < argc; i++) { 852 snprintf(buf, sizeof(buf), "argv%d", i); 853 gctl_ro_param(req, buf, -1, argv[i]); 854 } 855 } 856 errstr = gctl_issue(req); 857 if (errstr != NULL) { 858 warnx("can't get configuration: %s", errstr); 859 gctl_free(req); 860 return; 861 } 862 863 printf("%s", config); 864 gctl_free(req); 865 } 866 867 /* Create a mirrored volume. */ 868 static void 869 gvinum_mirror(int argc, char * const *argv) 870 { 871 872 if (argc < 2) { 873 warnx("usage\tmirror [-fsv] [-n name] drives\n"); 874 return; 875 } 876 create_volume(argc, argv, "mirror"); 877 } 878 879 /* Note that move is currently of form '[-r] target object [...]' */ 880 static void 881 gvinum_move(int argc, char * const *argv) 882 { 883 struct gctl_req *req; 884 const char *errstr; 885 char buf[20]; 886 int flags, i, j; 887 888 flags = 0; 889 if (argc) { 890 optreset = 1; 891 optind = 1; 892 while ((j = getopt(argc, argv, "f")) != -1) { 893 switch (j) { 894 case 'f': 895 flags |= GV_FLAG_F; 896 break; 897 case '?': 898 default: 899 return; 900 } 901 } 902 argc -= optind; 903 argv += optind; 904 } 905 906 switch (argc) { 907 case 0: 908 warnx("no destination or object(s) to move specified"); 909 return; 910 case 1: 911 warnx("no object(s) to move specified"); 912 return; 913 default: 914 break; 915 } 916 917 req = gctl_get_handle(); 918 gctl_ro_param(req, "class", -1, "VINUM"); 919 gctl_ro_param(req, "verb", -1, "move"); 920 gctl_ro_param(req, "argc", sizeof(int), &argc); 921 gctl_ro_param(req, "flags", sizeof(int), &flags); 922 gctl_ro_param(req, "destination", -1, argv[0]); 923 for (i = 1; i < argc; i++) { 924 snprintf(buf, sizeof(buf), "argv%d", i); 925 gctl_ro_param(req, buf, -1, argv[i]); 926 } 927 errstr = gctl_issue(req); 928 if (errstr != NULL) 929 warnx("can't move object(s): %s", errstr); 930 gctl_free(req); 931 } 932 933 static void 934 gvinum_printconfig(int argc, char * const *argv) 935 { 936 937 printconfig(stdout, ""); 938 } 939 940 static void 941 gvinum_parityop(int argc, char * const *argv, int rebuild) 942 { 943 struct gctl_req *req; 944 int flags, i; 945 const char *errstr; 946 char *op; 947 948 if (rebuild) { 949 op = "rebuildparity"; 950 } else { 951 op = "checkparity"; 952 } 953 954 optreset = 1; 955 optind = 1; 956 flags = 0; 957 while ((i = getopt(argc, argv, "fv")) != -1) { 958 switch (i) { 959 case 'f': 960 flags |= GV_FLAG_F; 961 break; 962 case 'v': 963 flags |= GV_FLAG_V; 964 break; 965 default: 966 warnx("invalid flag '%c'", i); 967 return; 968 } 969 } 970 argc -= optind; 971 argv += optind; 972 973 if (argc != 1) { 974 warn("usage: %s [-f] [-v] <plex>", op); 975 return; 976 } 977 978 req = gctl_get_handle(); 979 gctl_ro_param(req, "class", -1, "VINUM"); 980 gctl_ro_param(req, "verb", -1, op); 981 gctl_ro_param(req, "rebuild", sizeof(int), &rebuild); 982 gctl_ro_param(req, "flags", sizeof(int), &flags); 983 gctl_ro_param(req, "plex", -1, argv[0]); 984 985 errstr = gctl_issue(req); 986 if (errstr) 987 warnx("%s\n", errstr); 988 gctl_free(req); 989 } 990 991 /* Create a RAID-5 volume. */ 992 static void 993 gvinum_raid5(int argc, char * const *argv) 994 { 995 996 if (argc < 2) { 997 warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n"); 998 return; 999 } 1000 create_volume(argc, argv, "raid5"); 1001 } 1002 1003 static void 1004 gvinum_rename(int argc, char * const *argv) 1005 { 1006 struct gctl_req *req; 1007 const char *errstr; 1008 int flags, j; 1009 1010 flags = 0; 1011 1012 if (argc) { 1013 optreset = 1; 1014 optind = 1; 1015 while ((j = getopt(argc, argv, "r")) != -1) { 1016 switch (j) { 1017 case 'r': 1018 flags |= GV_FLAG_R; 1019 break; 1020 default: 1021 return; 1022 } 1023 } 1024 argc -= optind; 1025 argv += optind; 1026 } 1027 1028 switch (argc) { 1029 case 0: 1030 warnx("no object to rename specified"); 1031 return; 1032 case 1: 1033 warnx("no new name specified"); 1034 return; 1035 case 2: 1036 break; 1037 default: 1038 warnx("more than one new name specified"); 1039 return; 1040 } 1041 1042 req = gctl_get_handle(); 1043 gctl_ro_param(req, "class", -1, "VINUM"); 1044 gctl_ro_param(req, "verb", -1, "rename"); 1045 gctl_ro_param(req, "flags", sizeof(int), &flags); 1046 gctl_ro_param(req, "object", -1, argv[0]); 1047 gctl_ro_param(req, "newname", -1, argv[1]); 1048 errstr = gctl_issue(req); 1049 if (errstr != NULL) 1050 warnx("can't rename object: %s", errstr); 1051 gctl_free(req); 1052 } 1053 1054 static void 1055 gvinum_rm(int argc, char * const *argv) 1056 { 1057 struct gctl_req *req; 1058 int flags, i, j; 1059 const char *errstr; 1060 char buf[20]; 1061 1062 flags = 0; 1063 optreset = 1; 1064 optind = 1; 1065 while ((j = getopt(argc, argv, "rf")) != -1) { 1066 switch (j) { 1067 case 'f': 1068 flags |= GV_FLAG_F; 1069 break; 1070 case 'r': 1071 flags |= GV_FLAG_R; 1072 break; 1073 default: 1074 return; 1075 } 1076 } 1077 argc -= optind; 1078 argv += optind; 1079 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); 1085 if (argc) { 1086 for (i = 0; i < argc; i++) { 1087 snprintf(buf, sizeof(buf), "argv%d", i); 1088 gctl_ro_param(req, buf, -1, argv[i]); 1089 } 1090 } 1091 errstr = gctl_issue(req); 1092 if (errstr != NULL) { 1093 warnx("can't remove: %s", errstr); 1094 gctl_free(req); 1095 return; 1096 } 1097 gctl_free(req); 1098 } 1099 1100 static void 1101 gvinum_resetconfig(int argc, char * const *argv) 1102 { 1103 struct gctl_req *req; 1104 const char *errstr; 1105 char reply[32]; 1106 int flags, i; 1107 1108 flags = 0; 1109 while ((i = getopt(argc, argv, "f")) != -1) { 1110 switch (i) { 1111 case 'f': 1112 flags |= GV_FLAG_F; 1113 break; 1114 default: 1115 warn("invalid flag: %c", i); 1116 return; 1117 } 1118 } 1119 if ((flags & GV_FLAG_F) == 0) { 1120 if (!isatty(STDIN_FILENO)) { 1121 warn("Please enter this command from a tty device\n"); 1122 return; 1123 } 1124 printf(" WARNING! This command will completely wipe out" 1125 " your gvinum configuration.\n" 1126 " All data will be lost. If you really want to do this," 1127 " enter the text\n\n" 1128 " NO FUTURE\n" 1129 " Enter text -> "); 1130 fgets(reply, sizeof(reply), stdin); 1131 if (strcmp(reply, "NO FUTURE\n")) { 1132 printf("\n No change\n"); 1133 return; 1134 } 1135 } 1136 req = gctl_get_handle(); 1137 gctl_ro_param(req, "class", -1, "VINUM"); 1138 gctl_ro_param(req, "verb", -1, "resetconfig"); 1139 errstr = gctl_issue(req); 1140 if (errstr != NULL) { 1141 warnx("can't reset config: %s", errstr); 1142 gctl_free(req); 1143 return; 1144 } 1145 gctl_free(req); 1146 printf("gvinum configuration obliterated\n"); 1147 } 1148 1149 static void 1150 gvinum_saveconfig(void) 1151 { 1152 struct gctl_req *req; 1153 const char *errstr; 1154 1155 req = gctl_get_handle(); 1156 gctl_ro_param(req, "class", -1, "VINUM"); 1157 gctl_ro_param(req, "verb", -1, "saveconfig"); 1158 errstr = gctl_issue(req); 1159 if (errstr != NULL) 1160 warnx("can't save configuration: %s", errstr); 1161 gctl_free(req); 1162 } 1163 1164 static void 1165 gvinum_start(int argc, char * const *argv) 1166 { 1167 struct gctl_req *req; 1168 int i, initsize, j; 1169 const char *errstr; 1170 char buf[20]; 1171 1172 /* 'start' with no arguments is a no-op. */ 1173 if (argc == 1) 1174 return; 1175 1176 initsize = 0; 1177 1178 optreset = 1; 1179 optind = 1; 1180 while ((j = getopt(argc, argv, "S")) != -1) { 1181 switch (j) { 1182 case 'S': 1183 initsize = atoi(optarg); 1184 break; 1185 default: 1186 return; 1187 } 1188 } 1189 argc -= optind; 1190 argv += optind; 1191 1192 if (!initsize) 1193 initsize = 512; 1194 1195 req = gctl_get_handle(); 1196 gctl_ro_param(req, "class", -1, "VINUM"); 1197 gctl_ro_param(req, "verb", -1, "start"); 1198 gctl_ro_param(req, "argc", sizeof(int), &argc); 1199 gctl_ro_param(req, "initsize", sizeof(int), &initsize); 1200 if (argc) { 1201 for (i = 0; i < argc; i++) { 1202 snprintf(buf, sizeof(buf), "argv%d", i); 1203 gctl_ro_param(req, buf, -1, argv[i]); 1204 } 1205 } 1206 errstr = gctl_issue(req); 1207 if (errstr != NULL) { 1208 warnx("can't start: %s", errstr); 1209 gctl_free(req); 1210 return; 1211 } 1212 1213 gctl_free(req); 1214 } 1215 1216 static void 1217 gvinum_stop(int argc, char * const *argv) 1218 { 1219 int err, fileid; 1220 1221 fileid = kldfind(GVINUMKLD); 1222 if (fileid == -1) { 1223 if (modfind(GVINUMMOD) < 0) 1224 warn("cannot find " GVINUMKLD); 1225 return; 1226 } 1227 1228 /* 1229 * This little hack prevents that we end up in an infinite loop in 1230 * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM 1231 * event thread will be free for the g_wither_geom() call from 1232 * gv_unload(). It's silly, but it works. 1233 */ 1234 printf("unloading " GVINUMKLD " kernel module... "); 1235 fflush(stdout); 1236 if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) { 1237 sleep(1); 1238 err = kldunload(fileid); 1239 } 1240 if (err != 0) { 1241 printf(" failed!\n"); 1242 warn("cannot unload " GVINUMKLD); 1243 return; 1244 } 1245 1246 printf("done\n"); 1247 exit(0); 1248 } 1249 1250 /* Create a striped volume. */ 1251 static void 1252 gvinum_stripe(int argc, char * const *argv) 1253 { 1254 1255 if (argc < 2) { 1256 warnx("usage:\tstripe [-fv] [-n name] drives\n"); 1257 return; 1258 } 1259 create_volume(argc, argv, "stripe"); 1260 } 1261 1262 /* Grow a subdisk by adding disk backed by provider. */ 1263 static void 1264 gvinum_grow(int argc, char * const *argv) 1265 { 1266 struct gctl_req *req; 1267 char *drive, *sdname; 1268 char sdprefix[GV_MAXSDNAME]; 1269 struct gv_drive *d; 1270 struct gv_sd *s; 1271 const char *errstr; 1272 int drives, volumes, plexes, subdisks, flags; 1273 1274 flags = 0; 1275 drives = volumes = plexes = subdisks = 0; 1276 if (argc < 3) { 1277 warnx("usage:\tgrow plex drive\n"); 1278 return; 1279 } 1280 1281 s = gv_alloc_sd(); 1282 if (s == NULL) { 1283 warn("unable to create subdisk"); 1284 return; 1285 } 1286 d = gv_alloc_drive(); 1287 if (d == NULL) { 1288 warn("unable to create drive"); 1289 free(s); 1290 return; 1291 } 1292 /* Lookup device and set an appropriate drive name. */ 1293 drive = find_drive(); 1294 if (drive == NULL) { 1295 warn("unable to find an appropriate drive name"); 1296 free(s); 1297 free(d); 1298 return; 1299 } 1300 strlcpy(d->name, drive, sizeof(d->name)); 1301 copy_device(d, argv[2]); 1302 1303 drives = 1; 1304 1305 /* We try to use the plex name as basis for the subdisk name. */ 1306 snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]); 1307 sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME); 1308 if (sdname == NULL) { 1309 warn("unable to find an appropriate subdisk name"); 1310 free(s); 1311 free(d); 1312 free(drive); 1313 return; 1314 } 1315 strlcpy(s->name, sdname, sizeof(s->name)); 1316 free(sdname); 1317 strlcpy(s->plex, argv[1], sizeof(s->plex)); 1318 strlcpy(s->drive, d->name, sizeof(s->drive)); 1319 subdisks = 1; 1320 1321 req = gctl_get_handle(); 1322 gctl_ro_param(req, "class", -1, "VINUM"); 1323 gctl_ro_param(req, "verb", -1, "create"); 1324 gctl_ro_param(req, "flags", sizeof(int), &flags); 1325 gctl_ro_param(req, "volumes", sizeof(int), &volumes); 1326 gctl_ro_param(req, "plexes", sizeof(int), &plexes); 1327 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 1328 gctl_ro_param(req, "drives", sizeof(int), &drives); 1329 gctl_ro_param(req, "drive0", sizeof(*d), d); 1330 gctl_ro_param(req, "sd0", sizeof(*s), s); 1331 errstr = gctl_issue(req); 1332 free(drive); 1333 if (errstr != NULL) { 1334 warnx("unable to grow plex: %s", errstr); 1335 free(s); 1336 free(d); 1337 return; 1338 } 1339 gctl_free(req); 1340 } 1341 1342 static void 1343 parseline(int argc, char * const *argv) 1344 { 1345 1346 if (argc <= 0) 1347 return; 1348 1349 if (!strcmp(argv[0], "create")) 1350 gvinum_create(argc, argv); 1351 else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) 1352 exit(0); 1353 else if (!strcmp(argv[0], "attach")) 1354 gvinum_attach(argc, argv); 1355 else if (!strcmp(argv[0], "detach")) 1356 gvinum_detach(argc, argv); 1357 else if (!strcmp(argv[0], "concat")) 1358 gvinum_concat(argc, argv); 1359 else if (!strcmp(argv[0], "grow")) 1360 gvinum_grow(argc, argv); 1361 else if (!strcmp(argv[0], "help")) 1362 gvinum_help(); 1363 else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) 1364 gvinum_list(argc, argv); 1365 else if (!strcmp(argv[0], "ld")) 1366 gvinum_list(argc, argv); 1367 else if (!strcmp(argv[0], "lp")) 1368 gvinum_list(argc, argv); 1369 else if (!strcmp(argv[0], "ls")) 1370 gvinum_list(argc, argv); 1371 else if (!strcmp(argv[0], "lv")) 1372 gvinum_list(argc, argv); 1373 else if (!strcmp(argv[0], "mirror")) 1374 gvinum_mirror(argc, argv); 1375 else if (!strcmp(argv[0], "move")) 1376 gvinum_move(argc, argv); 1377 else if (!strcmp(argv[0], "mv")) 1378 gvinum_move(argc, argv); 1379 else if (!strcmp(argv[0], "printconfig")) 1380 gvinum_printconfig(argc, argv); 1381 else if (!strcmp(argv[0], "raid5")) 1382 gvinum_raid5(argc, argv); 1383 else if (!strcmp(argv[0], "rename")) 1384 gvinum_rename(argc, argv); 1385 else if (!strcmp(argv[0], "resetconfig")) 1386 gvinum_resetconfig(argc, argv); 1387 else if (!strcmp(argv[0], "rm")) 1388 gvinum_rm(argc, argv); 1389 else if (!strcmp(argv[0], "saveconfig")) 1390 gvinum_saveconfig(); 1391 else if (!strcmp(argv[0], "setstate")) 1392 gvinum_setstate(argc, argv); 1393 else if (!strcmp(argv[0], "start")) 1394 gvinum_start(argc, argv); 1395 else if (!strcmp(argv[0], "stop")) 1396 gvinum_stop(argc, argv); 1397 else if (!strcmp(argv[0], "stripe")) 1398 gvinum_stripe(argc, argv); 1399 else if (!strcmp(argv[0], "checkparity")) 1400 gvinum_parityop(argc, argv, 0); 1401 else if (!strcmp(argv[0], "rebuildparity")) 1402 gvinum_parityop(argc, argv, 1); 1403 else 1404 printf("unknown command '%s'\n", argv[0]); 1405 } 1406 1407 /* 1408 * The guts of printconfig. This is called from gvinum_printconfig and from 1409 * gvinum_create when called without an argument, in order to give the user 1410 * something to edit. 1411 */ 1412 static void 1413 printconfig(FILE *of, const char *comment) 1414 { 1415 struct gctl_req *req; 1416 struct utsname uname_s; 1417 const char *errstr; 1418 time_t now; 1419 char buf[GV_CFG_LEN + 1]; 1420 1421 uname(&uname_s); 1422 time(&now); 1423 1424 req = gctl_get_handle(); 1425 gctl_ro_param(req, "class", -1, "VINUM"); 1426 gctl_ro_param(req, "verb", -1, "getconfig"); 1427 gctl_ro_param(req, "comment", -1, comment); 1428 gctl_rw_param(req, "config", sizeof(buf), buf); 1429 errstr = gctl_issue(req); 1430 if (errstr != NULL) { 1431 warnx("can't get configuration: %s", errstr); 1432 return; 1433 } 1434 gctl_free(req); 1435 1436 fprintf(of, "# Vinum configuration of %s, saved at %s", 1437 uname_s.nodename, 1438 ctime(&now)); 1439 1440 if (*comment != '\0') 1441 fprintf(of, "# Current configuration:\n"); 1442 1443 fprintf(of, "%s", buf); 1444 } 1445