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