1 /* 2 * Copyright (c) 2004 Lukas Ertl, 2005 Chris Jones 3 * All rights reserved. 4 * 5 * Portions of this software were developed for the FreeBSD Project 6 * by Chris Jones thanks to the support of Google's Summer of Code 7 * program and mentoring by Lukas Ertl. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 */ 32 33 #include <sys/param.h> 34 #include <sys/linker.h> 35 #include <sys/lock.h> 36 #include <sys/module.h> 37 #include <sys/mutex.h> 38 #include <sys/queue.h> 39 #include <sys/utsname.h> 40 41 #include <geom/vinum/geom_vinum_var.h> 42 #include <geom/vinum/geom_vinum_share.h> 43 44 #include <ctype.h> 45 #include <err.h> 46 #include <libgeom.h> 47 #include <stdint.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <paths.h> 51 #include <readline/readline.h> 52 #include <readline/history.h> 53 #include <unistd.h> 54 55 #include "gvinum.h" 56 57 void gvinum_create(int, char **); 58 void gvinum_help(void); 59 void gvinum_list(int, char **); 60 void gvinum_move(int, char **); 61 void gvinum_parityop(int, char **, int); 62 void gvinum_printconfig(int, char **); 63 void gvinum_rename(int, char **); 64 void gvinum_resetconfig(void); 65 void gvinum_rm(int, char **); 66 void gvinum_saveconfig(void); 67 void gvinum_setstate(int, char **); 68 void gvinum_start(int, char **); 69 void gvinum_stop(int, char **); 70 void parseline(int, char **); 71 void printconfig(FILE *, char *); 72 73 int 74 main(int argc, char **argv) 75 { 76 int line, tokens; 77 char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS]; 78 79 /* Load the module if necessary. */ 80 if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0) 81 err(1, GVINUMMOD ": Kernel module not available"); 82 83 /* Arguments given on the command line. */ 84 if (argc > 1) { 85 argc--; 86 argv++; 87 parseline(argc, argv); 88 89 /* Interactive mode. */ 90 } else { 91 for (;;) { 92 inputline = readline("gvinum -> "); 93 if (inputline == NULL) { 94 if (ferror(stdin)) { 95 err(1, "can't read input"); 96 } else { 97 printf("\n"); 98 exit(0); 99 } 100 } else if (*inputline) { 101 add_history(inputline); 102 strcpy(buffer, inputline); 103 free(inputline); 104 line++; /* count the lines */ 105 tokens = gv_tokenize(buffer, token, GV_MAXARGS); 106 if (tokens) 107 parseline(tokens, token); 108 } 109 } 110 } 111 exit(0); 112 } 113 114 void 115 gvinum_create(int argc, char **argv) 116 { 117 struct gctl_req *req; 118 struct gv_drive *d; 119 struct gv_plex *p; 120 struct gv_sd *s; 121 struct gv_volume *v; 122 FILE *tmp; 123 int drives, errors, fd, line, plexes, plex_in_volume; 124 int sd_in_plex, status, subdisks, tokens, volumes; 125 const char *errstr; 126 char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed; 127 char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS]; 128 char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME]; 129 130 if (argc == 2) { 131 if ((tmp = fopen(argv[1], "r")) == NULL) { 132 warn("can't open '%s' for reading", argv[1]); 133 return; 134 } 135 } else { 136 snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX"); 137 138 if ((fd = mkstemp(tmpfile)) == -1) { 139 warn("temporary file not accessible"); 140 return; 141 } 142 if ((tmp = fdopen(fd, "w")) == NULL) { 143 warn("can't open '%s' for writing", tmpfile); 144 return; 145 } 146 printconfig(tmp, "# "); 147 fclose(tmp); 148 149 ed = getenv("EDITOR"); 150 if (ed == NULL) 151 ed = _PATH_VI; 152 153 snprintf(commandline, sizeof(commandline), "%s %s", ed, 154 tmpfile); 155 status = system(commandline); 156 if (status != 0) { 157 warn("couldn't exec %s; status: %d", ed, status); 158 return; 159 } 160 161 if ((tmp = fopen(tmpfile, "r")) == NULL) { 162 warn("can't open '%s' for reading", tmpfile); 163 return; 164 } 165 } 166 167 req = gctl_get_handle(); 168 gctl_ro_param(req, "class", -1, "VINUM"); 169 gctl_ro_param(req, "verb", -1, "create"); 170 171 drives = volumes = plexes = subdisks = 0; 172 plex_in_volume = sd_in_plex = 0; 173 errors = 0; 174 line = 1; 175 while ((fgets(buf, BUFSIZ, tmp)) != NULL) { 176 177 /* Skip empty lines and comments. */ 178 if (*buf == '\0' || *buf == '#') { 179 line++; 180 continue; 181 } 182 183 /* Kill off the newline. */ 184 buf[strlen(buf) - 1] = '\0'; 185 186 /* 187 * Copy the original input line in case we need it for error 188 * output. 189 */ 190 strncpy(original, buf, sizeof(buf)); 191 192 tokens = gv_tokenize(buf, token, GV_MAXARGS); 193 if (tokens <= 0) { 194 line++; 195 continue; 196 } 197 198 /* Volume definition. */ 199 if (!strcmp(token[0], "volume")) { 200 v = gv_new_volume(tokens, token); 201 if (v == NULL) { 202 warnx("line %d: invalid volume definition", 203 line); 204 warnx("line %d: '%s'", line, original); 205 errors++; 206 line++; 207 continue; 208 } 209 210 /* Reset plex count for this volume. */ 211 plex_in_volume = 0; 212 213 /* 214 * Set default volume name for following plex 215 * definitions. 216 */ 217 strncpy(volume, v->name, sizeof(volume)); 218 219 snprintf(buf1, sizeof(buf1), "volume%d", volumes); 220 gctl_ro_param(req, buf1, sizeof(*v), v); 221 volumes++; 222 223 /* Plex definition. */ 224 } else if (!strcmp(token[0], "plex")) { 225 p = gv_new_plex(tokens, token); 226 if (p == NULL) { 227 warnx("line %d: invalid plex definition", line); 228 warnx("line %d: '%s'", line, original); 229 errors++; 230 line++; 231 continue; 232 } 233 234 /* Reset subdisk count for this plex. */ 235 sd_in_plex = 0; 236 237 /* Default name. */ 238 if (strlen(p->name) == 0) { 239 snprintf(p->name, GV_MAXPLEXNAME, "%s.p%d", 240 volume, plex_in_volume++); 241 } 242 243 /* Default volume. */ 244 if (strlen(p->volume) == 0) { 245 snprintf(p->volume, GV_MAXVOLNAME, "%s", 246 volume); 247 } 248 249 /* 250 * Set default plex name for following subdisk 251 * definitions. 252 */ 253 strncpy(plex, p->name, GV_MAXPLEXNAME); 254 255 snprintf(buf1, sizeof(buf1), "plex%d", plexes); 256 gctl_ro_param(req, buf1, sizeof(*p), p); 257 plexes++; 258 259 /* Subdisk definition. */ 260 } else if (!strcmp(token[0], "sd")) { 261 s = gv_new_sd(tokens, token); 262 if (s == NULL) { 263 warnx("line %d: invalid subdisk " 264 "definition:", line); 265 warnx("line %d: '%s'", line, original); 266 errors++; 267 line++; 268 continue; 269 } 270 271 /* Default name. */ 272 if (strlen(s->name) == 0) { 273 snprintf(s->name, GV_MAXSDNAME, "%s.s%d", 274 plex, sd_in_plex++); 275 } 276 277 /* Default plex. */ 278 if (strlen(s->plex) == 0) 279 snprintf(s->plex, GV_MAXPLEXNAME, "%s", plex); 280 281 snprintf(buf1, sizeof(buf1), "sd%d", subdisks); 282 gctl_ro_param(req, buf1, sizeof(*s), s); 283 subdisks++; 284 285 /* Subdisk definition. */ 286 } else if (!strcmp(token[0], "drive")) { 287 d = gv_new_drive(tokens, token); 288 if (d == NULL) { 289 warnx("line %d: invalid drive definition:", 290 line); 291 warnx("line %d: '%s'", line, original); 292 errors++; 293 line++; 294 continue; 295 } 296 297 snprintf(buf1, sizeof(buf1), "drive%d", drives); 298 gctl_ro_param(req, buf1, sizeof(*d), d); 299 drives++; 300 301 /* Everything else is bogus. */ 302 } else { 303 warnx("line %d: invalid definition:", line); 304 warnx("line %d: '%s'", line, original); 305 errors++; 306 } 307 line++; 308 } 309 310 fclose(tmp); 311 unlink(tmpfile); 312 313 if (!errors && (volumes || plexes || subdisks || drives)) { 314 gctl_ro_param(req, "volumes", sizeof(int), &volumes); 315 gctl_ro_param(req, "plexes", sizeof(int), &plexes); 316 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 317 gctl_ro_param(req, "drives", sizeof(int), &drives); 318 errstr = gctl_issue(req); 319 if (errstr != NULL) 320 warnx("create failed: %s", errstr); 321 } 322 gctl_free(req); 323 gvinum_list(0, NULL); 324 } 325 326 void 327 gvinum_help(void) 328 { 329 printf("COMMANDS\n" 330 "checkparity [-f] plex\n" 331 " Check the parity blocks of a RAID-5 plex.\n" 332 "create description-file\n" 333 " Create as per description-file or open editor.\n" 334 "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" 335 " List information about specified objects.\n" 336 "ld [-r] [-v] [-V] [volume]\n" 337 " List information about drives.\n" 338 "ls [-r] [-v] [-V] [subdisk]\n" 339 " List information about subdisks.\n" 340 "lp [-r] [-v] [-V] [plex]\n" 341 " List information about plexes.\n" 342 "lv [-r] [-v] [-V] [volume]\n" 343 " List information about volumes.\n" 344 "move | mv -f drive object ...\n" 345 " Move the object(s) to the specified drive.\n" 346 "quit Exit the vinum program when running in interactive mode." 347 " Nor-\n" 348 " mally this would be done by entering the EOF character.\n" 349 "rename [-r] [drive | subdisk | plex | volume] newname\n" 350 " Change the name of the specified object.\n" 351 "rebuildparity plex [-f]\n" 352 " Rebuild the parity blocks of a RAID-5 plex.\n" 353 "resetconfig\n" 354 " Reset the complete gvinum configuration\n" 355 "rm [-r] volume | plex | subdisk | drive\n" 356 " Remove an object.\n" 357 "saveconfig\n" 358 " Save vinum configuration to disk after configuration" 359 " failures.\n" 360 "setstate [-f] state [volume | plex | subdisk | drive]\n" 361 " Set state without influencing other objects, for" 362 " diagnostic pur-\n" 363 " poses only.\n" 364 "start [-S size] volume | plex | subdisk\n" 365 " Allow the system to access the objects.\n" 366 ); 367 368 return; 369 } 370 371 void 372 gvinum_setstate(int argc, char **argv) 373 { 374 struct gctl_req *req; 375 int flags, i; 376 const char *errstr; 377 378 flags = 0; 379 380 optreset = 1; 381 optind = 1; 382 383 while ((i = getopt(argc, argv, "f")) != -1) { 384 switch (i) { 385 case 'f': 386 flags |= GV_FLAG_F; 387 break; 388 case '?': 389 default: 390 warn("invalid flag: %c", i); 391 return; 392 } 393 } 394 395 argc -= optind; 396 argv += optind; 397 398 if (argc != 2) { 399 warnx("usage: setstate [-f] <state> <obj>"); 400 return; 401 } 402 403 /* 404 * XXX: This hack is needed to avoid tripping over (now) invalid 405 * 'classic' vinum states and will go away later. 406 */ 407 if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && 408 strcmp(argv[0], "stale")) { 409 warnx("invalid state '%s'", argv[0]); 410 return; 411 } 412 413 req = gctl_get_handle(); 414 gctl_ro_param(req, "class", -1, "VINUM"); 415 gctl_ro_param(req, "verb", -1, "setstate"); 416 gctl_ro_param(req, "state", -1, argv[0]); 417 gctl_ro_param(req, "object", -1, argv[1]); 418 gctl_ro_param(req, "flags", sizeof(int), &flags); 419 420 errstr = gctl_issue(req); 421 if (errstr != NULL) 422 warnx("%s", errstr); 423 gctl_free(req); 424 } 425 426 void 427 gvinum_list(int argc, char **argv) 428 { 429 struct gctl_req *req; 430 int flags, i, j; 431 const char *errstr; 432 char buf[20], *cmd, config[GV_CFG_LEN + 1]; 433 434 flags = 0; 435 cmd = "list"; 436 437 if (argc) { 438 optreset = 1; 439 optind = 1; 440 cmd = argv[0]; 441 while ((j = getopt(argc, argv, "rsvV")) != -1) { 442 switch (j) { 443 case 'r': 444 flags |= GV_FLAG_R; 445 break; 446 case 's': 447 flags |= GV_FLAG_S; 448 break; 449 case 'v': 450 flags |= GV_FLAG_V; 451 break; 452 case 'V': 453 flags |= GV_FLAG_V; 454 flags |= GV_FLAG_VV; 455 break; 456 case '?': 457 default: 458 return; 459 } 460 } 461 argc -= optind; 462 argv += optind; 463 464 } 465 466 req = gctl_get_handle(); 467 gctl_ro_param(req, "class", -1, "VINUM"); 468 gctl_ro_param(req, "verb", -1, "list"); 469 gctl_ro_param(req, "cmd", -1, cmd); 470 gctl_ro_param(req, "argc", sizeof(int), &argc); 471 gctl_ro_param(req, "flags", sizeof(int), &flags); 472 gctl_rw_param(req, "config", sizeof(config), config); 473 if (argc) { 474 for (i = 0; i < argc; i++) { 475 snprintf(buf, sizeof(buf), "argv%d", i); 476 gctl_ro_param(req, buf, -1, argv[i]); 477 } 478 } 479 errstr = gctl_issue(req); 480 if (errstr != NULL) { 481 warnx("can't get configuration: %s", errstr); 482 gctl_free(req); 483 return; 484 } 485 486 printf("%s", config); 487 gctl_free(req); 488 return; 489 } 490 491 /* Note that move is currently of form '[-r] target object [...]' */ 492 void 493 gvinum_move(int argc, char **argv) 494 { 495 struct gctl_req *req; 496 const char *errstr; 497 char buf[20]; 498 int flags, i, j; 499 500 flags = 0; 501 if (argc) { 502 optreset = 1; 503 optind = 1; 504 while ((j = getopt(argc, argv, "f")) != -1) { 505 switch (j) { 506 case 'f': 507 flags |= GV_FLAG_F; 508 break; 509 case '?': 510 default: 511 return; 512 } 513 } 514 argc -= optind; 515 argv += optind; 516 } 517 518 switch (argc) { 519 case 0: 520 warnx("no destination or object(s) to move specified"); 521 return; 522 case 1: 523 warnx("no object(s) to move specified"); 524 return; 525 default: 526 break; 527 } 528 529 req = gctl_get_handle(); 530 gctl_ro_param(req, "class", -1, "VINUM"); 531 gctl_ro_param(req, "verb", -1, "move"); 532 gctl_ro_param(req, "argc", sizeof(int), &argc); 533 gctl_ro_param(req, "flags", sizeof(int), &flags); 534 gctl_ro_param(req, "destination", -1, argv[0]); 535 for (i = 1; i < argc; i++) { 536 snprintf(buf, sizeof(buf), "argv%d", i); 537 gctl_ro_param(req, buf, -1, argv[i]); 538 } 539 errstr = gctl_issue(req); 540 if (errstr != NULL) 541 warnx("can't move object(s): %s", errstr); 542 gctl_free(req); 543 return; 544 } 545 546 void 547 gvinum_printconfig(int argc, char **argv) 548 { 549 printconfig(stdout, ""); 550 } 551 552 void 553 gvinum_parityop(int argc, char **argv, int rebuild) 554 { 555 struct gctl_req *req; 556 int flags, i, rv; 557 off_t offset; 558 const char *errstr; 559 char *op, *msg; 560 561 if (rebuild) { 562 op = "rebuildparity"; 563 msg = "Rebuilding"; 564 } else { 565 op = "checkparity"; 566 msg = "Checking"; 567 } 568 569 optreset = 1; 570 optind = 1; 571 flags = 0; 572 while ((i = getopt(argc, argv, "fv")) != -1) { 573 switch (i) { 574 case 'f': 575 flags |= GV_FLAG_F; 576 break; 577 case 'v': 578 flags |= GV_FLAG_V; 579 break; 580 case '?': 581 default: 582 warnx("invalid flag '%c'", i); 583 return; 584 } 585 } 586 argc -= optind; 587 argv += optind; 588 589 if (argc != 1) { 590 warn("usage: %s [-f] [-v] <plex>", op); 591 return; 592 } 593 594 do { 595 rv = 0; 596 req = gctl_get_handle(); 597 gctl_ro_param(req, "class", -1, "VINUM"); 598 gctl_ro_param(req, "verb", -1, "parityop"); 599 gctl_ro_param(req, "flags", sizeof(int), &flags); 600 gctl_ro_param(req, "rebuild", sizeof(int), &rebuild); 601 gctl_rw_param(req, "rv", sizeof(int), &rv); 602 gctl_rw_param(req, "offset", sizeof(off_t), &offset); 603 gctl_ro_param(req, "plex", -1, argv[0]); 604 errstr = gctl_issue(req); 605 if (errstr) { 606 warnx("%s\n", errstr); 607 gctl_free(req); 608 break; 609 } 610 gctl_free(req); 611 if (flags & GV_FLAG_V) { 612 printf("\r%s at %s ... ", msg, 613 gv_roughlength(offset, 1)); 614 } 615 if (rv == 1) { 616 printf("Parity incorrect at offset 0x%jx\n", 617 (intmax_t)offset); 618 if (!rebuild) 619 break; 620 } 621 fflush(stdout); 622 623 /* Clear the -f flag. */ 624 flags &= ~GV_FLAG_F; 625 } while (rv >= 0); 626 627 if ((rv == 2) && (flags & GV_FLAG_V)) { 628 if (rebuild) 629 printf("Rebuilt parity on %s\n", argv[0]); 630 else 631 printf("%s has correct parity\n", argv[0]); 632 } 633 } 634 635 void 636 gvinum_rename(int argc, char **argv) 637 { 638 struct gctl_req *req; 639 const char *errstr; 640 int flags, j; 641 642 flags = 0; 643 644 if (argc) { 645 optreset = 1; 646 optind = 1; 647 while ((j = getopt(argc, argv, "r")) != -1) { 648 switch (j) { 649 case 'r': 650 flags |= GV_FLAG_R; 651 break; 652 case '?': 653 default: 654 return; 655 } 656 } 657 argc -= optind; 658 argv += optind; 659 } 660 661 switch (argc) { 662 case 0: 663 warnx("no object to rename specified"); 664 return; 665 case 1: 666 warnx("no new name specified"); 667 return; 668 case 2: 669 break; 670 default: 671 warnx("more than one new name specified"); 672 return; 673 } 674 675 req = gctl_get_handle(); 676 gctl_ro_param(req, "class", -1, "VINUM"); 677 gctl_ro_param(req, "verb", -1, "rename"); 678 gctl_ro_param(req, "flags", sizeof(int), &flags); 679 gctl_ro_param(req, "object", -1, argv[0]); 680 gctl_ro_param(req, "newname", -1, argv[1]); 681 errstr = gctl_issue(req); 682 if (errstr != NULL) 683 warnx("can't rename object: %s", errstr); 684 gctl_free(req); 685 return; 686 } 687 688 void 689 gvinum_rm(int argc, char **argv) 690 { 691 struct gctl_req *req; 692 int flags, i, j; 693 const char *errstr; 694 char buf[20], *cmd; 695 696 cmd = argv[0]; 697 flags = 0; 698 optreset = 1; 699 optind = 1; 700 while ((j = getopt(argc, argv, "r")) != -1) { 701 switch (j) { 702 case 'r': 703 flags |= GV_FLAG_R; 704 break; 705 case '?': 706 default: 707 return; 708 } 709 } 710 argc -= optind; 711 argv += optind; 712 713 req = gctl_get_handle(); 714 gctl_ro_param(req, "class", -1, "VINUM"); 715 gctl_ro_param(req, "verb", -1, "remove"); 716 gctl_ro_param(req, "argc", sizeof(int), &argc); 717 gctl_ro_param(req, "flags", sizeof(int), &flags); 718 if (argc) { 719 for (i = 0; i < argc; i++) { 720 snprintf(buf, sizeof(buf), "argv%d", i); 721 gctl_ro_param(req, buf, -1, argv[i]); 722 } 723 } 724 errstr = gctl_issue(req); 725 if (errstr != NULL) { 726 warnx("can't remove: %s", errstr); 727 gctl_free(req); 728 return; 729 } 730 gctl_free(req); 731 gvinum_list(0, NULL); 732 } 733 734 void 735 gvinum_resetconfig(void) 736 { 737 struct gctl_req *req; 738 const char *errstr; 739 char reply[32]; 740 741 if (!isatty(STDIN_FILENO)) { 742 warn("Please enter this command from a tty device\n"); 743 return; 744 } 745 printf(" WARNING! This command will completely wipe out your gvinum" 746 "configuration.\n" 747 " All data will be lost. If you really want to do this," 748 " enter the text\n\n" 749 " NO FUTURE\n" 750 " Enter text -> "); 751 fgets(reply, sizeof(reply), stdin); 752 if (strcmp(reply, "NO FUTURE\n")) { 753 printf("\n No change\n"); 754 return; 755 } 756 req = gctl_get_handle(); 757 gctl_ro_param(req, "class", -1, "VINUM"); 758 gctl_ro_param(req, "verb", -1, "resetconfig"); 759 errstr = gctl_issue(req); 760 if (errstr != NULL) { 761 warnx("can't reset config: %s", errstr); 762 gctl_free(req); 763 return; 764 } 765 gctl_free(req); 766 gvinum_list(0, NULL); 767 printf("gvinum configuration obliterated\n"); 768 } 769 770 void 771 gvinum_saveconfig(void) 772 { 773 struct gctl_req *req; 774 const char *errstr; 775 776 req = gctl_get_handle(); 777 gctl_ro_param(req, "class", -1, "VINUM"); 778 gctl_ro_param(req, "verb", -1, "saveconfig"); 779 errstr = gctl_issue(req); 780 if (errstr != NULL) 781 warnx("can't save configuration: %s", errstr); 782 gctl_free(req); 783 } 784 785 void 786 gvinum_start(int argc, char **argv) 787 { 788 struct gctl_req *req; 789 int i, initsize, j; 790 const char *errstr; 791 char buf[20]; 792 793 /* 'start' with no arguments is a no-op. */ 794 if (argc == 1) 795 return; 796 797 initsize = 0; 798 799 optreset = 1; 800 optind = 1; 801 while ((j = getopt(argc, argv, "S")) != -1) { 802 switch (j) { 803 case 'S': 804 initsize = atoi(optarg); 805 break; 806 case '?': 807 default: 808 return; 809 } 810 } 811 argc -= optind; 812 argv += optind; 813 814 if (!initsize) 815 initsize = 512; 816 817 req = gctl_get_handle(); 818 gctl_ro_param(req, "class", -1, "VINUM"); 819 gctl_ro_param(req, "verb", -1, "start"); 820 gctl_ro_param(req, "argc", sizeof(int), &argc); 821 gctl_ro_param(req, "initsize", sizeof(int), &initsize); 822 if (argc) { 823 for (i = 0; i < argc; i++) { 824 snprintf(buf, sizeof(buf), "argv%d", i); 825 gctl_ro_param(req, buf, -1, argv[i]); 826 } 827 } 828 errstr = gctl_issue(req); 829 if (errstr != NULL) { 830 warnx("can't start: %s", errstr); 831 gctl_free(req); 832 return; 833 } 834 835 gctl_free(req); 836 gvinum_list(0, NULL); 837 } 838 839 void 840 gvinum_stop(int argc, char **argv) 841 { 842 int fileid; 843 844 fileid = kldfind(GVINUMMOD); 845 if (fileid == -1) { 846 warn("cannot find " GVINUMMOD); 847 return; 848 } 849 if (kldunload(fileid) != 0) { 850 warn("cannot unload " GVINUMMOD); 851 return; 852 } 853 854 warnx(GVINUMMOD " unloaded"); 855 exit(0); 856 } 857 858 void 859 parseline(int argc, char **argv) 860 { 861 if (argc <= 0) 862 return; 863 864 if (!strcmp(argv[0], "create")) 865 gvinum_create(argc, argv); 866 else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) 867 exit(0); 868 else if (!strcmp(argv[0], "help")) 869 gvinum_help(); 870 else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) 871 gvinum_list(argc, argv); 872 else if (!strcmp(argv[0], "ld")) 873 gvinum_list(argc, argv); 874 else if (!strcmp(argv[0], "lp")) 875 gvinum_list(argc, argv); 876 else if (!strcmp(argv[0], "ls")) 877 gvinum_list(argc, argv); 878 else if (!strcmp(argv[0], "lv")) 879 gvinum_list(argc, argv); 880 else if (!strcmp(argv[0], "move")) 881 gvinum_move(argc, argv); 882 else if (!strcmp(argv[0], "mv")) 883 gvinum_move(argc, argv); 884 else if (!strcmp(argv[0], "printconfig")) 885 gvinum_printconfig(argc, argv); 886 else if (!strcmp(argv[0], "rename")) 887 gvinum_rename(argc, argv); 888 else if (!strcmp(argv[0], "resetconfig")) 889 gvinum_resetconfig(); 890 else if (!strcmp(argv[0], "rm")) 891 gvinum_rm(argc, argv); 892 else if (!strcmp(argv[0], "saveconfig")) 893 gvinum_saveconfig(); 894 else if (!strcmp(argv[0], "setstate")) 895 gvinum_setstate(argc, argv); 896 else if (!strcmp(argv[0], "start")) 897 gvinum_start(argc, argv); 898 else if (!strcmp(argv[0], "stop")) 899 gvinum_stop(argc, argv); 900 else if (!strcmp(argv[0], "checkparity")) 901 gvinum_parityop(argc, argv, 0); 902 else if (!strcmp(argv[0], "rebuildparity")) 903 gvinum_parityop(argc, argv, 1); 904 else 905 printf("unknown command '%s'\n", argv[0]); 906 907 return; 908 } 909 910 /* 911 * The guts of printconfig. This is called from gvinum_printconfig and from 912 * gvinum_create when called without an argument, in order to give the user 913 * something to edit. 914 */ 915 void 916 printconfig(FILE *of, char *comment) 917 { 918 struct gctl_req *req; 919 struct utsname uname_s; 920 const char *errstr; 921 time_t now; 922 char buf[GV_CFG_LEN + 1]; 923 924 uname(&uname_s); 925 time(&now); 926 927 req = gctl_get_handle(); 928 gctl_ro_param(req, "class", -1, "VINUM"); 929 gctl_ro_param(req, "verb", -1, "getconfig"); 930 gctl_ro_param(req, "comment", -1, comment); 931 gctl_rw_param(req, "config", sizeof(buf), buf); 932 errstr = gctl_issue(req); 933 if (errstr != NULL) { 934 warnx("can't get configuration: %s", errstr); 935 return; 936 } 937 gctl_free(req); 938 939 fprintf(of, "# Vinum configuration of %s, saved at %s", 940 uname_s.nodename, 941 ctime(&now)); 942 943 if (*comment != '\0') 944 fprintf(of, "# Current configuration:\n"); 945 946 fprintf(of, buf); 947 } 948