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