1130391Sle/* 2190507Slulf * Copyright (c) 2004 Lukas Ertl 3190507Slulf * Copyright (c) 2005 Chris Jones 4190507Slulf * Copyright (c) 2007 Ulf Lilleengen 5130391Sle * All rights reserved. 6152631Sle * 7152631Sle * Portions of this software were developed for the FreeBSD Project 8152631Sle * by Chris Jones thanks to the support of Google's Summer of Code 9152631Sle * program and mentoring by Lukas Ertl. 10152631Sle * 11130391Sle * Redistribution and use in source and binary forms, with or without 12130391Sle * modification, are permitted provided that the following conditions 13130391Sle * are met: 14130391Sle * 1. Redistributions of source code must retain the above copyright 15130391Sle * notice, this list of conditions and the following disclaimer. 16130391Sle * 2. Redistributions in binary form must reproduce the above copyright 17130391Sle * notice, this list of conditions and the following disclaimer in the 18130391Sle * documentation and/or other materials provided with the distribution. 19152631Sle * 20130391Sle * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21130391Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22130391Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23130391Sle * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 24130391Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25130391Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26130391Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27130391Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28130391Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29130391Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30130391Sle * SUCH DAMAGE. 31130391Sle * 32130391Sle * $FreeBSD$ 33130391Sle */ 34130391Sle 35130391Sle#include <sys/param.h> 36130391Sle#include <sys/linker.h> 37130391Sle#include <sys/lock.h> 38130391Sle#include <sys/module.h> 39130391Sle#include <sys/mutex.h> 40130391Sle#include <sys/queue.h> 41130391Sle#include <sys/utsname.h> 42130391Sle 43130391Sle#include <geom/vinum/geom_vinum_var.h> 44130391Sle#include <geom/vinum/geom_vinum_share.h> 45130391Sle 46130391Sle#include <ctype.h> 47130391Sle#include <err.h> 48190507Slulf#include <errno.h> 49130391Sle#include <libgeom.h> 50130391Sle#include <stdint.h> 51130391Sle#include <stdio.h> 52130391Sle#include <stdlib.h> 53220370Sobrien#include <string.h> 54130391Sle#include <paths.h> 55130391Sle#include <readline/readline.h> 56130391Sle#include <readline/history.h> 57130391Sle#include <unistd.h> 58130391Sle 59130391Sle#include "gvinum.h" 60130391Sle 61190507Slulfvoid gvinum_attach(int, char **); 62190507Slulfvoid gvinum_concat(int, char **); 63130391Slevoid gvinum_create(int, char **); 64190507Slulfvoid gvinum_detach(int, char **); 65190884Slulfvoid gvinum_grow(int, char **); 66130391Slevoid gvinum_help(void); 67130391Slevoid gvinum_list(int, char **); 68152616Slevoid gvinum_move(int, char **); 69190507Slulfvoid gvinum_mirror(int, char **); 70138110Slevoid gvinum_parityop(int, char **, int); 71130391Slevoid gvinum_printconfig(int, char **); 72190507Slulfvoid gvinum_raid5(int, char **); 73152616Slevoid gvinum_rename(int, char **); 74157052Slevoid gvinum_resetconfig(void); 75130391Slevoid gvinum_rm(int, char **); 76130391Slevoid gvinum_saveconfig(void); 77138112Slevoid gvinum_setstate(int, char **); 78130391Slevoid gvinum_start(int, char **); 79130391Slevoid gvinum_stop(int, char **); 80190507Slulfvoid gvinum_stripe(int, char **); 81130391Slevoid parseline(int, char **); 82130391Slevoid printconfig(FILE *, char *); 83130391Sle 84190507Slulfchar *create_drive(char *); 85190507Slulfvoid create_volume(int, char **, char *); 86190507Slulfchar *find_name(const char *, int, int); 87190507Slulfchar *find_pattern(char *, char *); 88204665Slulfvoid copy_device(struct gv_drive *, const char *); 89204665Slulf#define find_drive() find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME) 90190507Slulf 91130391Sleint 92130391Slemain(int argc, char **argv) 93130391Sle{ 94130391Sle int line, tokens; 95130391Sle char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS]; 96130391Sle 97130391Sle /* Load the module if necessary. */ 98130391Sle if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0) 99130391Sle err(1, GVINUMMOD ": Kernel module not available"); 100130391Sle 101130391Sle /* Arguments given on the command line. */ 102130391Sle if (argc > 1) { 103130391Sle argc--; 104130391Sle argv++; 105130391Sle parseline(argc, argv); 106130391Sle 107130391Sle /* Interactive mode. */ 108130391Sle } else { 109130391Sle for (;;) { 110130391Sle inputline = readline("gvinum -> "); 111130391Sle if (inputline == NULL) { 112130391Sle if (ferror(stdin)) { 113130391Sle err(1, "can't read input"); 114130391Sle } else { 115130391Sle printf("\n"); 116130391Sle exit(0); 117130391Sle } 118130391Sle } else if (*inputline) { 119130391Sle add_history(inputline); 120130391Sle strcpy(buffer, inputline); 121130391Sle free(inputline); 122130391Sle line++; /* count the lines */ 123130391Sle tokens = gv_tokenize(buffer, token, GV_MAXARGS); 124130391Sle if (tokens) 125130391Sle parseline(tokens, token); 126130391Sle } 127130391Sle } 128130391Sle } 129130391Sle exit(0); 130130391Sle} 131130391Sle 132190507Slulf/* Attach a plex to a volume or a subdisk to a plex. */ 133130391Slevoid 134190507Slulfgvinum_attach(int argc, char **argv) 135190507Slulf{ 136190507Slulf struct gctl_req *req; 137190507Slulf const char *errstr; 138190507Slulf int rename; 139190507Slulf off_t offset; 140190507Slulf 141190507Slulf rename = 0; 142190507Slulf offset = -1; 143190507Slulf if (argc < 3) { 144190507Slulf warnx("usage:\tattach <subdisk> <plex> [rename] " 145190507Slulf "[<plexoffset>]\n" 146190507Slulf "\tattach <plex> <volume> [rename]"); 147190507Slulf return; 148190507Slulf } 149190507Slulf if (argc > 3) { 150190507Slulf if (!strcmp(argv[3], "rename")) { 151190507Slulf rename = 1; 152190507Slulf if (argc == 5) 153190507Slulf offset = strtol(argv[4], NULL, 0); 154190507Slulf } else 155190507Slulf offset = strtol(argv[3], NULL, 0); 156190507Slulf } 157190507Slulf req = gctl_get_handle(); 158190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 159190507Slulf gctl_ro_param(req, "verb", -1, "attach"); 160190507Slulf gctl_ro_param(req, "child", -1, argv[1]); 161190507Slulf gctl_ro_param(req, "parent", -1, argv[2]); 162190507Slulf gctl_ro_param(req, "offset", sizeof(off_t), &offset); 163190507Slulf gctl_ro_param(req, "rename", sizeof(int), &rename); 164190507Slulf errstr = gctl_issue(req); 165190507Slulf if (errstr != NULL) 166190507Slulf warnx("attach failed: %s", errstr); 167190507Slulf gctl_free(req); 168190507Slulf} 169190507Slulf 170190507Slulfvoid 171130391Slegvinum_create(int argc, char **argv) 172130391Sle{ 173130391Sle struct gctl_req *req; 174130391Sle struct gv_drive *d; 175130391Sle struct gv_plex *p; 176130391Sle struct gv_sd *s; 177130391Sle struct gv_volume *v; 178130391Sle FILE *tmp; 179190507Slulf int drives, errors, fd, flags, i, line, plexes, plex_in_volume; 180190507Slulf int sd_in_plex, status, subdisks, tokens, undeffd, volumes; 181130391Sle const char *errstr; 182190507Slulf char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname; 183130391Sle char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS]; 184130391Sle char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME]; 185130391Sle 186190507Slulf tmp = NULL; 187190507Slulf flags = 0; 188190507Slulf for (i = 1; i < argc; i++) { 189190507Slulf /* Force flag used to ignore already created drives. */ 190190507Slulf if (!strcmp(argv[i], "-f")) { 191190507Slulf flags |= GV_FLAG_F; 192190507Slulf /* Else it must be a file. */ 193190507Slulf } else { 194190507Slulf if ((tmp = fopen(argv[1], "r")) == NULL) { 195190507Slulf warn("can't open '%s' for reading", argv[1]); 196190507Slulf return; 197190507Slulf } 198190507Slulf } 199190507Slulf } 200190507Slulf 201190507Slulf /* We didn't get a file. */ 202190507Slulf if (tmp == NULL) { 203133097Sle snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX"); 204133097Sle 205133097Sle if ((fd = mkstemp(tmpfile)) == -1) { 206133097Sle warn("temporary file not accessible"); 207133097Sle return; 208133097Sle } 209133097Sle if ((tmp = fdopen(fd, "w")) == NULL) { 210133097Sle warn("can't open '%s' for writing", tmpfile); 211133097Sle return; 212133097Sle } 213133097Sle printconfig(tmp, "# "); 214133097Sle fclose(tmp); 215133097Sle 216133097Sle ed = getenv("EDITOR"); 217133097Sle if (ed == NULL) 218133097Sle ed = _PATH_VI; 219133097Sle 220133097Sle snprintf(commandline, sizeof(commandline), "%s %s", ed, 221133097Sle tmpfile); 222133097Sle status = system(commandline); 223133097Sle if (status != 0) { 224133097Sle warn("couldn't exec %s; status: %d", ed, status); 225133097Sle return; 226133097Sle } 227133097Sle 228133097Sle if ((tmp = fopen(tmpfile, "r")) == NULL) { 229133097Sle warn("can't open '%s' for reading", tmpfile); 230133097Sle return; 231133097Sle } 232130391Sle } 233130391Sle 234130391Sle req = gctl_get_handle(); 235130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 236130391Sle gctl_ro_param(req, "verb", -1, "create"); 237190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 238130391Sle 239130391Sle drives = volumes = plexes = subdisks = 0; 240190507Slulf plex_in_volume = sd_in_plex = undeffd = 0; 241190507Slulf plex[0] = '\0'; 242130391Sle errors = 0; 243130391Sle line = 1; 244130391Sle while ((fgets(buf, BUFSIZ, tmp)) != NULL) { 245130391Sle 246130391Sle /* Skip empty lines and comments. */ 247130391Sle if (*buf == '\0' || *buf == '#') { 248130391Sle line++; 249130391Sle continue; 250130391Sle } 251130391Sle 252130391Sle /* Kill off the newline. */ 253130391Sle buf[strlen(buf) - 1] = '\0'; 254130391Sle 255130391Sle /* 256130391Sle * Copy the original input line in case we need it for error 257130391Sle * output. 258130391Sle */ 259190507Slulf strlcpy(original, buf, sizeof(original)); 260130391Sle 261130391Sle tokens = gv_tokenize(buf, token, GV_MAXARGS); 262150044Sle if (tokens <= 0) { 263150044Sle line++; 264150044Sle continue; 265150044Sle } 266130391Sle 267150044Sle /* Volume definition. */ 268150044Sle if (!strcmp(token[0], "volume")) { 269150044Sle v = gv_new_volume(tokens, token); 270150044Sle if (v == NULL) { 271150044Sle warnx("line %d: invalid volume definition", 272150044Sle line); 273150044Sle warnx("line %d: '%s'", line, original); 274150044Sle errors++; 275150044Sle line++; 276150044Sle continue; 277150044Sle } 278130391Sle 279150044Sle /* Reset plex count for this volume. */ 280150044Sle plex_in_volume = 0; 281130391Sle 282150044Sle /* 283150044Sle * Set default volume name for following plex 284150044Sle * definitions. 285150044Sle */ 286190507Slulf strlcpy(volume, v->name, sizeof(volume)); 287130391Sle 288150044Sle snprintf(buf1, sizeof(buf1), "volume%d", volumes); 289150044Sle gctl_ro_param(req, buf1, sizeof(*v), v); 290150044Sle volumes++; 291130391Sle 292150044Sle /* Plex definition. */ 293150044Sle } else if (!strcmp(token[0], "plex")) { 294150044Sle p = gv_new_plex(tokens, token); 295150044Sle if (p == NULL) { 296150044Sle warnx("line %d: invalid plex definition", line); 297150044Sle warnx("line %d: '%s'", line, original); 298150044Sle errors++; 299150044Sle line++; 300150044Sle continue; 301150044Sle } 302130391Sle 303150044Sle /* Reset subdisk count for this plex. */ 304150044Sle sd_in_plex = 0; 305130391Sle 306150044Sle /* Default name. */ 307150044Sle if (strlen(p->name) == 0) { 308190507Slulf snprintf(p->name, sizeof(p->name), "%s.p%d", 309150044Sle volume, plex_in_volume++); 310150044Sle } 311130391Sle 312150044Sle /* Default volume. */ 313150044Sle if (strlen(p->volume) == 0) { 314190507Slulf snprintf(p->volume, sizeof(p->volume), "%s", 315150044Sle volume); 316150044Sle } 317130391Sle 318150044Sle /* 319150044Sle * Set default plex name for following subdisk 320150044Sle * definitions. 321150044Sle */ 322190507Slulf strlcpy(plex, p->name, sizeof(plex)); 323130391Sle 324150044Sle snprintf(buf1, sizeof(buf1), "plex%d", plexes); 325150044Sle gctl_ro_param(req, buf1, sizeof(*p), p); 326150044Sle plexes++; 327130391Sle 328150044Sle /* Subdisk definition. */ 329150044Sle } else if (!strcmp(token[0], "sd")) { 330150044Sle s = gv_new_sd(tokens, token); 331150044Sle if (s == NULL) { 332150044Sle warnx("line %d: invalid subdisk " 333150044Sle "definition:", line); 334150044Sle warnx("line %d: '%s'", line, original); 335150044Sle errors++; 336150044Sle line++; 337150044Sle continue; 338150044Sle } 339130391Sle 340150044Sle /* Default name. */ 341150044Sle if (strlen(s->name) == 0) { 342190507Slulf if (strlen(plex) == 0) { 343190507Slulf sdname = find_name("gvinumsubdisk.p", 344190507Slulf GV_TYPE_SD, GV_MAXSDNAME); 345190507Slulf snprintf(s->name, sizeof(s->name), 346190507Slulf "%s.s%d", sdname, undeffd++); 347190507Slulf free(sdname); 348190507Slulf } else { 349190507Slulf snprintf(s->name, sizeof(s->name), 350190507Slulf "%s.s%d",plex, sd_in_plex++); 351190507Slulf } 352150044Sle } 353150044Sle 354150044Sle /* Default plex. */ 355150044Sle if (strlen(s->plex) == 0) 356190507Slulf snprintf(s->plex, sizeof(s->plex), "%s", plex); 357150044Sle 358150044Sle snprintf(buf1, sizeof(buf1), "sd%d", subdisks); 359150044Sle gctl_ro_param(req, buf1, sizeof(*s), s); 360150044Sle subdisks++; 361150044Sle 362150044Sle /* Subdisk definition. */ 363150044Sle } else if (!strcmp(token[0], "drive")) { 364150044Sle d = gv_new_drive(tokens, token); 365150044Sle if (d == NULL) { 366150044Sle warnx("line %d: invalid drive definition:", 367150044Sle line); 368130391Sle warnx("line %d: '%s'", line, original); 369130391Sle errors++; 370150044Sle line++; 371150044Sle continue; 372130391Sle } 373150044Sle 374150044Sle snprintf(buf1, sizeof(buf1), "drive%d", drives); 375150044Sle gctl_ro_param(req, buf1, sizeof(*d), d); 376150044Sle drives++; 377150044Sle 378150044Sle /* Everything else is bogus. */ 379150044Sle } else { 380150044Sle warnx("line %d: invalid definition:", line); 381150044Sle warnx("line %d: '%s'", line, original); 382150044Sle errors++; 383130391Sle } 384130391Sle line++; 385130391Sle } 386130391Sle 387130391Sle fclose(tmp); 388130391Sle unlink(tmpfile); 389130391Sle 390130391Sle if (!errors && (volumes || plexes || subdisks || drives)) { 391130391Sle gctl_ro_param(req, "volumes", sizeof(int), &volumes); 392130391Sle gctl_ro_param(req, "plexes", sizeof(int), &plexes); 393130391Sle gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 394130391Sle gctl_ro_param(req, "drives", sizeof(int), &drives); 395130391Sle errstr = gctl_issue(req); 396130391Sle if (errstr != NULL) 397130391Sle warnx("create failed: %s", errstr); 398130391Sle } 399130391Sle gctl_free(req); 400130391Sle} 401130391Sle 402190507Slulf/* Create a concatenated volume. */ 403130391Slevoid 404190507Slulfgvinum_concat(int argc, char **argv) 405190507Slulf{ 406190507Slulf 407190507Slulf if (argc < 2) { 408190507Slulf warnx("usage:\tconcat [-fv] [-n name] drives\n"); 409190507Slulf return; 410190507Slulf } 411190507Slulf create_volume(argc, argv, "concat"); 412190507Slulf} 413190507Slulf 414190507Slulf 415190507Slulf/* Create a drive quick and dirty. */ 416190507Slulfchar * 417190507Slulfcreate_drive(char *device) 418190507Slulf{ 419190507Slulf struct gv_drive *d; 420190507Slulf struct gctl_req *req; 421190507Slulf const char *errstr; 422190507Slulf char *drivename, *dname; 423190507Slulf int drives, i, flags, volumes, subdisks, plexes; 424190507Slulf 425190507Slulf flags = plexes = subdisks = volumes = 0; 426190507Slulf drives = 1; 427190507Slulf dname = NULL; 428190507Slulf 429204665Slulf drivename = find_drive(); 430190507Slulf if (drivename == NULL) 431190507Slulf return (NULL); 432190507Slulf 433190507Slulf req = gctl_get_handle(); 434190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 435190507Slulf gctl_ro_param(req, "verb", -1, "create"); 436190881Slulf d = gv_alloc_drive(); 437190507Slulf if (d == NULL) 438190507Slulf err(1, "unable to allocate for gv_drive object"); 439190507Slulf 440190507Slulf strlcpy(d->name, drivename, sizeof(d->name)); 441204665Slulf copy_device(d, device); 442190507Slulf gctl_ro_param(req, "drive0", sizeof(*d), d); 443190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 444190507Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 445190507Slulf gctl_ro_param(req, "volumes", sizeof(int), &volumes); 446190507Slulf gctl_ro_param(req, "plexes", sizeof(int), &plexes); 447190507Slulf gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 448190507Slulf errstr = gctl_issue(req); 449190507Slulf if (errstr != NULL) { 450190507Slulf warnx("error creating drive: %s", errstr); 451190507Slulf gctl_free(req); 452190507Slulf return (NULL); 453190507Slulf } else { 454190507Slulf gctl_free(req); 455190507Slulf /* XXX: This is needed because we have to make sure the drives 456190507Slulf * are created before we return. */ 457190507Slulf /* Loop until it's in the config. */ 458190507Slulf for (i = 0; i < 100000; i++) { 459190507Slulf dname = find_name("gvinumdrive", GV_TYPE_DRIVE, 460190507Slulf GV_MAXDRIVENAME); 461190507Slulf /* If we got a different name, quit. */ 462190507Slulf if (dname == NULL) 463190507Slulf continue; 464190507Slulf if (strcmp(dname, drivename)) { 465190507Slulf free(dname); 466190507Slulf return (drivename); 467190507Slulf } 468190507Slulf free(dname); 469190507Slulf dname = NULL; 470190507Slulf usleep(100000); /* Sleep for 0.1s */ 471190507Slulf } 472190507Slulf } 473190507Slulf gctl_free(req); 474190507Slulf return (drivename); 475190507Slulf} 476190507Slulf 477190507Slulf/* 478190507Slulf * General routine for creating a volume. Mainly for use by concat, mirror, 479190507Slulf * raid5 and stripe commands. 480190507Slulf */ 481190507Slulfvoid 482190507Slulfcreate_volume(int argc, char **argv, char *verb) 483190507Slulf{ 484190507Slulf struct gctl_req *req; 485190507Slulf const char *errstr; 486190507Slulf char buf[BUFSIZ], *drivename, *volname; 487190507Slulf int drives, flags, i; 488190507Slulf off_t stripesize; 489190507Slulf 490190507Slulf flags = 0; 491190507Slulf drives = 0; 492190507Slulf volname = NULL; 493190507Slulf stripesize = 262144; 494190507Slulf 495190507Slulf /* XXX: Should we check for argument length? */ 496190507Slulf 497190507Slulf req = gctl_get_handle(); 498190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 499190507Slulf 500190507Slulf for (i = 1; i < argc; i++) { 501190507Slulf if (!strcmp(argv[i], "-f")) { 502190507Slulf flags |= GV_FLAG_F; 503190507Slulf } else if (!strcmp(argv[i], "-n")) { 504190507Slulf volname = argv[++i]; 505190507Slulf } else if (!strcmp(argv[i], "-v")) { 506190507Slulf flags |= GV_FLAG_V; 507190507Slulf } else if (!strcmp(argv[i], "-s")) { 508190507Slulf flags |= GV_FLAG_S; 509190507Slulf if (!strcmp(verb, "raid5")) 510190507Slulf stripesize = gv_sizespec(argv[++i]); 511190507Slulf } else { 512190507Slulf /* Assume it's a drive. */ 513190507Slulf snprintf(buf, sizeof(buf), "drive%d", drives++); 514190507Slulf 515190507Slulf /* First we create the drive. */ 516190507Slulf drivename = create_drive(argv[i]); 517190507Slulf if (drivename == NULL) 518190507Slulf goto bad; 519190507Slulf /* Then we add it to the request. */ 520190507Slulf gctl_ro_param(req, buf, -1, drivename); 521190507Slulf } 522190507Slulf } 523190507Slulf 524190507Slulf gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize); 525190507Slulf 526190507Slulf /* Find a free volume name. */ 527190507Slulf if (volname == NULL) 528190507Slulf volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME); 529190507Slulf 530190507Slulf /* Then we send a request to actually create the volumes. */ 531190507Slulf gctl_ro_param(req, "verb", -1, verb); 532190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 533190507Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 534190507Slulf gctl_ro_param(req, "name", -1, volname); 535190507Slulf errstr = gctl_issue(req); 536190507Slulf if (errstr != NULL) 537190507Slulf warnx("creating %s volume failed: %s", verb, errstr); 538190507Slulfbad: 539190507Slulf gctl_free(req); 540190507Slulf} 541190507Slulf 542190507Slulf/* Parse a line of the config, return the word after <pattern>. */ 543190507Slulfchar * 544190507Slulffind_pattern(char *line, char *pattern) 545190507Slulf{ 546190507Slulf char *ptr; 547190507Slulf 548190507Slulf ptr = strsep(&line, " "); 549190507Slulf while (ptr != NULL) { 550190507Slulf if (!strcmp(ptr, pattern)) { 551190507Slulf /* Return the next. */ 552190507Slulf ptr = strsep(&line, " "); 553190507Slulf return (ptr); 554190507Slulf } 555190507Slulf ptr = strsep(&line, " "); 556190507Slulf } 557190507Slulf return (NULL); 558190507Slulf} 559190507Slulf 560227489Seadler/* Find a free name for an object given a prefix. */ 561190507Slulfchar * 562190507Slulffind_name(const char *prefix, int type, int namelen) 563190507Slulf{ 564190507Slulf struct gctl_req *req; 565190507Slulf char comment[1], buf[GV_CFG_LEN - 1], *name, *sname, *ptr; 566190507Slulf const char *errstr; 567190507Slulf int i, n, begin, len, conflict; 568190507Slulf char line[1024]; 569190507Slulf 570190507Slulf comment[0] = '\0'; 571190507Slulf 572190507Slulf /* Find a name. Fetch out configuration first. */ 573190507Slulf req = gctl_get_handle(); 574190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 575190507Slulf gctl_ro_param(req, "verb", -1, "getconfig"); 576190507Slulf gctl_ro_param(req, "comment", -1, comment); 577190507Slulf gctl_rw_param(req, "config", sizeof(buf), buf); 578190507Slulf errstr = gctl_issue(req); 579190507Slulf if (errstr != NULL) { 580190507Slulf warnx("can't get configuration: %s", errstr); 581190507Slulf return (NULL); 582190507Slulf } 583190507Slulf gctl_free(req); 584190507Slulf 585190507Slulf begin = 0; 586190507Slulf len = strlen(buf); 587190507Slulf i = 0; 588190507Slulf sname = malloc(namelen + 1); 589190507Slulf 590190507Slulf /* XXX: Max object setting? */ 591190507Slulf for (n = 0; n < 10000; n++) { 592190507Slulf snprintf(sname, namelen, "%s%d", prefix, n); 593190507Slulf conflict = 0; 594190507Slulf begin = 0; 595190507Slulf /* Loop through the configuration line by line. */ 596190507Slulf for (i = 0; i < len; i++) { 597190507Slulf if (buf[i] == '\n' || buf[i] == '\0') { 598190507Slulf ptr = buf + begin; 599190507Slulf strlcpy(line, ptr, (i - begin) + 1); 600190507Slulf begin = i + 1; 601190507Slulf switch (type) { 602190507Slulf case GV_TYPE_DRIVE: 603190507Slulf name = find_pattern(line, "drive"); 604190507Slulf break; 605190507Slulf case GV_TYPE_VOL: 606190507Slulf name = find_pattern(line, "volume"); 607190507Slulf break; 608190507Slulf case GV_TYPE_PLEX: 609190507Slulf case GV_TYPE_SD: 610190507Slulf name = find_pattern(line, "name"); 611190507Slulf break; 612190507Slulf default: 613190507Slulf printf("Invalid type given\n"); 614190507Slulf continue; 615190507Slulf } 616190507Slulf if (name == NULL) 617190507Slulf continue; 618190507Slulf if (!strcmp(sname, name)) { 619190507Slulf conflict = 1; 620190507Slulf /* XXX: Could quit the loop earlier. */ 621190507Slulf } 622190507Slulf } 623190507Slulf } 624190507Slulf if (!conflict) 625190507Slulf return (sname); 626190507Slulf } 627190507Slulf free(sname); 628190507Slulf return (NULL); 629190507Slulf} 630190507Slulf 631204665Slulfvoid 632204665Slulfcopy_device(struct gv_drive *d, const char *device) 633190882Slulf{ 634190882Slulf if (strncmp(device, "/dev/", 5) == 0) 635204665Slulf strlcpy(d->device, (device + 5), sizeof(d->device)); 636204665Slulf else 637204665Slulf strlcpy(d->device, device, sizeof(d->device)); 638190882Slulf} 639190882Slulf 640190507Slulf/* Detach a plex or subdisk from its parent. */ 641190507Slulfvoid 642190507Slulfgvinum_detach(int argc, char **argv) 643190507Slulf{ 644190507Slulf const char *errstr; 645190507Slulf struct gctl_req *req; 646190507Slulf int flags, i; 647190507Slulf 648209051Suqs flags = 0; 649190507Slulf optreset = 1; 650190507Slulf optind = 1; 651190507Slulf while ((i = getopt(argc, argv, "f")) != -1) { 652190507Slulf switch(i) { 653190507Slulf case 'f': 654190507Slulf flags |= GV_FLAG_F; 655190507Slulf break; 656190507Slulf default: 657190507Slulf warn("invalid flag: %c", i); 658190507Slulf return; 659190507Slulf } 660190507Slulf } 661190507Slulf argc -= optind; 662190507Slulf argv += optind; 663190507Slulf if (argc != 1) { 664190507Slulf warnx("usage: detach [-f] <subdisk> | <plex>"); 665190507Slulf return; 666190507Slulf } 667190507Slulf 668190507Slulf req = gctl_get_handle(); 669190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 670190507Slulf gctl_ro_param(req, "verb", -1, "detach"); 671190507Slulf gctl_ro_param(req, "object", -1, argv[0]); 672190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 673190507Slulf 674190507Slulf errstr = gctl_issue(req); 675190507Slulf if (errstr != NULL) 676190507Slulf warnx("detach failed: %s", errstr); 677190507Slulf gctl_free(req); 678190507Slulf} 679190507Slulf 680190507Slulfvoid 681130391Slegvinum_help(void) 682130391Sle{ 683130391Sle printf("COMMANDS\n" 684152616Sle "checkparity [-f] plex\n" 685152616Sle " Check the parity blocks of a RAID-5 plex.\n" 686190507Slulf "create [-f] description-file\n" 687152616Sle " Create as per description-file or open editor.\n" 688190507Slulf "attach plex volume [rename]\n" 689190507Slulf "attach subdisk plex [offset] [rename]\n" 690190507Slulf " Attach a plex to a volume, or a subdisk to a plex\n" 691190507Slulf "concat [-fv] [-n name] drives\n" 692190507Slulf " Create a concatenated volume from the specified drives.\n" 693190507Slulf "detach [-f] [plex | subdisk]\n" 694190507Slulf " Detach a plex or a subdisk from the volume or plex to\n" 695190507Slulf " which it is attached.\n" 696190884Slulf "grow plex drive\n" 697190884Slulf " Grow plex by creating a properly sized subdisk on drive\n" 698152616Sle "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" 699130391Sle " List information about specified objects.\n" 700152616Sle "ld [-r] [-v] [-V] [volume]\n" 701130391Sle " List information about drives.\n" 702152616Sle "ls [-r] [-v] [-V] [subdisk]\n" 703130391Sle " List information about subdisks.\n" 704152616Sle "lp [-r] [-v] [-V] [plex]\n" 705130391Sle " List information about plexes.\n" 706152616Sle "lv [-r] [-v] [-V] [volume]\n" 707130391Sle " List information about volumes.\n" 708190507Slulf "mirror [-fsv] [-n name] drives\n" 709190507Slulf " Create a mirrored volume from the specified drives.\n" 710130391Sle "move | mv -f drive object ...\n" 711130391Sle " Move the object(s) to the specified drive.\n" 712130391Sle "quit Exit the vinum program when running in interactive mode." 713130391Sle " Nor-\n" 714130391Sle " mally this would be done by entering the EOF character.\n" 715190507Slulf "raid5 [-fv] [-s stripesize] [-n name] drives\n" 716190507Slulf " Create a RAID-5 volume from the specified drives.\n" 717130391Sle "rename [-r] [drive | subdisk | plex | volume] newname\n" 718130391Sle " Change the name of the specified object.\n" 719152616Sle "rebuildparity plex [-f]\n" 720152616Sle " Rebuild the parity blocks of a RAID-5 plex.\n" 721157052Sle "resetconfig\n" 722157052Sle " Reset the complete gvinum configuration\n" 723190507Slulf "rm [-r] [-f] volume | plex | subdisk | drive\n" 724130391Sle " Remove an object.\n" 725130391Sle "saveconfig\n" 726130391Sle " Save vinum configuration to disk after configuration" 727130391Sle " failures.\n" 728152616Sle "setstate [-f] state [volume | plex | subdisk | drive]\n" 729130391Sle " Set state without influencing other objects, for" 730130391Sle " diagnostic pur-\n" 731130391Sle " poses only.\n" 732152616Sle "start [-S size] volume | plex | subdisk\n" 733130391Sle " Allow the system to access the objects.\n" 734190507Slulf "stripe [-fv] [-n name] drives\n" 735190507Slulf " Create a striped volume from the specified drives.\n" 736130391Sle ); 737130391Sle 738130391Sle return; 739130391Sle} 740130391Sle 741130391Slevoid 742138112Slegvinum_setstate(int argc, char **argv) 743138112Sle{ 744138112Sle struct gctl_req *req; 745138112Sle int flags, i; 746138112Sle const char *errstr; 747138112Sle 748138112Sle flags = 0; 749138112Sle 750138112Sle optreset = 1; 751138112Sle optind = 1; 752138112Sle 753138112Sle while ((i = getopt(argc, argv, "f")) != -1) { 754138112Sle switch (i) { 755138112Sle case 'f': 756138112Sle flags |= GV_FLAG_F; 757138112Sle break; 758138112Sle case '?': 759138112Sle default: 760138112Sle warn("invalid flag: %c", i); 761138112Sle return; 762138112Sle } 763138112Sle } 764138112Sle 765138112Sle argc -= optind; 766138112Sle argv += optind; 767138112Sle 768138112Sle if (argc != 2) { 769138112Sle warnx("usage: setstate [-f] <state> <obj>"); 770138112Sle return; 771138112Sle } 772138112Sle 773138112Sle /* 774138112Sle * XXX: This hack is needed to avoid tripping over (now) invalid 775138112Sle * 'classic' vinum states and will go away later. 776138112Sle */ 777138112Sle if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && 778138112Sle strcmp(argv[0], "stale")) { 779138112Sle warnx("invalid state '%s'", argv[0]); 780138112Sle return; 781138112Sle } 782138112Sle 783138112Sle req = gctl_get_handle(); 784138112Sle gctl_ro_param(req, "class", -1, "VINUM"); 785138112Sle gctl_ro_param(req, "verb", -1, "setstate"); 786138112Sle gctl_ro_param(req, "state", -1, argv[0]); 787138112Sle gctl_ro_param(req, "object", -1, argv[1]); 788138112Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 789138112Sle 790138112Sle errstr = gctl_issue(req); 791138112Sle if (errstr != NULL) 792138112Sle warnx("%s", errstr); 793138112Sle gctl_free(req); 794138112Sle} 795138112Sle 796138112Slevoid 797130391Slegvinum_list(int argc, char **argv) 798130391Sle{ 799130391Sle struct gctl_req *req; 800130391Sle int flags, i, j; 801130391Sle const char *errstr; 802130391Sle char buf[20], *cmd, config[GV_CFG_LEN + 1]; 803130391Sle 804130391Sle flags = 0; 805130391Sle cmd = "list"; 806130391Sle 807130391Sle if (argc) { 808130391Sle optreset = 1; 809130391Sle optind = 1; 810130391Sle cmd = argv[0]; 811130391Sle while ((j = getopt(argc, argv, "rsvV")) != -1) { 812130391Sle switch (j) { 813130391Sle case 'r': 814130391Sle flags |= GV_FLAG_R; 815130391Sle break; 816130391Sle case 's': 817130391Sle flags |= GV_FLAG_S; 818130391Sle break; 819130391Sle case 'v': 820130391Sle flags |= GV_FLAG_V; 821130391Sle break; 822130391Sle case 'V': 823130391Sle flags |= GV_FLAG_V; 824130391Sle flags |= GV_FLAG_VV; 825130391Sle break; 826130391Sle case '?': 827130391Sle default: 828130391Sle return; 829130391Sle } 830130391Sle } 831130391Sle argc -= optind; 832130391Sle argv += optind; 833130391Sle 834130391Sle } 835130391Sle 836130391Sle req = gctl_get_handle(); 837130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 838130391Sle gctl_ro_param(req, "verb", -1, "list"); 839130391Sle gctl_ro_param(req, "cmd", -1, cmd); 840130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 841130391Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 842130391Sle gctl_rw_param(req, "config", sizeof(config), config); 843130391Sle if (argc) { 844130391Sle for (i = 0; i < argc; i++) { 845130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 846130391Sle gctl_ro_param(req, buf, -1, argv[i]); 847130391Sle } 848130391Sle } 849130391Sle errstr = gctl_issue(req); 850130391Sle if (errstr != NULL) { 851130391Sle warnx("can't get configuration: %s", errstr); 852130391Sle gctl_free(req); 853130391Sle return; 854130391Sle } 855130391Sle 856130391Sle printf("%s", config); 857130391Sle gctl_free(req); 858130391Sle return; 859130391Sle} 860130391Sle 861190507Slulf/* Create a mirrored volume. */ 862190507Slulfvoid 863190507Slulfgvinum_mirror(int argc, char **argv) 864190507Slulf{ 865190507Slulf 866190507Slulf if (argc < 2) { 867190507Slulf warnx("usage\tmirror [-fsv] [-n name] drives\n"); 868190507Slulf return; 869190507Slulf } 870190507Slulf create_volume(argc, argv, "mirror"); 871190507Slulf} 872190507Slulf 873152616Sle/* Note that move is currently of form '[-r] target object [...]' */ 874130391Slevoid 875152616Slegvinum_move(int argc, char **argv) 876152616Sle{ 877152616Sle struct gctl_req *req; 878152616Sle const char *errstr; 879152616Sle char buf[20]; 880152616Sle int flags, i, j; 881152616Sle 882152616Sle flags = 0; 883152616Sle if (argc) { 884152616Sle optreset = 1; 885152616Sle optind = 1; 886152616Sle while ((j = getopt(argc, argv, "f")) != -1) { 887152616Sle switch (j) { 888152616Sle case 'f': 889152616Sle flags |= GV_FLAG_F; 890152616Sle break; 891152616Sle case '?': 892152616Sle default: 893152616Sle return; 894152616Sle } 895152616Sle } 896152616Sle argc -= optind; 897152616Sle argv += optind; 898152616Sle } 899152616Sle 900152616Sle switch (argc) { 901152616Sle case 0: 902152616Sle warnx("no destination or object(s) to move specified"); 903152616Sle return; 904152616Sle case 1: 905152616Sle warnx("no object(s) to move specified"); 906152616Sle return; 907152616Sle default: 908152616Sle break; 909152616Sle } 910152616Sle 911152616Sle req = gctl_get_handle(); 912152616Sle gctl_ro_param(req, "class", -1, "VINUM"); 913152616Sle gctl_ro_param(req, "verb", -1, "move"); 914152616Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 915152616Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 916152616Sle gctl_ro_param(req, "destination", -1, argv[0]); 917152616Sle for (i = 1; i < argc; i++) { 918152616Sle snprintf(buf, sizeof(buf), "argv%d", i); 919152616Sle gctl_ro_param(req, buf, -1, argv[i]); 920152616Sle } 921152631Sle errstr = gctl_issue(req); 922152616Sle if (errstr != NULL) 923152616Sle warnx("can't move object(s): %s", errstr); 924152616Sle gctl_free(req); 925152616Sle return; 926152616Sle} 927152616Sle 928152616Slevoid 929130391Slegvinum_printconfig(int argc, char **argv) 930130391Sle{ 931130391Sle printconfig(stdout, ""); 932130391Sle} 933130391Sle 934130391Slevoid 935138110Slegvinum_parityop(int argc, char **argv, int rebuild) 936138110Sle{ 937138110Sle struct gctl_req *req; 938190507Slulf int flags, i; 939138110Sle const char *errstr; 940229915Seadler char *op; 941138110Sle 942138110Sle if (rebuild) { 943138110Sle op = "rebuildparity"; 944138110Sle } else { 945138110Sle op = "checkparity"; 946138110Sle } 947138110Sle 948138110Sle optreset = 1; 949138110Sle optind = 1; 950138110Sle flags = 0; 951138110Sle while ((i = getopt(argc, argv, "fv")) != -1) { 952138110Sle switch (i) { 953138110Sle case 'f': 954138110Sle flags |= GV_FLAG_F; 955138110Sle break; 956138110Sle case 'v': 957138110Sle flags |= GV_FLAG_V; 958138110Sle break; 959138110Sle case '?': 960138110Sle default: 961138110Sle warnx("invalid flag '%c'", i); 962138110Sle return; 963138110Sle } 964138110Sle } 965138110Sle argc -= optind; 966138110Sle argv += optind; 967138110Sle 968138110Sle if (argc != 1) { 969138110Sle warn("usage: %s [-f] [-v] <plex>", op); 970138110Sle return; 971138110Sle } 972138110Sle 973190507Slulf req = gctl_get_handle(); 974190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 975190507Slulf gctl_ro_param(req, "verb", -1, op); 976190507Slulf gctl_ro_param(req, "rebuild", sizeof(int), &rebuild); 977190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 978190507Slulf gctl_ro_param(req, "plex", -1, argv[0]); 979138110Sle 980190507Slulf errstr = gctl_issue(req); 981190507Slulf if (errstr) 982190507Slulf warnx("%s\n", errstr); 983190507Slulf gctl_free(req); 984190507Slulf} 985138110Sle 986190507Slulf/* Create a RAID-5 volume. */ 987190507Slulfvoid 988190507Slulfgvinum_raid5(int argc, char **argv) 989190507Slulf{ 990190507Slulf 991190507Slulf if (argc < 2) { 992190507Slulf warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n"); 993190507Slulf return; 994138110Sle } 995190507Slulf create_volume(argc, argv, "raid5"); 996138110Sle} 997138110Sle 998190507Slulf 999138110Slevoid 1000152616Slegvinum_rename(int argc, char **argv) 1001152616Sle{ 1002152616Sle struct gctl_req *req; 1003152616Sle const char *errstr; 1004152616Sle int flags, j; 1005152616Sle 1006152616Sle flags = 0; 1007152616Sle 1008152616Sle if (argc) { 1009152616Sle optreset = 1; 1010152616Sle optind = 1; 1011152616Sle while ((j = getopt(argc, argv, "r")) != -1) { 1012152616Sle switch (j) { 1013152616Sle case 'r': 1014152616Sle flags |= GV_FLAG_R; 1015152616Sle break; 1016152616Sle case '?': 1017152616Sle default: 1018152616Sle return; 1019152616Sle } 1020152616Sle } 1021152616Sle argc -= optind; 1022152616Sle argv += optind; 1023152616Sle } 1024152616Sle 1025152616Sle switch (argc) { 1026152616Sle case 0: 1027152616Sle warnx("no object to rename specified"); 1028152616Sle return; 1029152616Sle case 1: 1030152616Sle warnx("no new name specified"); 1031152616Sle return; 1032152616Sle case 2: 1033152616Sle break; 1034152616Sle default: 1035152616Sle warnx("more than one new name specified"); 1036152616Sle return; 1037152616Sle } 1038152616Sle 1039152616Sle req = gctl_get_handle(); 1040152616Sle gctl_ro_param(req, "class", -1, "VINUM"); 1041152616Sle gctl_ro_param(req, "verb", -1, "rename"); 1042152616Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 1043152616Sle gctl_ro_param(req, "object", -1, argv[0]); 1044152616Sle gctl_ro_param(req, "newname", -1, argv[1]); 1045152631Sle errstr = gctl_issue(req); 1046152616Sle if (errstr != NULL) 1047152616Sle warnx("can't rename object: %s", errstr); 1048152616Sle gctl_free(req); 1049152616Sle return; 1050152616Sle} 1051152616Sle 1052152616Slevoid 1053130391Slegvinum_rm(int argc, char **argv) 1054130391Sle{ 1055130391Sle struct gctl_req *req; 1056130391Sle int flags, i, j; 1057130391Sle const char *errstr; 1058229915Seadler char buf[20]; 1059130391Sle 1060130391Sle flags = 0; 1061130391Sle optreset = 1; 1062130391Sle optind = 1; 1063190507Slulf while ((j = getopt(argc, argv, "rf")) != -1) { 1064130391Sle switch (j) { 1065190507Slulf case 'f': 1066190507Slulf flags |= GV_FLAG_F; 1067190507Slulf break; 1068130391Sle case 'r': 1069130391Sle flags |= GV_FLAG_R; 1070130391Sle break; 1071130391Sle case '?': 1072130391Sle default: 1073130391Sle return; 1074130391Sle } 1075130391Sle } 1076130391Sle argc -= optind; 1077130391Sle argv += optind; 1078130391Sle 1079130391Sle req = gctl_get_handle(); 1080130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1081130391Sle gctl_ro_param(req, "verb", -1, "remove"); 1082130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 1083130391Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 1084130391Sle if (argc) { 1085130391Sle for (i = 0; i < argc; i++) { 1086130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 1087130391Sle gctl_ro_param(req, buf, -1, argv[i]); 1088130391Sle } 1089130391Sle } 1090130391Sle errstr = gctl_issue(req); 1091130391Sle if (errstr != NULL) { 1092130391Sle warnx("can't remove: %s", errstr); 1093130391Sle gctl_free(req); 1094130391Sle return; 1095130391Sle } 1096130391Sle gctl_free(req); 1097130391Sle} 1098130391Sle 1099130391Slevoid 1100157052Slegvinum_resetconfig(void) 1101157052Sle{ 1102157052Sle struct gctl_req *req; 1103157052Sle const char *errstr; 1104157052Sle char reply[32]; 1105157052Sle 1106157052Sle if (!isatty(STDIN_FILENO)) { 1107157052Sle warn("Please enter this command from a tty device\n"); 1108157052Sle return; 1109157052Sle } 1110157052Sle printf(" WARNING! This command will completely wipe out your gvinum" 1111157052Sle "configuration.\n" 1112157052Sle " All data will be lost. If you really want to do this," 1113157052Sle " enter the text\n\n" 1114157052Sle " NO FUTURE\n" 1115157052Sle " Enter text -> "); 1116157052Sle fgets(reply, sizeof(reply), stdin); 1117157052Sle if (strcmp(reply, "NO FUTURE\n")) { 1118157052Sle printf("\n No change\n"); 1119157052Sle return; 1120157052Sle } 1121157052Sle req = gctl_get_handle(); 1122157052Sle gctl_ro_param(req, "class", -1, "VINUM"); 1123157052Sle gctl_ro_param(req, "verb", -1, "resetconfig"); 1124157052Sle errstr = gctl_issue(req); 1125157052Sle if (errstr != NULL) { 1126157052Sle warnx("can't reset config: %s", errstr); 1127157052Sle gctl_free(req); 1128157052Sle return; 1129157052Sle } 1130157052Sle gctl_free(req); 1131157052Sle printf("gvinum configuration obliterated\n"); 1132157052Sle} 1133157052Sle 1134157052Slevoid 1135130391Slegvinum_saveconfig(void) 1136130391Sle{ 1137130391Sle struct gctl_req *req; 1138130391Sle const char *errstr; 1139130391Sle 1140130391Sle req = gctl_get_handle(); 1141130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1142130391Sle gctl_ro_param(req, "verb", -1, "saveconfig"); 1143130391Sle errstr = gctl_issue(req); 1144130391Sle if (errstr != NULL) 1145130391Sle warnx("can't save configuration: %s", errstr); 1146130391Sle gctl_free(req); 1147130391Sle} 1148130391Sle 1149130391Slevoid 1150130391Slegvinum_start(int argc, char **argv) 1151130391Sle{ 1152130391Sle struct gctl_req *req; 1153130391Sle int i, initsize, j; 1154130391Sle const char *errstr; 1155130391Sle char buf[20]; 1156130391Sle 1157130391Sle /* 'start' with no arguments is a no-op. */ 1158130391Sle if (argc == 1) 1159130391Sle return; 1160130391Sle 1161130391Sle initsize = 0; 1162130391Sle 1163130391Sle optreset = 1; 1164130391Sle optind = 1; 1165130391Sle while ((j = getopt(argc, argv, "S")) != -1) { 1166130391Sle switch (j) { 1167130391Sle case 'S': 1168130391Sle initsize = atoi(optarg); 1169130391Sle break; 1170130391Sle case '?': 1171130391Sle default: 1172130391Sle return; 1173130391Sle } 1174130391Sle } 1175130391Sle argc -= optind; 1176130391Sle argv += optind; 1177130391Sle 1178130391Sle if (!initsize) 1179130391Sle initsize = 512; 1180130391Sle 1181130391Sle req = gctl_get_handle(); 1182130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1183130391Sle gctl_ro_param(req, "verb", -1, "start"); 1184130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 1185130391Sle gctl_ro_param(req, "initsize", sizeof(int), &initsize); 1186130391Sle if (argc) { 1187130391Sle for (i = 0; i < argc; i++) { 1188130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 1189130391Sle gctl_ro_param(req, buf, -1, argv[i]); 1190130391Sle } 1191130391Sle } 1192130391Sle errstr = gctl_issue(req); 1193130391Sle if (errstr != NULL) { 1194130391Sle warnx("can't start: %s", errstr); 1195130391Sle gctl_free(req); 1196130391Sle return; 1197130391Sle } 1198130391Sle 1199130391Sle gctl_free(req); 1200130391Sle} 1201130391Sle 1202130391Slevoid 1203130391Slegvinum_stop(int argc, char **argv) 1204130391Sle{ 1205190507Slulf int err, fileid; 1206130391Sle 1207130391Sle fileid = kldfind(GVINUMMOD); 1208130391Sle if (fileid == -1) { 1209130391Sle warn("cannot find " GVINUMMOD); 1210130391Sle return; 1211130391Sle } 1212190507Slulf 1213190507Slulf /* 1214190507Slulf * This little hack prevents that we end up in an infinite loop in 1215190507Slulf * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM 1216190507Slulf * event thread will be free for the g_wither_geom() call from 1217190507Slulf * gv_unload(). It's silly, but it works. 1218190507Slulf */ 1219190507Slulf printf("unloading " GVINUMMOD " kernel module... "); 1220190507Slulf fflush(stdout); 1221190507Slulf if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) { 1222190507Slulf sleep(1); 1223190507Slulf err = kldunload(fileid); 1224190507Slulf } 1225190507Slulf if (err != 0) { 1226190507Slulf printf(" failed!\n"); 1227130391Sle warn("cannot unload " GVINUMMOD); 1228130391Sle return; 1229130391Sle } 1230130391Sle 1231190507Slulf printf("done\n"); 1232130391Sle exit(0); 1233130391Sle} 1234130391Sle 1235190507Slulf/* Create a striped volume. */ 1236130391Slevoid 1237190507Slulfgvinum_stripe(int argc, char **argv) 1238190507Slulf{ 1239190507Slulf 1240190507Slulf if (argc < 2) { 1241190507Slulf warnx("usage:\tstripe [-fv] [-n name] drives\n"); 1242190507Slulf return; 1243190507Slulf } 1244190507Slulf create_volume(argc, argv, "stripe"); 1245190507Slulf} 1246190507Slulf 1247190884Slulf/* Grow a subdisk by adding disk backed by provider. */ 1248190507Slulfvoid 1249190884Slulfgvinum_grow(int argc, char **argv) 1250190884Slulf{ 1251190884Slulf struct gctl_req *req; 1252190884Slulf char *drive, *sdname; 1253190884Slulf char sdprefix[GV_MAXSDNAME]; 1254190884Slulf struct gv_drive *d; 1255190884Slulf struct gv_sd *s; 1256190884Slulf const char *errstr; 1257190884Slulf int drives, volumes, plexes, subdisks, flags; 1258190884Slulf 1259190884Slulf drives = volumes = plexes = subdisks = 0; 1260190884Slulf if (argc < 3) { 1261190884Slulf warnx("usage:\tgrow plex drive\n"); 1262190884Slulf return; 1263190884Slulf } 1264190884Slulf 1265190884Slulf s = gv_alloc_sd(); 1266190884Slulf if (s == NULL) { 1267190884Slulf warn("unable to create subdisk"); 1268190884Slulf return; 1269190884Slulf } 1270190884Slulf d = gv_alloc_drive(); 1271190884Slulf if (d == NULL) { 1272190884Slulf warn("unable to create drive"); 1273190884Slulf free(s); 1274190884Slulf return; 1275190884Slulf } 1276190884Slulf /* Lookup device and set an appropriate drive name. */ 1277204665Slulf drive = find_drive(); 1278190884Slulf if (drive == NULL) { 1279190884Slulf warn("unable to find an appropriate drive name"); 1280190884Slulf free(s); 1281190884Slulf free(d); 1282190884Slulf return; 1283190884Slulf } 1284190884Slulf strlcpy(d->name, drive, sizeof(d->name)); 1285204665Slulf copy_device(d, argv[2]); 1286204665Slulf 1287190884Slulf drives = 1; 1288190884Slulf 1289190884Slulf /* We try to use the plex name as basis for the subdisk name. */ 1290190884Slulf snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]); 1291190884Slulf sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME); 1292190884Slulf if (sdname == NULL) { 1293190884Slulf warn("unable to find an appropriate subdisk name"); 1294190884Slulf free(s); 1295190884Slulf free(d); 1296190884Slulf free(drive); 1297190884Slulf return; 1298190884Slulf } 1299190884Slulf strlcpy(s->name, sdname, sizeof(s->name)); 1300190884Slulf free(sdname); 1301190884Slulf strlcpy(s->plex, argv[1], sizeof(s->plex)); 1302190884Slulf strlcpy(s->drive, d->name, sizeof(s->drive)); 1303190884Slulf subdisks = 1; 1304190884Slulf 1305190884Slulf req = gctl_get_handle(); 1306190884Slulf gctl_ro_param(req, "class", -1, "VINUM"); 1307190884Slulf gctl_ro_param(req, "verb", -1, "create"); 1308190884Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 1309190884Slulf gctl_ro_param(req, "volumes", sizeof(int), &volumes); 1310190884Slulf gctl_ro_param(req, "plexes", sizeof(int), &plexes); 1311190884Slulf gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 1312190884Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 1313190884Slulf gctl_ro_param(req, "drive0", sizeof(*d), d); 1314190884Slulf gctl_ro_param(req, "sd0", sizeof(*s), s); 1315190884Slulf errstr = gctl_issue(req); 1316190884Slulf free(drive); 1317190884Slulf if (errstr != NULL) { 1318190884Slulf warnx("unable to grow plex: %s", errstr); 1319190884Slulf free(s); 1320190884Slulf free(d); 1321190884Slulf return; 1322190884Slulf } 1323190884Slulf gctl_free(req); 1324190884Slulf} 1325190884Slulf 1326190884Slulfvoid 1327130391Sleparseline(int argc, char **argv) 1328130391Sle{ 1329130391Sle if (argc <= 0) 1330130391Sle return; 1331130391Sle 1332150044Sle if (!strcmp(argv[0], "create")) 1333130391Sle gvinum_create(argc, argv); 1334130391Sle else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) 1335130391Sle exit(0); 1336190507Slulf else if (!strcmp(argv[0], "attach")) 1337190507Slulf gvinum_attach(argc, argv); 1338190507Slulf else if (!strcmp(argv[0], "detach")) 1339190507Slulf gvinum_detach(argc, argv); 1340190507Slulf else if (!strcmp(argv[0], "concat")) 1341190507Slulf gvinum_concat(argc, argv); 1342190884Slulf else if (!strcmp(argv[0], "grow")) 1343190884Slulf gvinum_grow(argc, argv); 1344130391Sle else if (!strcmp(argv[0], "help")) 1345130391Sle gvinum_help(); 1346130391Sle else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) 1347130391Sle gvinum_list(argc, argv); 1348130391Sle else if (!strcmp(argv[0], "ld")) 1349130391Sle gvinum_list(argc, argv); 1350130391Sle else if (!strcmp(argv[0], "lp")) 1351130391Sle gvinum_list(argc, argv); 1352130391Sle else if (!strcmp(argv[0], "ls")) 1353130391Sle gvinum_list(argc, argv); 1354130391Sle else if (!strcmp(argv[0], "lv")) 1355130391Sle gvinum_list(argc, argv); 1356190507Slulf else if (!strcmp(argv[0], "mirror")) 1357190507Slulf gvinum_mirror(argc, argv); 1358152616Sle else if (!strcmp(argv[0], "move")) 1359152616Sle gvinum_move(argc, argv); 1360152616Sle else if (!strcmp(argv[0], "mv")) 1361152616Sle gvinum_move(argc, argv); 1362130391Sle else if (!strcmp(argv[0], "printconfig")) 1363130391Sle gvinum_printconfig(argc, argv); 1364190507Slulf else if (!strcmp(argv[0], "raid5")) 1365190507Slulf gvinum_raid5(argc, argv); 1366152616Sle else if (!strcmp(argv[0], "rename")) 1367152616Sle gvinum_rename(argc, argv); 1368157052Sle else if (!strcmp(argv[0], "resetconfig")) 1369157052Sle gvinum_resetconfig(); 1370130391Sle else if (!strcmp(argv[0], "rm")) 1371130391Sle gvinum_rm(argc, argv); 1372130391Sle else if (!strcmp(argv[0], "saveconfig")) 1373130391Sle gvinum_saveconfig(); 1374138112Sle else if (!strcmp(argv[0], "setstate")) 1375138112Sle gvinum_setstate(argc, argv); 1376130391Sle else if (!strcmp(argv[0], "start")) 1377130391Sle gvinum_start(argc, argv); 1378130391Sle else if (!strcmp(argv[0], "stop")) 1379130391Sle gvinum_stop(argc, argv); 1380190507Slulf else if (!strcmp(argv[0], "stripe")) 1381190507Slulf gvinum_stripe(argc, argv); 1382138110Sle else if (!strcmp(argv[0], "checkparity")) 1383138110Sle gvinum_parityop(argc, argv, 0); 1384138110Sle else if (!strcmp(argv[0], "rebuildparity")) 1385138110Sle gvinum_parityop(argc, argv, 1); 1386130391Sle else 1387130391Sle printf("unknown command '%s'\n", argv[0]); 1388130391Sle 1389130391Sle return; 1390130391Sle} 1391130391Sle 1392130391Sle/* 1393130391Sle * The guts of printconfig. This is called from gvinum_printconfig and from 1394130391Sle * gvinum_create when called without an argument, in order to give the user 1395130391Sle * something to edit. 1396130391Sle */ 1397130391Slevoid 1398130391Sleprintconfig(FILE *of, char *comment) 1399130391Sle{ 1400130391Sle struct gctl_req *req; 1401130391Sle struct utsname uname_s; 1402130391Sle const char *errstr; 1403130391Sle time_t now; 1404130391Sle char buf[GV_CFG_LEN + 1]; 1405130391Sle 1406130391Sle uname(&uname_s); 1407130391Sle time(&now); 1408130391Sle 1409130391Sle req = gctl_get_handle(); 1410130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1411130391Sle gctl_ro_param(req, "verb", -1, "getconfig"); 1412130391Sle gctl_ro_param(req, "comment", -1, comment); 1413130391Sle gctl_rw_param(req, "config", sizeof(buf), buf); 1414130391Sle errstr = gctl_issue(req); 1415130391Sle if (errstr != NULL) { 1416130391Sle warnx("can't get configuration: %s", errstr); 1417130391Sle return; 1418130391Sle } 1419130391Sle gctl_free(req); 1420130391Sle 1421130391Sle fprintf(of, "# Vinum configuration of %s, saved at %s", 1422130391Sle uname_s.nodename, 1423130391Sle ctime(&now)); 1424130391Sle 1425130391Sle if (*comment != '\0') 1426130391Sle fprintf(of, "# Current configuration:\n"); 1427130391Sle 1428215704Sbrucec fprintf(of, "%s", buf); 1429130391Sle} 1430