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