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