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