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