gvinum.c revision 190881
174462Salfred/* 274462Salfred * Copyright (c) 2004 Lukas Ertl 3261046Smav * Copyright (c) 2005 Chris Jones 4261046Smav * Copyright (c) 2007 Ulf Lilleengen 5261046Smav * All rights reserved. 68870Srgrimes * 7261046Smav * Portions of this software were developed for the FreeBSD Project 8261046Smav * by Chris Jones thanks to the support of Google's Summer of Code 9261046Smav * program and mentoring by Lukas Ertl. 10261046Smav * 11261046Smav * Redistribution and use in source and binary forms, with or without 12261046Smav * modification, are permitted provided that the following conditions 13261046Smav * are met: 14261046Smav * 1. Redistributions of source code must retain the above copyright 15261046Smav * notice, this list of conditions and the following disclaimer. 16261046Smav * 2. Redistributions in binary form must reproduce the above copyright 17261046Smav * notice, this list of conditions and the following disclaimer in the 18261046Smav * documentation and/or other materials provided with the distribution. 19261046Smav * 20261046Smav * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21261046Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22261046Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23261046Smav * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 24261046Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25261046Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26261046Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27261046Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28261046Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291901Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3074462Salfred * SUCH DAMAGE. 3174462Salfred * 3274462Salfred * $FreeBSD: head/sbin/gvinum/gvinum.c 190881 2009-04-10 08:50:14Z lulf $ 331901Swollman */ 341901Swollman 35136581Sobrien#include <sys/param.h> 3692990Sobrien#include <sys/linker.h> 371901Swollman#include <sys/lock.h> 3892990Sobrien#include <sys/module.h> 3992990Sobrien#include <sys/mutex.h> 401901Swollman#include <sys/queue.h> 418870Srgrimes#include <sys/utsname.h> 421901Swollman 4374462Salfred#include <geom/vinum/geom_vinum_var.h> 441901Swollman#include <geom/vinum/geom_vinum_share.h> 451901Swollman 461901Swollman#include <ctype.h> 4775094Siedowse#include <err.h> 4874462Salfred#include <errno.h> 4921070Speter#include <libgeom.h> 501901Swollman#include <stdint.h> 5174462Salfred#include <stdio.h> 5274462Salfred#include <stdlib.h> 5374462Salfred#include <paths.h> 541901Swollman#include <readline/readline.h> 5574462Salfred#include <readline/history.h> 5611666Sphk#include <unistd.h> 5771579Sdeischen 58156090Sdeischen#include "gvinum.h" 591901Swollman 6074462Salfredvoid gvinum_attach(int, char **); 6174462Salfredvoid gvinum_concat(int, char **); 6274462Salfredvoid gvinum_create(int, char **); 631901Swollmanvoid gvinum_detach(int, char **); 6474462Salfredvoid gvinum_help(void); 6574462Salfredvoid gvinum_list(int, char **); 6674462Salfredvoid gvinum_move(int, char **); 6774462Salfredvoid gvinum_mirror(int, char **); 6874462Salfredvoid gvinum_parityop(int, char **, int); 6974462Salfredvoid gvinum_printconfig(int, char **); 7074462Salfredvoid gvinum_raid5(int, char **); 7174462Salfredvoid gvinum_rename(int, char **); 7274462Salfredvoid gvinum_resetconfig(void); 7374462Salfredvoid gvinum_rm(int, char **); 7474462Salfredvoid gvinum_saveconfig(void); 7574462Salfredvoid gvinum_setstate(int, char **); 7674462Salfredvoid gvinum_start(int, char **); 7774462Salfredvoid gvinum_stop(int, char **); 78204950Sjhbvoid gvinum_stripe(int, char **); 79204950Sjhbvoid parseline(int, char **); 80204950Sjhbvoid printconfig(FILE *, char *); 8174462Salfred 82204950Sjhbchar *create_drive(char *); 8392905Sobrienvoid create_volume(int, char **, char *); 8474462Salfredchar *find_name(const char *, int, int); 8574462Salfredchar *find_pattern(char *, char *); 8674462Salfred 871901Swollmanint 8874462Salfredmain(int argc, char **argv) 8974462Salfred{ 9074462Salfred int line, tokens; 9174462Salfred char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS]; 9274462Salfred 9374462Salfred /* Load the module if necessary. */ 9474462Salfred if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0) 9574462Salfred err(1, GVINUMMOD ": Kernel module not available"); 9674462Salfred 97204950Sjhb /* Arguments given on the command line. */ 98204950Sjhb if (argc > 1) { 99204950Sjhb argc--; 100204950Sjhb argv++; 101204950Sjhb parseline(argc, argv); 102204950Sjhb 103204950Sjhb /* Interactive mode. */ 10474462Salfred } else { 10574462Salfred for (;;) { 10674462Salfred inputline = readline("gvinum -> "); 10774462Salfred if (inputline == NULL) { 10874462Salfred if (ferror(stdin)) { 10974462Salfred err(1, "can't read input"); 11074462Salfred } else { 11174462Salfred printf("\n"); 11274462Salfred exit(0); 11374462Salfred } 11474462Salfred } else if (*inputline) { 11574462Salfred add_history(inputline); 11674462Salfred strcpy(buffer, inputline); 11774462Salfred free(inputline); 11874462Salfred line++; /* count the lines */ 11974462Salfred tokens = gv_tokenize(buffer, token, GV_MAXARGS); 12074462Salfred if (tokens) 12174462Salfred parseline(tokens, token); 12274462Salfred } 1231901Swollman } 1241901Swollman } 12574462Salfred exit(0); 1261901Swollman} 12774462Salfred 12874462Salfred/* Attach a plex to a volume or a subdisk to a plex. */ 12974462Salfredvoid 130204950Sjhbgvinum_attach(int argc, char **argv) 131204950Sjhb{ 132204950Sjhb struct gctl_req *req; 133204950Sjhb const char *errstr; 134204950Sjhb int rename; 13574462Salfred off_t offset; 13674462Salfred 1371901Swollman rename = 0; 13874462Salfred offset = -1; 13974462Salfred if (argc < 3) { 14074462Salfred warnx("usage:\tattach <subdisk> <plex> [rename] " 14174462Salfred "[<plexoffset>]\n" 14274462Salfred "\tattach <plex> <volume> [rename]"); 14374462Salfred return; 14474462Salfred } 14574462Salfred if (argc > 3) { 14674462Salfred if (!strcmp(argv[3], "rename")) { 14774462Salfred rename = 1; 14874462Salfred if (argc == 5) 14974462Salfred offset = strtol(argv[4], NULL, 0); 15074462Salfred } else 1511901Swollman offset = strtol(argv[3], NULL, 0); 152121651Smbr } 15374462Salfred req = gctl_get_handle(); 15474462Salfred gctl_ro_param(req, "class", -1, "VINUM"); 15574462Salfred gctl_ro_param(req, "verb", -1, "attach"); 15674462Salfred gctl_ro_param(req, "child", -1, argv[1]); 15774462Salfred gctl_ro_param(req, "parent", -1, argv[2]); 15874462Salfred gctl_ro_param(req, "offset", sizeof(off_t), &offset); 15974462Salfred gctl_ro_param(req, "rename", sizeof(int), &rename); 16074462Salfred errstr = gctl_issue(req); 16174462Salfred if (errstr != NULL) 16274462Salfred warnx("attach failed: %s", errstr); 16374462Salfred gctl_free(req); 16474462Salfred} 16574462Salfred 16674462Salfredvoid 16774462Salfredgvinum_create(int argc, char **argv) 16874462Salfred{ 16974462Salfred struct gctl_req *req; 17074462Salfred struct gv_drive *d; 1711901Swollman struct gv_plex *p; 17274462Salfred struct gv_sd *s; 17374462Salfred struct gv_volume *v; 17474462Salfred FILE *tmp; 17574462Salfred int drives, errors, fd, flags, i, line, plexes, plex_in_volume; 17674462Salfred int sd_in_plex, status, subdisks, tokens, undeffd, volumes; 1771901Swollman const char *errstr; 1781901Swollman char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname; 17974462Salfred char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS]; 18074462Salfred char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME]; 18174462Salfred 18274462Salfred tmp = NULL; 18374462Salfred flags = 0; 18474462Salfred for (i = 1; i < argc; i++) { 18574462Salfred /* Force flag used to ignore already created drives. */ 18674462Salfred if (!strcmp(argv[i], "-f")) { 18774462Salfred flags |= GV_FLAG_F; 18874462Salfred /* Else it must be a file. */ 18974462Salfred } else { 19074462Salfred if ((tmp = fopen(argv[1], "r")) == NULL) { 19174462Salfred warn("can't open '%s' for reading", argv[1]); 19274462Salfred return; 19374462Salfred } 1941901Swollman } 1951901Swollman } 19674462Salfred 19774462Salfred /* We didn't get a file. */ 1981901Swollman if (tmp == NULL) { 1998870Srgrimes snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX"); 2001901Swollman 2011901Swollman if ((fd = mkstemp(tmpfile)) == -1) { 2021901Swollman warn("temporary file not accessible"); 20374462Salfred return; 20474462Salfred } 2051901Swollman if ((tmp = fdopen(fd, "w")) == NULL) { 206 warn("can't open '%s' for writing", tmpfile); 207 return; 208 } 209 printconfig(tmp, "# "); 210 fclose(tmp); 211 212 ed = getenv("EDITOR"); 213 if (ed == NULL) 214 ed = _PATH_VI; 215 216 snprintf(commandline, sizeof(commandline), "%s %s", ed, 217 tmpfile); 218 status = system(commandline); 219 if (status != 0) { 220 warn("couldn't exec %s; status: %d", ed, status); 221 return; 222 } 223 224 if ((tmp = fopen(tmpfile, "r")) == NULL) { 225 warn("can't open '%s' for reading", tmpfile); 226 return; 227 } 228 } 229 230 req = gctl_get_handle(); 231 gctl_ro_param(req, "class", -1, "VINUM"); 232 gctl_ro_param(req, "verb", -1, "create"); 233 gctl_ro_param(req, "flags", sizeof(int), &flags); 234 235 drives = volumes = plexes = subdisks = 0; 236 plex_in_volume = sd_in_plex = undeffd = 0; 237 plex[0] = '\0'; 238 errors = 0; 239 line = 1; 240 while ((fgets(buf, BUFSIZ, tmp)) != NULL) { 241 242 /* Skip empty lines and comments. */ 243 if (*buf == '\0' || *buf == '#') { 244 line++; 245 continue; 246 } 247 248 /* Kill off the newline. */ 249 buf[strlen(buf) - 1] = '\0'; 250 251 /* 252 * Copy the original input line in case we need it for error 253 * output. 254 */ 255 strlcpy(original, buf, sizeof(original)); 256 257 tokens = gv_tokenize(buf, token, GV_MAXARGS); 258 if (tokens <= 0) { 259 line++; 260 continue; 261 } 262 263 /* Volume definition. */ 264 if (!strcmp(token[0], "volume")) { 265 v = gv_new_volume(tokens, token); 266 if (v == NULL) { 267 warnx("line %d: invalid volume definition", 268 line); 269 warnx("line %d: '%s'", line, original); 270 errors++; 271 line++; 272 continue; 273 } 274 275 /* Reset plex count for this volume. */ 276 plex_in_volume = 0; 277 278 /* 279 * Set default volume name for following plex 280 * definitions. 281 */ 282 strlcpy(volume, v->name, sizeof(volume)); 283 284 snprintf(buf1, sizeof(buf1), "volume%d", volumes); 285 gctl_ro_param(req, buf1, sizeof(*v), v); 286 volumes++; 287 288 /* Plex definition. */ 289 } else if (!strcmp(token[0], "plex")) { 290 p = gv_new_plex(tokens, token); 291 if (p == NULL) { 292 warnx("line %d: invalid plex definition", line); 293 warnx("line %d: '%s'", line, original); 294 errors++; 295 line++; 296 continue; 297 } 298 299 /* Reset subdisk count for this plex. */ 300 sd_in_plex = 0; 301 302 /* Default name. */ 303 if (strlen(p->name) == 0) { 304 snprintf(p->name, sizeof(p->name), "%s.p%d", 305 volume, plex_in_volume++); 306 } 307 308 /* Default volume. */ 309 if (strlen(p->volume) == 0) { 310 snprintf(p->volume, sizeof(p->volume), "%s", 311 volume); 312 } 313 314 /* 315 * Set default plex name for following subdisk 316 * definitions. 317 */ 318 strlcpy(plex, p->name, sizeof(plex)); 319 320 snprintf(buf1, sizeof(buf1), "plex%d", plexes); 321 gctl_ro_param(req, buf1, sizeof(*p), p); 322 plexes++; 323 324 /* Subdisk definition. */ 325 } else if (!strcmp(token[0], "sd")) { 326 s = gv_new_sd(tokens, token); 327 if (s == NULL) { 328 warnx("line %d: invalid subdisk " 329 "definition:", line); 330 warnx("line %d: '%s'", line, original); 331 errors++; 332 line++; 333 continue; 334 } 335 336 /* Default name. */ 337 if (strlen(s->name) == 0) { 338 if (strlen(plex) == 0) { 339 sdname = find_name("gvinumsubdisk.p", 340 GV_TYPE_SD, GV_MAXSDNAME); 341 snprintf(s->name, sizeof(s->name), 342 "%s.s%d", sdname, undeffd++); 343 free(sdname); 344 } else { 345 snprintf(s->name, sizeof(s->name), 346 "%s.s%d",plex, sd_in_plex++); 347 } 348 } 349 350 /* Default plex. */ 351 if (strlen(s->plex) == 0) 352 snprintf(s->plex, sizeof(s->plex), "%s", plex); 353 354 snprintf(buf1, sizeof(buf1), "sd%d", subdisks); 355 gctl_ro_param(req, buf1, sizeof(*s), s); 356 subdisks++; 357 358 /* Subdisk definition. */ 359 } else if (!strcmp(token[0], "drive")) { 360 d = gv_new_drive(tokens, token); 361 if (d == NULL) { 362 warnx("line %d: invalid drive definition:", 363 line); 364 warnx("line %d: '%s'", line, original); 365 errors++; 366 line++; 367 continue; 368 } 369 370 snprintf(buf1, sizeof(buf1), "drive%d", drives); 371 gctl_ro_param(req, buf1, sizeof(*d), d); 372 drives++; 373 374 /* Everything else is bogus. */ 375 } else { 376 warnx("line %d: invalid definition:", line); 377 warnx("line %d: '%s'", line, original); 378 errors++; 379 } 380 line++; 381 } 382 383 fclose(tmp); 384 unlink(tmpfile); 385 386 if (!errors && (volumes || plexes || subdisks || drives)) { 387 gctl_ro_param(req, "volumes", sizeof(int), &volumes); 388 gctl_ro_param(req, "plexes", sizeof(int), &plexes); 389 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 390 gctl_ro_param(req, "drives", sizeof(int), &drives); 391 errstr = gctl_issue(req); 392 if (errstr != NULL) 393 warnx("create failed: %s", errstr); 394 } 395 gctl_free(req); 396} 397 398/* Create a concatenated volume. */ 399void 400gvinum_concat(int argc, char **argv) 401{ 402 403 if (argc < 2) { 404 warnx("usage:\tconcat [-fv] [-n name] drives\n"); 405 return; 406 } 407 create_volume(argc, argv, "concat"); 408} 409 410 411/* Create a drive quick and dirty. */ 412char * 413create_drive(char *device) 414{ 415 struct gv_drive *d; 416 struct gctl_req *req; 417 const char *errstr; 418 char *drivename, *dname; 419 int drives, i, flags, volumes, subdisks, plexes; 420 421 flags = plexes = subdisks = volumes = 0; 422 drives = 1; 423 dname = NULL; 424 425 /* Strip away eventual /dev/ in front. */ 426 if (strncmp(device, "/dev/", 5) == 0) 427 device += 5; 428 429 drivename = find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME); 430 if (drivename == NULL) 431 return (NULL); 432 433 req = gctl_get_handle(); 434 gctl_ro_param(req, "class", -1, "VINUM"); 435 gctl_ro_param(req, "verb", -1, "create"); 436 d = gv_alloc_drive(); 437 if (d == NULL) 438 err(1, "unable to allocate for gv_drive object"); 439 440 strlcpy(d->name, drivename, sizeof(d->name)); 441 strlcpy(d->device, device, sizeof(d->device)); 442 gctl_ro_param(req, "drive0", sizeof(*d), d); 443 gctl_ro_param(req, "flags", sizeof(int), &flags); 444 gctl_ro_param(req, "drives", sizeof(int), &drives); 445 gctl_ro_param(req, "volumes", sizeof(int), &volumes); 446 gctl_ro_param(req, "plexes", sizeof(int), &plexes); 447 gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 448 errstr = gctl_issue(req); 449 if (errstr != NULL) { 450 warnx("error creating drive: %s", errstr); 451 gctl_free(req); 452 return (NULL); 453 } else { 454 gctl_free(req); 455 /* XXX: This is needed because we have to make sure the drives 456 * are created before we return. */ 457 /* Loop until it's in the config. */ 458 for (i = 0; i < 100000; i++) { 459 dname = find_name("gvinumdrive", GV_TYPE_DRIVE, 460 GV_MAXDRIVENAME); 461 /* If we got a different name, quit. */ 462 if (dname == NULL) 463 continue; 464 if (strcmp(dname, drivename)) { 465 free(dname); 466 return (drivename); 467 } 468 free(dname); 469 dname = NULL; 470 usleep(100000); /* Sleep for 0.1s */ 471 } 472 } 473 gctl_free(req); 474 return (drivename); 475} 476 477/* 478 * General routine for creating a volume. Mainly for use by concat, mirror, 479 * raid5 and stripe commands. 480 */ 481void 482create_volume(int argc, char **argv, char *verb) 483{ 484 struct gctl_req *req; 485 const char *errstr; 486 char buf[BUFSIZ], *drivename, *volname; 487 int drives, flags, i; 488 off_t stripesize; 489 490 flags = 0; 491 drives = 0; 492 volname = NULL; 493 stripesize = 262144; 494 495 /* XXX: Should we check for argument length? */ 496 497 req = gctl_get_handle(); 498 gctl_ro_param(req, "class", -1, "VINUM"); 499 500 for (i = 1; i < argc; i++) { 501 if (!strcmp(argv[i], "-f")) { 502 flags |= GV_FLAG_F; 503 } else if (!strcmp(argv[i], "-n")) { 504 volname = argv[++i]; 505 } else if (!strcmp(argv[i], "-v")) { 506 flags |= GV_FLAG_V; 507 } else if (!strcmp(argv[i], "-s")) { 508 flags |= GV_FLAG_S; 509 if (!strcmp(verb, "raid5")) 510 stripesize = gv_sizespec(argv[++i]); 511 } else { 512 /* Assume it's a drive. */ 513 snprintf(buf, sizeof(buf), "drive%d", drives++); 514 515 /* First we create the drive. */ 516 drivename = create_drive(argv[i]); 517 if (drivename == NULL) 518 goto bad; 519 /* Then we add it to the request. */ 520 gctl_ro_param(req, buf, -1, drivename); 521 } 522 } 523 524 gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize); 525 526 /* Find a free volume name. */ 527 if (volname == NULL) 528 volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME); 529 530 /* Then we send a request to actually create the volumes. */ 531 gctl_ro_param(req, "verb", -1, verb); 532 gctl_ro_param(req, "flags", sizeof(int), &flags); 533 gctl_ro_param(req, "drives", sizeof(int), &drives); 534 gctl_ro_param(req, "name", -1, volname); 535 errstr = gctl_issue(req); 536 if (errstr != NULL) 537 warnx("creating %s volume failed: %s", verb, errstr); 538bad: 539 gctl_free(req); 540} 541 542/* Parse a line of the config, return the word after <pattern>. */ 543char * 544find_pattern(char *line, char *pattern) 545{ 546 char *ptr; 547 548 ptr = strsep(&line, " "); 549 while (ptr != NULL) { 550 if (!strcmp(ptr, pattern)) { 551 /* Return the next. */ 552 ptr = strsep(&line, " "); 553 return (ptr); 554 } 555 ptr = strsep(&line, " "); 556 } 557 return (NULL); 558} 559 560/* Find a free name for an object given a a prefix. */ 561char * 562find_name(const char *prefix, int type, int namelen) 563{ 564 struct gctl_req *req; 565 char comment[1], buf[GV_CFG_LEN - 1], *name, *sname, *ptr; 566 const char *errstr; 567 int i, n, begin, len, conflict; 568 char line[1024]; 569 570 comment[0] = '\0'; 571 572 /* Find a name. Fetch out configuration first. */ 573 req = gctl_get_handle(); 574 gctl_ro_param(req, "class", -1, "VINUM"); 575 gctl_ro_param(req, "verb", -1, "getconfig"); 576 gctl_ro_param(req, "comment", -1, comment); 577 gctl_rw_param(req, "config", sizeof(buf), buf); 578 errstr = gctl_issue(req); 579 if (errstr != NULL) { 580 warnx("can't get configuration: %s", errstr); 581 return (NULL); 582 } 583 gctl_free(req); 584 585 begin = 0; 586 len = strlen(buf); 587 i = 0; 588 sname = malloc(namelen + 1); 589 590 /* XXX: Max object setting? */ 591 for (n = 0; n < 10000; n++) { 592 snprintf(sname, namelen, "%s%d", prefix, n); 593 conflict = 0; 594 begin = 0; 595 /* Loop through the configuration line by line. */ 596 for (i = 0; i < len; i++) { 597 if (buf[i] == '\n' || buf[i] == '\0') { 598 ptr = buf + begin; 599 strlcpy(line, ptr, (i - begin) + 1); 600 begin = i + 1; 601 switch (type) { 602 case GV_TYPE_DRIVE: 603 name = find_pattern(line, "drive"); 604 break; 605 case GV_TYPE_VOL: 606 name = find_pattern(line, "volume"); 607 break; 608 case GV_TYPE_PLEX: 609 case GV_TYPE_SD: 610 name = find_pattern(line, "name"); 611 break; 612 default: 613 printf("Invalid type given\n"); 614 continue; 615 } 616 if (name == NULL) 617 continue; 618 if (!strcmp(sname, name)) { 619 conflict = 1; 620 /* XXX: Could quit the loop earlier. */ 621 } 622 } 623 } 624 if (!conflict) 625 return (sname); 626 } 627 free(sname); 628 return (NULL); 629} 630 631/* Detach a plex or subdisk from its parent. */ 632void 633gvinum_detach(int argc, char **argv) 634{ 635 const char *errstr; 636 struct gctl_req *req; 637 int flags, i; 638 639 optreset = 1; 640 optind = 1; 641 while ((i = getopt(argc, argv, "f")) != -1) { 642 switch(i) { 643 case 'f': 644 flags |= GV_FLAG_F; 645 break; 646 default: 647 warn("invalid flag: %c", i); 648 return; 649 } 650 } 651 argc -= optind; 652 argv += optind; 653 if (argc != 1) { 654 warnx("usage: detach [-f] <subdisk> | <plex>"); 655 return; 656 } 657 658 req = gctl_get_handle(); 659 gctl_ro_param(req, "class", -1, "VINUM"); 660 gctl_ro_param(req, "verb", -1, "detach"); 661 gctl_ro_param(req, "object", -1, argv[0]); 662 gctl_ro_param(req, "flags", sizeof(int), &flags); 663 664 errstr = gctl_issue(req); 665 if (errstr != NULL) 666 warnx("detach failed: %s", errstr); 667 gctl_free(req); 668} 669 670void 671gvinum_help(void) 672{ 673 printf("COMMANDS\n" 674 "checkparity [-f] plex\n" 675 " Check the parity blocks of a RAID-5 plex.\n" 676 "create [-f] description-file\n" 677 " Create as per description-file or open editor.\n" 678 "attach plex volume [rename]\n" 679 "attach subdisk plex [offset] [rename]\n" 680 " Attach a plex to a volume, or a subdisk to a plex\n" 681 "concat [-fv] [-n name] drives\n" 682 " Create a concatenated volume from the specified drives.\n" 683 "detach [-f] [plex | subdisk]\n" 684 " Detach a plex or a subdisk from the volume or plex to\n" 685 " which it is attached.\n" 686 "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" 687 " List information about specified objects.\n" 688 "ld [-r] [-v] [-V] [volume]\n" 689 " List information about drives.\n" 690 "ls [-r] [-v] [-V] [subdisk]\n" 691 " List information about subdisks.\n" 692 "lp [-r] [-v] [-V] [plex]\n" 693 " List information about plexes.\n" 694 "lv [-r] [-v] [-V] [volume]\n" 695 " List information about volumes.\n" 696 "mirror [-fsv] [-n name] drives\n" 697 " Create a mirrored volume from the specified drives.\n" 698 "move | mv -f drive object ...\n" 699 " Move the object(s) to the specified drive.\n" 700 "quit Exit the vinum program when running in interactive mode." 701 " Nor-\n" 702 " mally this would be done by entering the EOF character.\n" 703 "raid5 [-fv] [-s stripesize] [-n name] drives\n" 704 " Create a RAID-5 volume from the specified drives.\n" 705 "rename [-r] [drive | subdisk | plex | volume] newname\n" 706 " Change the name of the specified object.\n" 707 "rebuildparity plex [-f]\n" 708 " Rebuild the parity blocks of a RAID-5 plex.\n" 709 "resetconfig\n" 710 " Reset the complete gvinum configuration\n" 711 "rm [-r] [-f] volume | plex | subdisk | drive\n" 712 " Remove an object.\n" 713 "saveconfig\n" 714 " Save vinum configuration to disk after configuration" 715 " failures.\n" 716 "setstate [-f] state [volume | plex | subdisk | drive]\n" 717 " Set state without influencing other objects, for" 718 " diagnostic pur-\n" 719 " poses only.\n" 720 "start [-S size] volume | plex | subdisk\n" 721 " Allow the system to access the objects.\n" 722 "stripe [-fv] [-n name] drives\n" 723 " Create a striped volume from the specified drives.\n" 724 ); 725 726 return; 727} 728 729void 730gvinum_setstate(int argc, char **argv) 731{ 732 struct gctl_req *req; 733 int flags, i; 734 const char *errstr; 735 736 flags = 0; 737 738 optreset = 1; 739 optind = 1; 740 741 while ((i = getopt(argc, argv, "f")) != -1) { 742 switch (i) { 743 case 'f': 744 flags |= GV_FLAG_F; 745 break; 746 case '?': 747 default: 748 warn("invalid flag: %c", i); 749 return; 750 } 751 } 752 753 argc -= optind; 754 argv += optind; 755 756 if (argc != 2) { 757 warnx("usage: setstate [-f] <state> <obj>"); 758 return; 759 } 760 761 /* 762 * XXX: This hack is needed to avoid tripping over (now) invalid 763 * 'classic' vinum states and will go away later. 764 */ 765 if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && 766 strcmp(argv[0], "stale")) { 767 warnx("invalid state '%s'", argv[0]); 768 return; 769 } 770 771 req = gctl_get_handle(); 772 gctl_ro_param(req, "class", -1, "VINUM"); 773 gctl_ro_param(req, "verb", -1, "setstate"); 774 gctl_ro_param(req, "state", -1, argv[0]); 775 gctl_ro_param(req, "object", -1, argv[1]); 776 gctl_ro_param(req, "flags", sizeof(int), &flags); 777 778 errstr = gctl_issue(req); 779 if (errstr != NULL) 780 warnx("%s", errstr); 781 gctl_free(req); 782} 783 784void 785gvinum_list(int argc, char **argv) 786{ 787 struct gctl_req *req; 788 int flags, i, j; 789 const char *errstr; 790 char buf[20], *cmd, config[GV_CFG_LEN + 1]; 791 792 flags = 0; 793 cmd = "list"; 794 795 if (argc) { 796 optreset = 1; 797 optind = 1; 798 cmd = argv[0]; 799 while ((j = getopt(argc, argv, "rsvV")) != -1) { 800 switch (j) { 801 case 'r': 802 flags |= GV_FLAG_R; 803 break; 804 case 's': 805 flags |= GV_FLAG_S; 806 break; 807 case 'v': 808 flags |= GV_FLAG_V; 809 break; 810 case 'V': 811 flags |= GV_FLAG_V; 812 flags |= GV_FLAG_VV; 813 break; 814 case '?': 815 default: 816 return; 817 } 818 } 819 argc -= optind; 820 argv += optind; 821 822 } 823 824 req = gctl_get_handle(); 825 gctl_ro_param(req, "class", -1, "VINUM"); 826 gctl_ro_param(req, "verb", -1, "list"); 827 gctl_ro_param(req, "cmd", -1, cmd); 828 gctl_ro_param(req, "argc", sizeof(int), &argc); 829 gctl_ro_param(req, "flags", sizeof(int), &flags); 830 gctl_rw_param(req, "config", sizeof(config), config); 831 if (argc) { 832 for (i = 0; i < argc; i++) { 833 snprintf(buf, sizeof(buf), "argv%d", i); 834 gctl_ro_param(req, buf, -1, argv[i]); 835 } 836 } 837 errstr = gctl_issue(req); 838 if (errstr != NULL) { 839 warnx("can't get configuration: %s", errstr); 840 gctl_free(req); 841 return; 842 } 843 844 printf("%s", config); 845 gctl_free(req); 846 return; 847} 848 849/* Create a mirrored volume. */ 850void 851gvinum_mirror(int argc, char **argv) 852{ 853 854 if (argc < 2) { 855 warnx("usage\tmirror [-fsv] [-n name] drives\n"); 856 return; 857 } 858 create_volume(argc, argv, "mirror"); 859} 860 861/* Note that move is currently of form '[-r] target object [...]' */ 862void 863gvinum_move(int argc, char **argv) 864{ 865 struct gctl_req *req; 866 const char *errstr; 867 char buf[20]; 868 int flags, i, j; 869 870 flags = 0; 871 if (argc) { 872 optreset = 1; 873 optind = 1; 874 while ((j = getopt(argc, argv, "f")) != -1) { 875 switch (j) { 876 case 'f': 877 flags |= GV_FLAG_F; 878 break; 879 case '?': 880 default: 881 return; 882 } 883 } 884 argc -= optind; 885 argv += optind; 886 } 887 888 switch (argc) { 889 case 0: 890 warnx("no destination or object(s) to move specified"); 891 return; 892 case 1: 893 warnx("no object(s) to move specified"); 894 return; 895 default: 896 break; 897 } 898 899 req = gctl_get_handle(); 900 gctl_ro_param(req, "class", -1, "VINUM"); 901 gctl_ro_param(req, "verb", -1, "move"); 902 gctl_ro_param(req, "argc", sizeof(int), &argc); 903 gctl_ro_param(req, "flags", sizeof(int), &flags); 904 gctl_ro_param(req, "destination", -1, argv[0]); 905 for (i = 1; i < argc; i++) { 906 snprintf(buf, sizeof(buf), "argv%d", i); 907 gctl_ro_param(req, buf, -1, argv[i]); 908 } 909 errstr = gctl_issue(req); 910 if (errstr != NULL) 911 warnx("can't move object(s): %s", errstr); 912 gctl_free(req); 913 return; 914} 915 916void 917gvinum_printconfig(int argc, char **argv) 918{ 919 printconfig(stdout, ""); 920} 921 922void 923gvinum_parityop(int argc, char **argv, int rebuild) 924{ 925 struct gctl_req *req; 926 int flags, i; 927 const char *errstr; 928 char *op, *msg; 929 930 if (rebuild) { 931 op = "rebuildparity"; 932 msg = "Rebuilding"; 933 } else { 934 op = "checkparity"; 935 msg = "Checking"; 936 } 937 938 optreset = 1; 939 optind = 1; 940 flags = 0; 941 while ((i = getopt(argc, argv, "fv")) != -1) { 942 switch (i) { 943 case 'f': 944 flags |= GV_FLAG_F; 945 break; 946 case 'v': 947 flags |= GV_FLAG_V; 948 break; 949 case '?': 950 default: 951 warnx("invalid flag '%c'", i); 952 return; 953 } 954 } 955 argc -= optind; 956 argv += optind; 957 958 if (argc != 1) { 959 warn("usage: %s [-f] [-v] <plex>", op); 960 return; 961 } 962 963 req = gctl_get_handle(); 964 gctl_ro_param(req, "class", -1, "VINUM"); 965 gctl_ro_param(req, "verb", -1, op); 966 gctl_ro_param(req, "rebuild", sizeof(int), &rebuild); 967 gctl_ro_param(req, "flags", sizeof(int), &flags); 968 gctl_ro_param(req, "plex", -1, argv[0]); 969 970 errstr = gctl_issue(req); 971 if (errstr) 972 warnx("%s\n", errstr); 973 gctl_free(req); 974} 975 976/* Create a RAID-5 volume. */ 977void 978gvinum_raid5(int argc, char **argv) 979{ 980 981 if (argc < 2) { 982 warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n"); 983 return; 984 } 985 create_volume(argc, argv, "raid5"); 986} 987 988 989void 990gvinum_rename(int argc, char **argv) 991{ 992 struct gctl_req *req; 993 const char *errstr; 994 int flags, j; 995 996 flags = 0; 997 998 if (argc) { 999 optreset = 1; 1000 optind = 1; 1001 while ((j = getopt(argc, argv, "r")) != -1) { 1002 switch (j) { 1003 case 'r': 1004 flags |= GV_FLAG_R; 1005 break; 1006 case '?': 1007 default: 1008 return; 1009 } 1010 } 1011 argc -= optind; 1012 argv += optind; 1013 } 1014 1015 switch (argc) { 1016 case 0: 1017 warnx("no object to rename specified"); 1018 return; 1019 case 1: 1020 warnx("no new name specified"); 1021 return; 1022 case 2: 1023 break; 1024 default: 1025 warnx("more than one new name specified"); 1026 return; 1027 } 1028 1029 req = gctl_get_handle(); 1030 gctl_ro_param(req, "class", -1, "VINUM"); 1031 gctl_ro_param(req, "verb", -1, "rename"); 1032 gctl_ro_param(req, "flags", sizeof(int), &flags); 1033 gctl_ro_param(req, "object", -1, argv[0]); 1034 gctl_ro_param(req, "newname", -1, argv[1]); 1035 errstr = gctl_issue(req); 1036 if (errstr != NULL) 1037 warnx("can't rename object: %s", errstr); 1038 gctl_free(req); 1039 return; 1040} 1041 1042void 1043gvinum_rm(int argc, char **argv) 1044{ 1045 struct gctl_req *req; 1046 int flags, i, j; 1047 const char *errstr; 1048 char buf[20], *cmd; 1049 1050 cmd = argv[0]; 1051 flags = 0; 1052 optreset = 1; 1053 optind = 1; 1054 while ((j = getopt(argc, argv, "rf")) != -1) { 1055 switch (j) { 1056 case 'f': 1057 flags |= GV_FLAG_F; 1058 break; 1059 case 'r': 1060 flags |= GV_FLAG_R; 1061 break; 1062 case '?': 1063 default: 1064 return; 1065 } 1066 } 1067 argc -= optind; 1068 argv += optind; 1069 1070 req = gctl_get_handle(); 1071 gctl_ro_param(req, "class", -1, "VINUM"); 1072 gctl_ro_param(req, "verb", -1, "remove"); 1073 gctl_ro_param(req, "argc", sizeof(int), &argc); 1074 gctl_ro_param(req, "flags", sizeof(int), &flags); 1075 if (argc) { 1076 for (i = 0; i < argc; i++) { 1077 snprintf(buf, sizeof(buf), "argv%d", i); 1078 gctl_ro_param(req, buf, -1, argv[i]); 1079 } 1080 } 1081 errstr = gctl_issue(req); 1082 if (errstr != NULL) { 1083 warnx("can't remove: %s", errstr); 1084 gctl_free(req); 1085 return; 1086 } 1087 gctl_free(req); 1088} 1089 1090void 1091gvinum_resetconfig(void) 1092{ 1093 struct gctl_req *req; 1094 const char *errstr; 1095 char reply[32]; 1096 1097 if (!isatty(STDIN_FILENO)) { 1098 warn("Please enter this command from a tty device\n"); 1099 return; 1100 } 1101 printf(" WARNING! This command will completely wipe out your gvinum" 1102 "configuration.\n" 1103 " All data will be lost. If you really want to do this," 1104 " enter the text\n\n" 1105 " NO FUTURE\n" 1106 " Enter text -> "); 1107 fgets(reply, sizeof(reply), stdin); 1108 if (strcmp(reply, "NO FUTURE\n")) { 1109 printf("\n No change\n"); 1110 return; 1111 } 1112 req = gctl_get_handle(); 1113 gctl_ro_param(req, "class", -1, "VINUM"); 1114 gctl_ro_param(req, "verb", -1, "resetconfig"); 1115 errstr = gctl_issue(req); 1116 if (errstr != NULL) { 1117 warnx("can't reset config: %s", errstr); 1118 gctl_free(req); 1119 return; 1120 } 1121 gctl_free(req); 1122 printf("gvinum configuration obliterated\n"); 1123} 1124 1125void 1126gvinum_saveconfig(void) 1127{ 1128 struct gctl_req *req; 1129 const char *errstr; 1130 1131 req = gctl_get_handle(); 1132 gctl_ro_param(req, "class", -1, "VINUM"); 1133 gctl_ro_param(req, "verb", -1, "saveconfig"); 1134 errstr = gctl_issue(req); 1135 if (errstr != NULL) 1136 warnx("can't save configuration: %s", errstr); 1137 gctl_free(req); 1138} 1139 1140void 1141gvinum_start(int argc, char **argv) 1142{ 1143 struct gctl_req *req; 1144 int i, initsize, j; 1145 const char *errstr; 1146 char buf[20]; 1147 1148 /* 'start' with no arguments is a no-op. */ 1149 if (argc == 1) 1150 return; 1151 1152 initsize = 0; 1153 1154 optreset = 1; 1155 optind = 1; 1156 while ((j = getopt(argc, argv, "S")) != -1) { 1157 switch (j) { 1158 case 'S': 1159 initsize = atoi(optarg); 1160 break; 1161 case '?': 1162 default: 1163 return; 1164 } 1165 } 1166 argc -= optind; 1167 argv += optind; 1168 1169 if (!initsize) 1170 initsize = 512; 1171 1172 req = gctl_get_handle(); 1173 gctl_ro_param(req, "class", -1, "VINUM"); 1174 gctl_ro_param(req, "verb", -1, "start"); 1175 gctl_ro_param(req, "argc", sizeof(int), &argc); 1176 gctl_ro_param(req, "initsize", sizeof(int), &initsize); 1177 if (argc) { 1178 for (i = 0; i < argc; i++) { 1179 snprintf(buf, sizeof(buf), "argv%d", i); 1180 gctl_ro_param(req, buf, -1, argv[i]); 1181 } 1182 } 1183 errstr = gctl_issue(req); 1184 if (errstr != NULL) { 1185 warnx("can't start: %s", errstr); 1186 gctl_free(req); 1187 return; 1188 } 1189 1190 gctl_free(req); 1191} 1192 1193void 1194gvinum_stop(int argc, char **argv) 1195{ 1196 int err, fileid; 1197 1198 fileid = kldfind(GVINUMMOD); 1199 if (fileid == -1) { 1200 warn("cannot find " GVINUMMOD); 1201 return; 1202 } 1203 1204 /* 1205 * This little hack prevents that we end up in an infinite loop in 1206 * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM 1207 * event thread will be free for the g_wither_geom() call from 1208 * gv_unload(). It's silly, but it works. 1209 */ 1210 printf("unloading " GVINUMMOD " kernel module... "); 1211 fflush(stdout); 1212 if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) { 1213 sleep(1); 1214 err = kldunload(fileid); 1215 } 1216 if (err != 0) { 1217 printf(" failed!\n"); 1218 warn("cannot unload " GVINUMMOD); 1219 return; 1220 } 1221 1222 printf("done\n"); 1223 exit(0); 1224} 1225 1226/* Create a striped volume. */ 1227void 1228gvinum_stripe(int argc, char **argv) 1229{ 1230 1231 if (argc < 2) { 1232 warnx("usage:\tstripe [-fv] [-n name] drives\n"); 1233 return; 1234 } 1235 create_volume(argc, argv, "stripe"); 1236} 1237 1238void 1239parseline(int argc, char **argv) 1240{ 1241 if (argc <= 0) 1242 return; 1243 1244 if (!strcmp(argv[0], "create")) 1245 gvinum_create(argc, argv); 1246 else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) 1247 exit(0); 1248 else if (!strcmp(argv[0], "attach")) 1249 gvinum_attach(argc, argv); 1250 else if (!strcmp(argv[0], "detach")) 1251 gvinum_detach(argc, argv); 1252 else if (!strcmp(argv[0], "concat")) 1253 gvinum_concat(argc, argv); 1254 else if (!strcmp(argv[0], "help")) 1255 gvinum_help(); 1256 else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) 1257 gvinum_list(argc, argv); 1258 else if (!strcmp(argv[0], "ld")) 1259 gvinum_list(argc, argv); 1260 else if (!strcmp(argv[0], "lp")) 1261 gvinum_list(argc, argv); 1262 else if (!strcmp(argv[0], "ls")) 1263 gvinum_list(argc, argv); 1264 else if (!strcmp(argv[0], "lv")) 1265 gvinum_list(argc, argv); 1266 else if (!strcmp(argv[0], "mirror")) 1267 gvinum_mirror(argc, argv); 1268 else if (!strcmp(argv[0], "move")) 1269 gvinum_move(argc, argv); 1270 else if (!strcmp(argv[0], "mv")) 1271 gvinum_move(argc, argv); 1272 else if (!strcmp(argv[0], "printconfig")) 1273 gvinum_printconfig(argc, argv); 1274 else if (!strcmp(argv[0], "raid5")) 1275 gvinum_raid5(argc, argv); 1276 else if (!strcmp(argv[0], "rename")) 1277 gvinum_rename(argc, argv); 1278 else if (!strcmp(argv[0], "resetconfig")) 1279 gvinum_resetconfig(); 1280 else if (!strcmp(argv[0], "rm")) 1281 gvinum_rm(argc, argv); 1282 else if (!strcmp(argv[0], "saveconfig")) 1283 gvinum_saveconfig(); 1284 else if (!strcmp(argv[0], "setstate")) 1285 gvinum_setstate(argc, argv); 1286 else if (!strcmp(argv[0], "start")) 1287 gvinum_start(argc, argv); 1288 else if (!strcmp(argv[0], "stop")) 1289 gvinum_stop(argc, argv); 1290 else if (!strcmp(argv[0], "stripe")) 1291 gvinum_stripe(argc, argv); 1292 else if (!strcmp(argv[0], "checkparity")) 1293 gvinum_parityop(argc, argv, 0); 1294 else if (!strcmp(argv[0], "rebuildparity")) 1295 gvinum_parityop(argc, argv, 1); 1296 else 1297 printf("unknown command '%s'\n", argv[0]); 1298 1299 return; 1300} 1301 1302/* 1303 * The guts of printconfig. This is called from gvinum_printconfig and from 1304 * gvinum_create when called without an argument, in order to give the user 1305 * something to edit. 1306 */ 1307void 1308printconfig(FILE *of, char *comment) 1309{ 1310 struct gctl_req *req; 1311 struct utsname uname_s; 1312 const char *errstr; 1313 time_t now; 1314 char buf[GV_CFG_LEN + 1]; 1315 1316 uname(&uname_s); 1317 time(&now); 1318 1319 req = gctl_get_handle(); 1320 gctl_ro_param(req, "class", -1, "VINUM"); 1321 gctl_ro_param(req, "verb", -1, "getconfig"); 1322 gctl_ro_param(req, "comment", -1, comment); 1323 gctl_rw_param(req, "config", sizeof(buf), buf); 1324 errstr = gctl_issue(req); 1325 if (errstr != NULL) { 1326 warnx("can't get configuration: %s", errstr); 1327 return; 1328 } 1329 gctl_free(req); 1330 1331 fprintf(of, "# Vinum configuration of %s, saved at %s", 1332 uname_s.nodename, 1333 ctime(&now)); 1334 1335 if (*comment != '\0') 1336 fprintf(of, "# Current configuration:\n"); 1337 1338 fprintf(of, buf); 1339} 1340