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