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