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