gvinum.c revision 265536
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: stable/10/sbin/gvinum/gvinum.c 265536 2014-05-07 09:55:47Z marius $ 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. */ 98265536Smarius if (modfind(GVINUMMOD) < 0) { 99265536Smarius if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0) 100265536Smarius err(1, GVINUMKLD ": Kernel module not available"); 101265536Smarius } 102130391Sle 103130391Sle /* Arguments given on the command line. */ 104130391Sle if (argc > 1) { 105130391Sle argc--; 106130391Sle argv++; 107130391Sle parseline(argc, argv); 108130391Sle 109130391Sle /* Interactive mode. */ 110130391Sle } else { 111130391Sle for (;;) { 112130391Sle inputline = readline("gvinum -> "); 113130391Sle if (inputline == NULL) { 114130391Sle if (ferror(stdin)) { 115130391Sle err(1, "can't read input"); 116130391Sle } else { 117130391Sle printf("\n"); 118130391Sle exit(0); 119130391Sle } 120130391Sle } else if (*inputline) { 121130391Sle add_history(inputline); 122130391Sle strcpy(buffer, inputline); 123130391Sle free(inputline); 124130391Sle line++; /* count the lines */ 125130391Sle tokens = gv_tokenize(buffer, token, GV_MAXARGS); 126130391Sle if (tokens) 127130391Sle parseline(tokens, token); 128130391Sle } 129130391Sle } 130130391Sle } 131130391Sle exit(0); 132130391Sle} 133130391Sle 134190507Slulf/* Attach a plex to a volume or a subdisk to a plex. */ 135130391Slevoid 136190507Slulfgvinum_attach(int argc, char **argv) 137190507Slulf{ 138190507Slulf struct gctl_req *req; 139190507Slulf const char *errstr; 140190507Slulf int rename; 141190507Slulf off_t offset; 142190507Slulf 143190507Slulf rename = 0; 144190507Slulf offset = -1; 145190507Slulf if (argc < 3) { 146190507Slulf warnx("usage:\tattach <subdisk> <plex> [rename] " 147190507Slulf "[<plexoffset>]\n" 148190507Slulf "\tattach <plex> <volume> [rename]"); 149190507Slulf return; 150190507Slulf } 151190507Slulf if (argc > 3) { 152190507Slulf if (!strcmp(argv[3], "rename")) { 153190507Slulf rename = 1; 154190507Slulf if (argc == 5) 155190507Slulf offset = strtol(argv[4], NULL, 0); 156190507Slulf } else 157190507Slulf offset = strtol(argv[3], NULL, 0); 158190507Slulf } 159190507Slulf req = gctl_get_handle(); 160190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 161190507Slulf gctl_ro_param(req, "verb", -1, "attach"); 162190507Slulf gctl_ro_param(req, "child", -1, argv[1]); 163190507Slulf gctl_ro_param(req, "parent", -1, argv[2]); 164190507Slulf gctl_ro_param(req, "offset", sizeof(off_t), &offset); 165190507Slulf gctl_ro_param(req, "rename", sizeof(int), &rename); 166190507Slulf errstr = gctl_issue(req); 167190507Slulf if (errstr != NULL) 168190507Slulf warnx("attach failed: %s", errstr); 169190507Slulf gctl_free(req); 170190507Slulf} 171190507Slulf 172190507Slulfvoid 173130391Slegvinum_create(int argc, char **argv) 174130391Sle{ 175130391Sle struct gctl_req *req; 176130391Sle struct gv_drive *d; 177130391Sle struct gv_plex *p; 178130391Sle struct gv_sd *s; 179130391Sle struct gv_volume *v; 180130391Sle FILE *tmp; 181190507Slulf int drives, errors, fd, flags, i, line, plexes, plex_in_volume; 182190507Slulf int sd_in_plex, status, subdisks, tokens, undeffd, volumes; 183130391Sle const char *errstr; 184190507Slulf char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname; 185130391Sle char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS]; 186130391Sle char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME]; 187130391Sle 188190507Slulf tmp = NULL; 189190507Slulf flags = 0; 190190507Slulf for (i = 1; i < argc; i++) { 191190507Slulf /* Force flag used to ignore already created drives. */ 192190507Slulf if (!strcmp(argv[i], "-f")) { 193190507Slulf flags |= GV_FLAG_F; 194190507Slulf /* Else it must be a file. */ 195190507Slulf } else { 196190507Slulf if ((tmp = fopen(argv[1], "r")) == NULL) { 197190507Slulf warn("can't open '%s' for reading", argv[1]); 198190507Slulf return; 199190507Slulf } 200190507Slulf } 201190507Slulf } 202190507Slulf 203190507Slulf /* We didn't get a file. */ 204190507Slulf if (tmp == NULL) { 205133097Sle snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX"); 206133097Sle 207133097Sle if ((fd = mkstemp(tmpfile)) == -1) { 208133097Sle warn("temporary file not accessible"); 209133097Sle return; 210133097Sle } 211133097Sle if ((tmp = fdopen(fd, "w")) == NULL) { 212133097Sle warn("can't open '%s' for writing", tmpfile); 213133097Sle return; 214133097Sle } 215133097Sle printconfig(tmp, "# "); 216133097Sle fclose(tmp); 217133097Sle 218133097Sle ed = getenv("EDITOR"); 219133097Sle if (ed == NULL) 220133097Sle ed = _PATH_VI; 221133097Sle 222133097Sle snprintf(commandline, sizeof(commandline), "%s %s", ed, 223133097Sle tmpfile); 224133097Sle status = system(commandline); 225133097Sle if (status != 0) { 226133097Sle warn("couldn't exec %s; status: %d", ed, status); 227133097Sle return; 228133097Sle } 229133097Sle 230133097Sle if ((tmp = fopen(tmpfile, "r")) == NULL) { 231133097Sle warn("can't open '%s' for reading", tmpfile); 232133097Sle return; 233133097Sle } 234130391Sle } 235130391Sle 236130391Sle req = gctl_get_handle(); 237130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 238130391Sle gctl_ro_param(req, "verb", -1, "create"); 239190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 240130391Sle 241130391Sle drives = volumes = plexes = subdisks = 0; 242190507Slulf plex_in_volume = sd_in_plex = undeffd = 0; 243190507Slulf plex[0] = '\0'; 244130391Sle errors = 0; 245130391Sle line = 1; 246130391Sle while ((fgets(buf, BUFSIZ, tmp)) != NULL) { 247130391Sle 248130391Sle /* Skip empty lines and comments. */ 249130391Sle if (*buf == '\0' || *buf == '#') { 250130391Sle line++; 251130391Sle continue; 252130391Sle } 253130391Sle 254130391Sle /* Kill off the newline. */ 255130391Sle buf[strlen(buf) - 1] = '\0'; 256130391Sle 257130391Sle /* 258130391Sle * Copy the original input line in case we need it for error 259130391Sle * output. 260130391Sle */ 261190507Slulf strlcpy(original, buf, sizeof(original)); 262130391Sle 263130391Sle tokens = gv_tokenize(buf, token, GV_MAXARGS); 264150044Sle if (tokens <= 0) { 265150044Sle line++; 266150044Sle continue; 267150044Sle } 268130391Sle 269150044Sle /* Volume definition. */ 270150044Sle if (!strcmp(token[0], "volume")) { 271150044Sle v = gv_new_volume(tokens, token); 272150044Sle if (v == NULL) { 273150044Sle warnx("line %d: invalid volume definition", 274150044Sle line); 275150044Sle warnx("line %d: '%s'", line, original); 276150044Sle errors++; 277150044Sle line++; 278150044Sle continue; 279150044Sle } 280130391Sle 281150044Sle /* Reset plex count for this volume. */ 282150044Sle plex_in_volume = 0; 283130391Sle 284150044Sle /* 285150044Sle * Set default volume name for following plex 286150044Sle * definitions. 287150044Sle */ 288190507Slulf strlcpy(volume, v->name, sizeof(volume)); 289130391Sle 290150044Sle snprintf(buf1, sizeof(buf1), "volume%d", volumes); 291150044Sle gctl_ro_param(req, buf1, sizeof(*v), v); 292150044Sle volumes++; 293130391Sle 294150044Sle /* Plex definition. */ 295150044Sle } else if (!strcmp(token[0], "plex")) { 296150044Sle p = gv_new_plex(tokens, token); 297150044Sle if (p == NULL) { 298150044Sle warnx("line %d: invalid plex definition", line); 299150044Sle warnx("line %d: '%s'", line, original); 300150044Sle errors++; 301150044Sle line++; 302150044Sle continue; 303150044Sle } 304130391Sle 305150044Sle /* Reset subdisk count for this plex. */ 306150044Sle sd_in_plex = 0; 307130391Sle 308150044Sle /* Default name. */ 309150044Sle if (strlen(p->name) == 0) { 310190507Slulf snprintf(p->name, sizeof(p->name), "%s.p%d", 311150044Sle volume, plex_in_volume++); 312150044Sle } 313130391Sle 314150044Sle /* Default volume. */ 315150044Sle if (strlen(p->volume) == 0) { 316190507Slulf snprintf(p->volume, sizeof(p->volume), "%s", 317150044Sle volume); 318150044Sle } 319130391Sle 320150044Sle /* 321150044Sle * Set default plex name for following subdisk 322150044Sle * definitions. 323150044Sle */ 324190507Slulf strlcpy(plex, p->name, sizeof(plex)); 325130391Sle 326150044Sle snprintf(buf1, sizeof(buf1), "plex%d", plexes); 327150044Sle gctl_ro_param(req, buf1, sizeof(*p), p); 328150044Sle plexes++; 329130391Sle 330150044Sle /* Subdisk definition. */ 331150044Sle } else if (!strcmp(token[0], "sd")) { 332150044Sle s = gv_new_sd(tokens, token); 333150044Sle if (s == NULL) { 334150044Sle warnx("line %d: invalid subdisk " 335150044Sle "definition:", line); 336150044Sle warnx("line %d: '%s'", line, original); 337150044Sle errors++; 338150044Sle line++; 339150044Sle continue; 340150044Sle } 341130391Sle 342150044Sle /* Default name. */ 343150044Sle if (strlen(s->name) == 0) { 344190507Slulf if (strlen(plex) == 0) { 345190507Slulf sdname = find_name("gvinumsubdisk.p", 346190507Slulf GV_TYPE_SD, GV_MAXSDNAME); 347190507Slulf snprintf(s->name, sizeof(s->name), 348190507Slulf "%s.s%d", sdname, undeffd++); 349190507Slulf free(sdname); 350190507Slulf } else { 351190507Slulf snprintf(s->name, sizeof(s->name), 352190507Slulf "%s.s%d",plex, sd_in_plex++); 353190507Slulf } 354150044Sle } 355150044Sle 356150044Sle /* Default plex. */ 357150044Sle if (strlen(s->plex) == 0) 358190507Slulf snprintf(s->plex, sizeof(s->plex), "%s", plex); 359150044Sle 360150044Sle snprintf(buf1, sizeof(buf1), "sd%d", subdisks); 361150044Sle gctl_ro_param(req, buf1, sizeof(*s), s); 362150044Sle subdisks++; 363150044Sle 364150044Sle /* Subdisk definition. */ 365150044Sle } else if (!strcmp(token[0], "drive")) { 366150044Sle d = gv_new_drive(tokens, token); 367150044Sle if (d == NULL) { 368150044Sle warnx("line %d: invalid drive definition:", 369150044Sle line); 370130391Sle warnx("line %d: '%s'", line, original); 371130391Sle errors++; 372150044Sle line++; 373150044Sle continue; 374130391Sle } 375150044Sle 376150044Sle snprintf(buf1, sizeof(buf1), "drive%d", drives); 377150044Sle gctl_ro_param(req, buf1, sizeof(*d), d); 378150044Sle drives++; 379150044Sle 380150044Sle /* Everything else is bogus. */ 381150044Sle } else { 382150044Sle warnx("line %d: invalid definition:", line); 383150044Sle warnx("line %d: '%s'", line, original); 384150044Sle errors++; 385130391Sle } 386130391Sle line++; 387130391Sle } 388130391Sle 389130391Sle fclose(tmp); 390130391Sle unlink(tmpfile); 391130391Sle 392130391Sle if (!errors && (volumes || plexes || subdisks || drives)) { 393130391Sle gctl_ro_param(req, "volumes", sizeof(int), &volumes); 394130391Sle gctl_ro_param(req, "plexes", sizeof(int), &plexes); 395130391Sle gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 396130391Sle gctl_ro_param(req, "drives", sizeof(int), &drives); 397130391Sle errstr = gctl_issue(req); 398130391Sle if (errstr != NULL) 399130391Sle warnx("create failed: %s", errstr); 400130391Sle } 401130391Sle gctl_free(req); 402130391Sle} 403130391Sle 404190507Slulf/* Create a concatenated volume. */ 405130391Slevoid 406190507Slulfgvinum_concat(int argc, char **argv) 407190507Slulf{ 408190507Slulf 409190507Slulf if (argc < 2) { 410190507Slulf warnx("usage:\tconcat [-fv] [-n name] drives\n"); 411190507Slulf return; 412190507Slulf } 413190507Slulf create_volume(argc, argv, "concat"); 414190507Slulf} 415190507Slulf 416190507Slulf 417190507Slulf/* Create a drive quick and dirty. */ 418190507Slulfchar * 419190507Slulfcreate_drive(char *device) 420190507Slulf{ 421190507Slulf struct gv_drive *d; 422190507Slulf struct gctl_req *req; 423190507Slulf const char *errstr; 424190507Slulf char *drivename, *dname; 425190507Slulf int drives, i, flags, volumes, subdisks, plexes; 426190507Slulf 427190507Slulf flags = plexes = subdisks = volumes = 0; 428190507Slulf drives = 1; 429190507Slulf dname = NULL; 430190507Slulf 431204665Slulf drivename = find_drive(); 432190507Slulf if (drivename == NULL) 433190507Slulf return (NULL); 434190507Slulf 435190507Slulf req = gctl_get_handle(); 436190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 437190507Slulf gctl_ro_param(req, "verb", -1, "create"); 438190881Slulf d = gv_alloc_drive(); 439190507Slulf if (d == NULL) 440190507Slulf err(1, "unable to allocate for gv_drive object"); 441190507Slulf 442190507Slulf strlcpy(d->name, drivename, sizeof(d->name)); 443204665Slulf copy_device(d, device); 444190507Slulf gctl_ro_param(req, "drive0", sizeof(*d), d); 445190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 446190507Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 447190507Slulf gctl_ro_param(req, "volumes", sizeof(int), &volumes); 448190507Slulf gctl_ro_param(req, "plexes", sizeof(int), &plexes); 449190507Slulf gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 450190507Slulf errstr = gctl_issue(req); 451190507Slulf if (errstr != NULL) { 452190507Slulf warnx("error creating drive: %s", errstr); 453190507Slulf gctl_free(req); 454190507Slulf return (NULL); 455190507Slulf } else { 456190507Slulf gctl_free(req); 457190507Slulf /* XXX: This is needed because we have to make sure the drives 458190507Slulf * are created before we return. */ 459190507Slulf /* Loop until it's in the config. */ 460190507Slulf for (i = 0; i < 100000; i++) { 461190507Slulf dname = find_name("gvinumdrive", GV_TYPE_DRIVE, 462190507Slulf GV_MAXDRIVENAME); 463190507Slulf /* If we got a different name, quit. */ 464190507Slulf if (dname == NULL) 465190507Slulf continue; 466190507Slulf if (strcmp(dname, drivename)) { 467190507Slulf free(dname); 468190507Slulf return (drivename); 469190507Slulf } 470190507Slulf free(dname); 471190507Slulf dname = NULL; 472190507Slulf usleep(100000); /* Sleep for 0.1s */ 473190507Slulf } 474190507Slulf } 475190507Slulf gctl_free(req); 476190507Slulf return (drivename); 477190507Slulf} 478190507Slulf 479190507Slulf/* 480190507Slulf * General routine for creating a volume. Mainly for use by concat, mirror, 481190507Slulf * raid5 and stripe commands. 482190507Slulf */ 483190507Slulfvoid 484190507Slulfcreate_volume(int argc, char **argv, char *verb) 485190507Slulf{ 486190507Slulf struct gctl_req *req; 487190507Slulf const char *errstr; 488190507Slulf char buf[BUFSIZ], *drivename, *volname; 489190507Slulf int drives, flags, i; 490190507Slulf off_t stripesize; 491190507Slulf 492190507Slulf flags = 0; 493190507Slulf drives = 0; 494190507Slulf volname = NULL; 495190507Slulf stripesize = 262144; 496190507Slulf 497190507Slulf /* XXX: Should we check for argument length? */ 498190507Slulf 499190507Slulf req = gctl_get_handle(); 500190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 501190507Slulf 502190507Slulf for (i = 1; i < argc; i++) { 503190507Slulf if (!strcmp(argv[i], "-f")) { 504190507Slulf flags |= GV_FLAG_F; 505190507Slulf } else if (!strcmp(argv[i], "-n")) { 506190507Slulf volname = argv[++i]; 507190507Slulf } else if (!strcmp(argv[i], "-v")) { 508190507Slulf flags |= GV_FLAG_V; 509190507Slulf } else if (!strcmp(argv[i], "-s")) { 510190507Slulf flags |= GV_FLAG_S; 511190507Slulf if (!strcmp(verb, "raid5")) 512190507Slulf stripesize = gv_sizespec(argv[++i]); 513190507Slulf } else { 514190507Slulf /* Assume it's a drive. */ 515190507Slulf snprintf(buf, sizeof(buf), "drive%d", drives++); 516190507Slulf 517190507Slulf /* First we create the drive. */ 518190507Slulf drivename = create_drive(argv[i]); 519190507Slulf if (drivename == NULL) 520190507Slulf goto bad; 521190507Slulf /* Then we add it to the request. */ 522190507Slulf gctl_ro_param(req, buf, -1, drivename); 523190507Slulf } 524190507Slulf } 525190507Slulf 526190507Slulf gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize); 527190507Slulf 528190507Slulf /* Find a free volume name. */ 529190507Slulf if (volname == NULL) 530190507Slulf volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME); 531190507Slulf 532190507Slulf /* Then we send a request to actually create the volumes. */ 533190507Slulf gctl_ro_param(req, "verb", -1, verb); 534190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 535190507Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 536190507Slulf gctl_ro_param(req, "name", -1, volname); 537190507Slulf errstr = gctl_issue(req); 538190507Slulf if (errstr != NULL) 539190507Slulf warnx("creating %s volume failed: %s", verb, errstr); 540190507Slulfbad: 541190507Slulf gctl_free(req); 542190507Slulf} 543190507Slulf 544190507Slulf/* Parse a line of the config, return the word after <pattern>. */ 545190507Slulfchar * 546190507Slulffind_pattern(char *line, char *pattern) 547190507Slulf{ 548190507Slulf char *ptr; 549190507Slulf 550190507Slulf ptr = strsep(&line, " "); 551190507Slulf while (ptr != NULL) { 552190507Slulf if (!strcmp(ptr, pattern)) { 553190507Slulf /* Return the next. */ 554190507Slulf ptr = strsep(&line, " "); 555190507Slulf return (ptr); 556190507Slulf } 557190507Slulf ptr = strsep(&line, " "); 558190507Slulf } 559190507Slulf return (NULL); 560190507Slulf} 561190507Slulf 562227489Seadler/* Find a free name for an object given a prefix. */ 563190507Slulfchar * 564190507Slulffind_name(const char *prefix, int type, int namelen) 565190507Slulf{ 566190507Slulf struct gctl_req *req; 567190507Slulf char comment[1], buf[GV_CFG_LEN - 1], *name, *sname, *ptr; 568190507Slulf const char *errstr; 569190507Slulf int i, n, begin, len, conflict; 570190507Slulf char line[1024]; 571190507Slulf 572190507Slulf comment[0] = '\0'; 573190507Slulf 574190507Slulf /* Find a name. Fetch out configuration first. */ 575190507Slulf req = gctl_get_handle(); 576190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 577190507Slulf gctl_ro_param(req, "verb", -1, "getconfig"); 578190507Slulf gctl_ro_param(req, "comment", -1, comment); 579190507Slulf gctl_rw_param(req, "config", sizeof(buf), buf); 580190507Slulf errstr = gctl_issue(req); 581190507Slulf if (errstr != NULL) { 582190507Slulf warnx("can't get configuration: %s", errstr); 583190507Slulf return (NULL); 584190507Slulf } 585190507Slulf gctl_free(req); 586190507Slulf 587190507Slulf begin = 0; 588190507Slulf len = strlen(buf); 589190507Slulf i = 0; 590190507Slulf sname = malloc(namelen + 1); 591190507Slulf 592190507Slulf /* XXX: Max object setting? */ 593190507Slulf for (n = 0; n < 10000; n++) { 594190507Slulf snprintf(sname, namelen, "%s%d", prefix, n); 595190507Slulf conflict = 0; 596190507Slulf begin = 0; 597190507Slulf /* Loop through the configuration line by line. */ 598190507Slulf for (i = 0; i < len; i++) { 599190507Slulf if (buf[i] == '\n' || buf[i] == '\0') { 600190507Slulf ptr = buf + begin; 601190507Slulf strlcpy(line, ptr, (i - begin) + 1); 602190507Slulf begin = i + 1; 603190507Slulf switch (type) { 604190507Slulf case GV_TYPE_DRIVE: 605190507Slulf name = find_pattern(line, "drive"); 606190507Slulf break; 607190507Slulf case GV_TYPE_VOL: 608190507Slulf name = find_pattern(line, "volume"); 609190507Slulf break; 610190507Slulf case GV_TYPE_PLEX: 611190507Slulf case GV_TYPE_SD: 612190507Slulf name = find_pattern(line, "name"); 613190507Slulf break; 614190507Slulf default: 615190507Slulf printf("Invalid type given\n"); 616190507Slulf continue; 617190507Slulf } 618190507Slulf if (name == NULL) 619190507Slulf continue; 620190507Slulf if (!strcmp(sname, name)) { 621190507Slulf conflict = 1; 622190507Slulf /* XXX: Could quit the loop earlier. */ 623190507Slulf } 624190507Slulf } 625190507Slulf } 626190507Slulf if (!conflict) 627190507Slulf return (sname); 628190507Slulf } 629190507Slulf free(sname); 630190507Slulf return (NULL); 631190507Slulf} 632190507Slulf 633204665Slulfvoid 634204665Slulfcopy_device(struct gv_drive *d, const char *device) 635190882Slulf{ 636190882Slulf if (strncmp(device, "/dev/", 5) == 0) 637204665Slulf strlcpy(d->device, (device + 5), sizeof(d->device)); 638204665Slulf else 639204665Slulf strlcpy(d->device, device, sizeof(d->device)); 640190882Slulf} 641190882Slulf 642190507Slulf/* Detach a plex or subdisk from its parent. */ 643190507Slulfvoid 644190507Slulfgvinum_detach(int argc, char **argv) 645190507Slulf{ 646190507Slulf const char *errstr; 647190507Slulf struct gctl_req *req; 648190507Slulf int flags, i; 649190507Slulf 650209051Suqs flags = 0; 651190507Slulf optreset = 1; 652190507Slulf optind = 1; 653190507Slulf while ((i = getopt(argc, argv, "f")) != -1) { 654190507Slulf switch(i) { 655190507Slulf case 'f': 656190507Slulf flags |= GV_FLAG_F; 657190507Slulf break; 658190507Slulf default: 659190507Slulf warn("invalid flag: %c", i); 660190507Slulf return; 661190507Slulf } 662190507Slulf } 663190507Slulf argc -= optind; 664190507Slulf argv += optind; 665190507Slulf if (argc != 1) { 666190507Slulf warnx("usage: detach [-f] <subdisk> | <plex>"); 667190507Slulf return; 668190507Slulf } 669190507Slulf 670190507Slulf req = gctl_get_handle(); 671190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 672190507Slulf gctl_ro_param(req, "verb", -1, "detach"); 673190507Slulf gctl_ro_param(req, "object", -1, argv[0]); 674190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 675190507Slulf 676190507Slulf errstr = gctl_issue(req); 677190507Slulf if (errstr != NULL) 678190507Slulf warnx("detach failed: %s", errstr); 679190507Slulf gctl_free(req); 680190507Slulf} 681190507Slulf 682190507Slulfvoid 683130391Slegvinum_help(void) 684130391Sle{ 685130391Sle printf("COMMANDS\n" 686152616Sle "checkparity [-f] plex\n" 687152616Sle " Check the parity blocks of a RAID-5 plex.\n" 688190507Slulf "create [-f] description-file\n" 689152616Sle " Create as per description-file or open editor.\n" 690190507Slulf "attach plex volume [rename]\n" 691190507Slulf "attach subdisk plex [offset] [rename]\n" 692190507Slulf " Attach a plex to a volume, or a subdisk to a plex\n" 693190507Slulf "concat [-fv] [-n name] drives\n" 694190507Slulf " Create a concatenated volume from the specified drives.\n" 695190507Slulf "detach [-f] [plex | subdisk]\n" 696190507Slulf " Detach a plex or a subdisk from the volume or plex to\n" 697190507Slulf " which it is attached.\n" 698190884Slulf "grow plex drive\n" 699190884Slulf " Grow plex by creating a properly sized subdisk on drive\n" 700152616Sle "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" 701130391Sle " List information about specified objects.\n" 702152616Sle "ld [-r] [-v] [-V] [volume]\n" 703130391Sle " List information about drives.\n" 704152616Sle "ls [-r] [-v] [-V] [subdisk]\n" 705130391Sle " List information about subdisks.\n" 706152616Sle "lp [-r] [-v] [-V] [plex]\n" 707130391Sle " List information about plexes.\n" 708152616Sle "lv [-r] [-v] [-V] [volume]\n" 709130391Sle " List information about volumes.\n" 710190507Slulf "mirror [-fsv] [-n name] drives\n" 711190507Slulf " Create a mirrored volume from the specified drives.\n" 712130391Sle "move | mv -f drive object ...\n" 713130391Sle " Move the object(s) to the specified drive.\n" 714130391Sle "quit Exit the vinum program when running in interactive mode." 715130391Sle " Nor-\n" 716130391Sle " mally this would be done by entering the EOF character.\n" 717190507Slulf "raid5 [-fv] [-s stripesize] [-n name] drives\n" 718190507Slulf " Create a RAID-5 volume from the specified drives.\n" 719130391Sle "rename [-r] [drive | subdisk | plex | volume] newname\n" 720130391Sle " Change the name of the specified object.\n" 721152616Sle "rebuildparity plex [-f]\n" 722152616Sle " Rebuild the parity blocks of a RAID-5 plex.\n" 723157052Sle "resetconfig\n" 724157052Sle " Reset the complete gvinum configuration\n" 725190507Slulf "rm [-r] [-f] volume | plex | subdisk | drive\n" 726130391Sle " Remove an object.\n" 727130391Sle "saveconfig\n" 728130391Sle " Save vinum configuration to disk after configuration" 729130391Sle " failures.\n" 730152616Sle "setstate [-f] state [volume | plex | subdisk | drive]\n" 731130391Sle " Set state without influencing other objects, for" 732130391Sle " diagnostic pur-\n" 733130391Sle " poses only.\n" 734152616Sle "start [-S size] volume | plex | subdisk\n" 735130391Sle " Allow the system to access the objects.\n" 736190507Slulf "stripe [-fv] [-n name] drives\n" 737190507Slulf " Create a striped volume from the specified drives.\n" 738130391Sle ); 739130391Sle 740130391Sle return; 741130391Sle} 742130391Sle 743130391Slevoid 744138112Slegvinum_setstate(int argc, char **argv) 745138112Sle{ 746138112Sle struct gctl_req *req; 747138112Sle int flags, i; 748138112Sle const char *errstr; 749138112Sle 750138112Sle flags = 0; 751138112Sle 752138112Sle optreset = 1; 753138112Sle optind = 1; 754138112Sle 755138112Sle while ((i = getopt(argc, argv, "f")) != -1) { 756138112Sle switch (i) { 757138112Sle case 'f': 758138112Sle flags |= GV_FLAG_F; 759138112Sle break; 760138112Sle case '?': 761138112Sle default: 762138112Sle warn("invalid flag: %c", i); 763138112Sle return; 764138112Sle } 765138112Sle } 766138112Sle 767138112Sle argc -= optind; 768138112Sle argv += optind; 769138112Sle 770138112Sle if (argc != 2) { 771138112Sle warnx("usage: setstate [-f] <state> <obj>"); 772138112Sle return; 773138112Sle } 774138112Sle 775138112Sle /* 776138112Sle * XXX: This hack is needed to avoid tripping over (now) invalid 777138112Sle * 'classic' vinum states and will go away later. 778138112Sle */ 779138112Sle if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && 780138112Sle strcmp(argv[0], "stale")) { 781138112Sle warnx("invalid state '%s'", argv[0]); 782138112Sle return; 783138112Sle } 784138112Sle 785138112Sle req = gctl_get_handle(); 786138112Sle gctl_ro_param(req, "class", -1, "VINUM"); 787138112Sle gctl_ro_param(req, "verb", -1, "setstate"); 788138112Sle gctl_ro_param(req, "state", -1, argv[0]); 789138112Sle gctl_ro_param(req, "object", -1, argv[1]); 790138112Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 791138112Sle 792138112Sle errstr = gctl_issue(req); 793138112Sle if (errstr != NULL) 794138112Sle warnx("%s", errstr); 795138112Sle gctl_free(req); 796138112Sle} 797138112Sle 798138112Slevoid 799130391Slegvinum_list(int argc, char **argv) 800130391Sle{ 801130391Sle struct gctl_req *req; 802130391Sle int flags, i, j; 803130391Sle const char *errstr; 804130391Sle char buf[20], *cmd, config[GV_CFG_LEN + 1]; 805130391Sle 806130391Sle flags = 0; 807130391Sle cmd = "list"; 808130391Sle 809130391Sle if (argc) { 810130391Sle optreset = 1; 811130391Sle optind = 1; 812130391Sle cmd = argv[0]; 813130391Sle while ((j = getopt(argc, argv, "rsvV")) != -1) { 814130391Sle switch (j) { 815130391Sle case 'r': 816130391Sle flags |= GV_FLAG_R; 817130391Sle break; 818130391Sle case 's': 819130391Sle flags |= GV_FLAG_S; 820130391Sle break; 821130391Sle case 'v': 822130391Sle flags |= GV_FLAG_V; 823130391Sle break; 824130391Sle case 'V': 825130391Sle flags |= GV_FLAG_V; 826130391Sle flags |= GV_FLAG_VV; 827130391Sle break; 828130391Sle case '?': 829130391Sle default: 830130391Sle return; 831130391Sle } 832130391Sle } 833130391Sle argc -= optind; 834130391Sle argv += optind; 835130391Sle 836130391Sle } 837130391Sle 838130391Sle req = gctl_get_handle(); 839130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 840130391Sle gctl_ro_param(req, "verb", -1, "list"); 841130391Sle gctl_ro_param(req, "cmd", -1, cmd); 842130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 843130391Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 844130391Sle gctl_rw_param(req, "config", sizeof(config), config); 845130391Sle if (argc) { 846130391Sle for (i = 0; i < argc; i++) { 847130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 848130391Sle gctl_ro_param(req, buf, -1, argv[i]); 849130391Sle } 850130391Sle } 851130391Sle errstr = gctl_issue(req); 852130391Sle if (errstr != NULL) { 853130391Sle warnx("can't get configuration: %s", errstr); 854130391Sle gctl_free(req); 855130391Sle return; 856130391Sle } 857130391Sle 858130391Sle printf("%s", config); 859130391Sle gctl_free(req); 860130391Sle return; 861130391Sle} 862130391Sle 863190507Slulf/* Create a mirrored volume. */ 864190507Slulfvoid 865190507Slulfgvinum_mirror(int argc, char **argv) 866190507Slulf{ 867190507Slulf 868190507Slulf if (argc < 2) { 869190507Slulf warnx("usage\tmirror [-fsv] [-n name] drives\n"); 870190507Slulf return; 871190507Slulf } 872190507Slulf create_volume(argc, argv, "mirror"); 873190507Slulf} 874190507Slulf 875152616Sle/* Note that move is currently of form '[-r] target object [...]' */ 876130391Slevoid 877152616Slegvinum_move(int argc, char **argv) 878152616Sle{ 879152616Sle struct gctl_req *req; 880152616Sle const char *errstr; 881152616Sle char buf[20]; 882152616Sle int flags, i, j; 883152616Sle 884152616Sle flags = 0; 885152616Sle if (argc) { 886152616Sle optreset = 1; 887152616Sle optind = 1; 888152616Sle while ((j = getopt(argc, argv, "f")) != -1) { 889152616Sle switch (j) { 890152616Sle case 'f': 891152616Sle flags |= GV_FLAG_F; 892152616Sle break; 893152616Sle case '?': 894152616Sle default: 895152616Sle return; 896152616Sle } 897152616Sle } 898152616Sle argc -= optind; 899152616Sle argv += optind; 900152616Sle } 901152616Sle 902152616Sle switch (argc) { 903152616Sle case 0: 904152616Sle warnx("no destination or object(s) to move specified"); 905152616Sle return; 906152616Sle case 1: 907152616Sle warnx("no object(s) to move specified"); 908152616Sle return; 909152616Sle default: 910152616Sle break; 911152616Sle } 912152616Sle 913152616Sle req = gctl_get_handle(); 914152616Sle gctl_ro_param(req, "class", -1, "VINUM"); 915152616Sle gctl_ro_param(req, "verb", -1, "move"); 916152616Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 917152616Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 918152616Sle gctl_ro_param(req, "destination", -1, argv[0]); 919152616Sle for (i = 1; i < argc; i++) { 920152616Sle snprintf(buf, sizeof(buf), "argv%d", i); 921152616Sle gctl_ro_param(req, buf, -1, argv[i]); 922152616Sle } 923152631Sle errstr = gctl_issue(req); 924152616Sle if (errstr != NULL) 925152616Sle warnx("can't move object(s): %s", errstr); 926152616Sle gctl_free(req); 927152616Sle return; 928152616Sle} 929152616Sle 930152616Slevoid 931130391Slegvinum_printconfig(int argc, char **argv) 932130391Sle{ 933130391Sle printconfig(stdout, ""); 934130391Sle} 935130391Sle 936130391Slevoid 937138110Slegvinum_parityop(int argc, char **argv, int rebuild) 938138110Sle{ 939138110Sle struct gctl_req *req; 940190507Slulf int flags, i; 941138110Sle const char *errstr; 942229915Seadler char *op; 943138110Sle 944138110Sle if (rebuild) { 945138110Sle op = "rebuildparity"; 946138110Sle } else { 947138110Sle op = "checkparity"; 948138110Sle } 949138110Sle 950138110Sle optreset = 1; 951138110Sle optind = 1; 952138110Sle flags = 0; 953138110Sle while ((i = getopt(argc, argv, "fv")) != -1) { 954138110Sle switch (i) { 955138110Sle case 'f': 956138110Sle flags |= GV_FLAG_F; 957138110Sle break; 958138110Sle case 'v': 959138110Sle flags |= GV_FLAG_V; 960138110Sle break; 961138110Sle case '?': 962138110Sle default: 963138110Sle warnx("invalid flag '%c'", i); 964138110Sle return; 965138110Sle } 966138110Sle } 967138110Sle argc -= optind; 968138110Sle argv += optind; 969138110Sle 970138110Sle if (argc != 1) { 971138110Sle warn("usage: %s [-f] [-v] <plex>", op); 972138110Sle return; 973138110Sle } 974138110Sle 975190507Slulf req = gctl_get_handle(); 976190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 977190507Slulf gctl_ro_param(req, "verb", -1, op); 978190507Slulf gctl_ro_param(req, "rebuild", sizeof(int), &rebuild); 979190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 980190507Slulf gctl_ro_param(req, "plex", -1, argv[0]); 981138110Sle 982190507Slulf errstr = gctl_issue(req); 983190507Slulf if (errstr) 984190507Slulf warnx("%s\n", errstr); 985190507Slulf gctl_free(req); 986190507Slulf} 987138110Sle 988190507Slulf/* Create a RAID-5 volume. */ 989190507Slulfvoid 990190507Slulfgvinum_raid5(int argc, char **argv) 991190507Slulf{ 992190507Slulf 993190507Slulf if (argc < 2) { 994190507Slulf warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n"); 995190507Slulf return; 996138110Sle } 997190507Slulf create_volume(argc, argv, "raid5"); 998138110Sle} 999138110Sle 1000190507Slulf 1001138110Slevoid 1002152616Slegvinum_rename(int argc, char **argv) 1003152616Sle{ 1004152616Sle struct gctl_req *req; 1005152616Sle const char *errstr; 1006152616Sle int flags, j; 1007152616Sle 1008152616Sle flags = 0; 1009152616Sle 1010152616Sle if (argc) { 1011152616Sle optreset = 1; 1012152616Sle optind = 1; 1013152616Sle while ((j = getopt(argc, argv, "r")) != -1) { 1014152616Sle switch (j) { 1015152616Sle case 'r': 1016152616Sle flags |= GV_FLAG_R; 1017152616Sle break; 1018152616Sle case '?': 1019152616Sle default: 1020152616Sle return; 1021152616Sle } 1022152616Sle } 1023152616Sle argc -= optind; 1024152616Sle argv += optind; 1025152616Sle } 1026152616Sle 1027152616Sle switch (argc) { 1028152616Sle case 0: 1029152616Sle warnx("no object to rename specified"); 1030152616Sle return; 1031152616Sle case 1: 1032152616Sle warnx("no new name specified"); 1033152616Sle return; 1034152616Sle case 2: 1035152616Sle break; 1036152616Sle default: 1037152616Sle warnx("more than one new name specified"); 1038152616Sle return; 1039152616Sle } 1040152616Sle 1041152616Sle req = gctl_get_handle(); 1042152616Sle gctl_ro_param(req, "class", -1, "VINUM"); 1043152616Sle gctl_ro_param(req, "verb", -1, "rename"); 1044152616Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 1045152616Sle gctl_ro_param(req, "object", -1, argv[0]); 1046152616Sle gctl_ro_param(req, "newname", -1, argv[1]); 1047152631Sle errstr = gctl_issue(req); 1048152616Sle if (errstr != NULL) 1049152616Sle warnx("can't rename object: %s", errstr); 1050152616Sle gctl_free(req); 1051152616Sle return; 1052152616Sle} 1053152616Sle 1054152616Slevoid 1055130391Slegvinum_rm(int argc, char **argv) 1056130391Sle{ 1057130391Sle struct gctl_req *req; 1058130391Sle int flags, i, j; 1059130391Sle const char *errstr; 1060229915Seadler char buf[20]; 1061130391Sle 1062130391Sle flags = 0; 1063130391Sle optreset = 1; 1064130391Sle optind = 1; 1065190507Slulf while ((j = getopt(argc, argv, "rf")) != -1) { 1066130391Sle switch (j) { 1067190507Slulf case 'f': 1068190507Slulf flags |= GV_FLAG_F; 1069190507Slulf break; 1070130391Sle case 'r': 1071130391Sle flags |= GV_FLAG_R; 1072130391Sle break; 1073130391Sle case '?': 1074130391Sle default: 1075130391Sle return; 1076130391Sle } 1077130391Sle } 1078130391Sle argc -= optind; 1079130391Sle argv += optind; 1080130391Sle 1081130391Sle req = gctl_get_handle(); 1082130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1083130391Sle gctl_ro_param(req, "verb", -1, "remove"); 1084130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 1085130391Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 1086130391Sle if (argc) { 1087130391Sle for (i = 0; i < argc; i++) { 1088130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 1089130391Sle gctl_ro_param(req, buf, -1, argv[i]); 1090130391Sle } 1091130391Sle } 1092130391Sle errstr = gctl_issue(req); 1093130391Sle if (errstr != NULL) { 1094130391Sle warnx("can't remove: %s", errstr); 1095130391Sle gctl_free(req); 1096130391Sle return; 1097130391Sle } 1098130391Sle gctl_free(req); 1099130391Sle} 1100130391Sle 1101130391Slevoid 1102157052Slegvinum_resetconfig(void) 1103157052Sle{ 1104157052Sle struct gctl_req *req; 1105157052Sle const char *errstr; 1106157052Sle char reply[32]; 1107157052Sle 1108157052Sle if (!isatty(STDIN_FILENO)) { 1109157052Sle warn("Please enter this command from a tty device\n"); 1110157052Sle return; 1111157052Sle } 1112157052Sle printf(" WARNING! This command will completely wipe out your gvinum" 1113157052Sle "configuration.\n" 1114157052Sle " All data will be lost. If you really want to do this," 1115157052Sle " enter the text\n\n" 1116157052Sle " NO FUTURE\n" 1117157052Sle " Enter text -> "); 1118157052Sle fgets(reply, sizeof(reply), stdin); 1119157052Sle if (strcmp(reply, "NO FUTURE\n")) { 1120157052Sle printf("\n No change\n"); 1121157052Sle return; 1122157052Sle } 1123157052Sle req = gctl_get_handle(); 1124157052Sle gctl_ro_param(req, "class", -1, "VINUM"); 1125157052Sle gctl_ro_param(req, "verb", -1, "resetconfig"); 1126157052Sle errstr = gctl_issue(req); 1127157052Sle if (errstr != NULL) { 1128157052Sle warnx("can't reset config: %s", errstr); 1129157052Sle gctl_free(req); 1130157052Sle return; 1131157052Sle } 1132157052Sle gctl_free(req); 1133157052Sle printf("gvinum configuration obliterated\n"); 1134157052Sle} 1135157052Sle 1136157052Slevoid 1137130391Slegvinum_saveconfig(void) 1138130391Sle{ 1139130391Sle struct gctl_req *req; 1140130391Sle const char *errstr; 1141130391Sle 1142130391Sle req = gctl_get_handle(); 1143130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1144130391Sle gctl_ro_param(req, "verb", -1, "saveconfig"); 1145130391Sle errstr = gctl_issue(req); 1146130391Sle if (errstr != NULL) 1147130391Sle warnx("can't save configuration: %s", errstr); 1148130391Sle gctl_free(req); 1149130391Sle} 1150130391Sle 1151130391Slevoid 1152130391Slegvinum_start(int argc, char **argv) 1153130391Sle{ 1154130391Sle struct gctl_req *req; 1155130391Sle int i, initsize, j; 1156130391Sle const char *errstr; 1157130391Sle char buf[20]; 1158130391Sle 1159130391Sle /* 'start' with no arguments is a no-op. */ 1160130391Sle if (argc == 1) 1161130391Sle return; 1162130391Sle 1163130391Sle initsize = 0; 1164130391Sle 1165130391Sle optreset = 1; 1166130391Sle optind = 1; 1167130391Sle while ((j = getopt(argc, argv, "S")) != -1) { 1168130391Sle switch (j) { 1169130391Sle case 'S': 1170130391Sle initsize = atoi(optarg); 1171130391Sle break; 1172130391Sle case '?': 1173130391Sle default: 1174130391Sle return; 1175130391Sle } 1176130391Sle } 1177130391Sle argc -= optind; 1178130391Sle argv += optind; 1179130391Sle 1180130391Sle if (!initsize) 1181130391Sle initsize = 512; 1182130391Sle 1183130391Sle req = gctl_get_handle(); 1184130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1185130391Sle gctl_ro_param(req, "verb", -1, "start"); 1186130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 1187130391Sle gctl_ro_param(req, "initsize", sizeof(int), &initsize); 1188130391Sle if (argc) { 1189130391Sle for (i = 0; i < argc; i++) { 1190130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 1191130391Sle gctl_ro_param(req, buf, -1, argv[i]); 1192130391Sle } 1193130391Sle } 1194130391Sle errstr = gctl_issue(req); 1195130391Sle if (errstr != NULL) { 1196130391Sle warnx("can't start: %s", errstr); 1197130391Sle gctl_free(req); 1198130391Sle return; 1199130391Sle } 1200130391Sle 1201130391Sle gctl_free(req); 1202130391Sle} 1203130391Sle 1204130391Slevoid 1205130391Slegvinum_stop(int argc, char **argv) 1206130391Sle{ 1207190507Slulf int err, fileid; 1208130391Sle 1209265536Smarius fileid = kldfind(GVINUMKLD); 1210130391Sle if (fileid == -1) { 1211265536Smarius if (modfind(GVINUMMOD) < 0) 1212265536Smarius warn("cannot find " GVINUMKLD); 1213130391Sle return; 1214130391Sle } 1215190507Slulf 1216190507Slulf /* 1217190507Slulf * This little hack prevents that we end up in an infinite loop in 1218190507Slulf * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM 1219190507Slulf * event thread will be free for the g_wither_geom() call from 1220190507Slulf * gv_unload(). It's silly, but it works. 1221190507Slulf */ 1222265536Smarius printf("unloading " GVINUMKLD " kernel module... "); 1223190507Slulf fflush(stdout); 1224190507Slulf if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) { 1225190507Slulf sleep(1); 1226190507Slulf err = kldunload(fileid); 1227190507Slulf } 1228190507Slulf if (err != 0) { 1229190507Slulf printf(" failed!\n"); 1230265536Smarius warn("cannot unload " GVINUMKLD); 1231130391Sle return; 1232130391Sle } 1233130391Sle 1234190507Slulf printf("done\n"); 1235130391Sle exit(0); 1236130391Sle} 1237130391Sle 1238190507Slulf/* Create a striped volume. */ 1239130391Slevoid 1240190507Slulfgvinum_stripe(int argc, char **argv) 1241190507Slulf{ 1242190507Slulf 1243190507Slulf if (argc < 2) { 1244190507Slulf warnx("usage:\tstripe [-fv] [-n name] drives\n"); 1245190507Slulf return; 1246190507Slulf } 1247190507Slulf create_volume(argc, argv, "stripe"); 1248190507Slulf} 1249190507Slulf 1250190884Slulf/* Grow a subdisk by adding disk backed by provider. */ 1251190507Slulfvoid 1252190884Slulfgvinum_grow(int argc, char **argv) 1253190884Slulf{ 1254190884Slulf struct gctl_req *req; 1255190884Slulf char *drive, *sdname; 1256190884Slulf char sdprefix[GV_MAXSDNAME]; 1257190884Slulf struct gv_drive *d; 1258190884Slulf struct gv_sd *s; 1259190884Slulf const char *errstr; 1260190884Slulf int drives, volumes, plexes, subdisks, flags; 1261190884Slulf 1262190884Slulf drives = volumes = plexes = subdisks = 0; 1263190884Slulf if (argc < 3) { 1264190884Slulf warnx("usage:\tgrow plex drive\n"); 1265190884Slulf return; 1266190884Slulf } 1267190884Slulf 1268190884Slulf s = gv_alloc_sd(); 1269190884Slulf if (s == NULL) { 1270190884Slulf warn("unable to create subdisk"); 1271190884Slulf return; 1272190884Slulf } 1273190884Slulf d = gv_alloc_drive(); 1274190884Slulf if (d == NULL) { 1275190884Slulf warn("unable to create drive"); 1276190884Slulf free(s); 1277190884Slulf return; 1278190884Slulf } 1279190884Slulf /* Lookup device and set an appropriate drive name. */ 1280204665Slulf drive = find_drive(); 1281190884Slulf if (drive == NULL) { 1282190884Slulf warn("unable to find an appropriate drive name"); 1283190884Slulf free(s); 1284190884Slulf free(d); 1285190884Slulf return; 1286190884Slulf } 1287190884Slulf strlcpy(d->name, drive, sizeof(d->name)); 1288204665Slulf copy_device(d, argv[2]); 1289204665Slulf 1290190884Slulf drives = 1; 1291190884Slulf 1292190884Slulf /* We try to use the plex name as basis for the subdisk name. */ 1293190884Slulf snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]); 1294190884Slulf sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME); 1295190884Slulf if (sdname == NULL) { 1296190884Slulf warn("unable to find an appropriate subdisk name"); 1297190884Slulf free(s); 1298190884Slulf free(d); 1299190884Slulf free(drive); 1300190884Slulf return; 1301190884Slulf } 1302190884Slulf strlcpy(s->name, sdname, sizeof(s->name)); 1303190884Slulf free(sdname); 1304190884Slulf strlcpy(s->plex, argv[1], sizeof(s->plex)); 1305190884Slulf strlcpy(s->drive, d->name, sizeof(s->drive)); 1306190884Slulf subdisks = 1; 1307190884Slulf 1308190884Slulf req = gctl_get_handle(); 1309190884Slulf gctl_ro_param(req, "class", -1, "VINUM"); 1310190884Slulf gctl_ro_param(req, "verb", -1, "create"); 1311190884Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 1312190884Slulf gctl_ro_param(req, "volumes", sizeof(int), &volumes); 1313190884Slulf gctl_ro_param(req, "plexes", sizeof(int), &plexes); 1314190884Slulf gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 1315190884Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 1316190884Slulf gctl_ro_param(req, "drive0", sizeof(*d), d); 1317190884Slulf gctl_ro_param(req, "sd0", sizeof(*s), s); 1318190884Slulf errstr = gctl_issue(req); 1319190884Slulf free(drive); 1320190884Slulf if (errstr != NULL) { 1321190884Slulf warnx("unable to grow plex: %s", errstr); 1322190884Slulf free(s); 1323190884Slulf free(d); 1324190884Slulf return; 1325190884Slulf } 1326190884Slulf gctl_free(req); 1327190884Slulf} 1328190884Slulf 1329190884Slulfvoid 1330130391Sleparseline(int argc, char **argv) 1331130391Sle{ 1332130391Sle if (argc <= 0) 1333130391Sle return; 1334130391Sle 1335150044Sle if (!strcmp(argv[0], "create")) 1336130391Sle gvinum_create(argc, argv); 1337130391Sle else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) 1338130391Sle exit(0); 1339190507Slulf else if (!strcmp(argv[0], "attach")) 1340190507Slulf gvinum_attach(argc, argv); 1341190507Slulf else if (!strcmp(argv[0], "detach")) 1342190507Slulf gvinum_detach(argc, argv); 1343190507Slulf else if (!strcmp(argv[0], "concat")) 1344190507Slulf gvinum_concat(argc, argv); 1345190884Slulf else if (!strcmp(argv[0], "grow")) 1346190884Slulf gvinum_grow(argc, argv); 1347130391Sle else if (!strcmp(argv[0], "help")) 1348130391Sle gvinum_help(); 1349130391Sle else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) 1350130391Sle gvinum_list(argc, argv); 1351130391Sle else if (!strcmp(argv[0], "ld")) 1352130391Sle gvinum_list(argc, argv); 1353130391Sle else if (!strcmp(argv[0], "lp")) 1354130391Sle gvinum_list(argc, argv); 1355130391Sle else if (!strcmp(argv[0], "ls")) 1356130391Sle gvinum_list(argc, argv); 1357130391Sle else if (!strcmp(argv[0], "lv")) 1358130391Sle gvinum_list(argc, argv); 1359190507Slulf else if (!strcmp(argv[0], "mirror")) 1360190507Slulf gvinum_mirror(argc, argv); 1361152616Sle else if (!strcmp(argv[0], "move")) 1362152616Sle gvinum_move(argc, argv); 1363152616Sle else if (!strcmp(argv[0], "mv")) 1364152616Sle gvinum_move(argc, argv); 1365130391Sle else if (!strcmp(argv[0], "printconfig")) 1366130391Sle gvinum_printconfig(argc, argv); 1367190507Slulf else if (!strcmp(argv[0], "raid5")) 1368190507Slulf gvinum_raid5(argc, argv); 1369152616Sle else if (!strcmp(argv[0], "rename")) 1370152616Sle gvinum_rename(argc, argv); 1371157052Sle else if (!strcmp(argv[0], "resetconfig")) 1372157052Sle gvinum_resetconfig(); 1373130391Sle else if (!strcmp(argv[0], "rm")) 1374130391Sle gvinum_rm(argc, argv); 1375130391Sle else if (!strcmp(argv[0], "saveconfig")) 1376130391Sle gvinum_saveconfig(); 1377138112Sle else if (!strcmp(argv[0], "setstate")) 1378138112Sle gvinum_setstate(argc, argv); 1379130391Sle else if (!strcmp(argv[0], "start")) 1380130391Sle gvinum_start(argc, argv); 1381130391Sle else if (!strcmp(argv[0], "stop")) 1382130391Sle gvinum_stop(argc, argv); 1383190507Slulf else if (!strcmp(argv[0], "stripe")) 1384190507Slulf gvinum_stripe(argc, argv); 1385138110Sle else if (!strcmp(argv[0], "checkparity")) 1386138110Sle gvinum_parityop(argc, argv, 0); 1387138110Sle else if (!strcmp(argv[0], "rebuildparity")) 1388138110Sle gvinum_parityop(argc, argv, 1); 1389130391Sle else 1390130391Sle printf("unknown command '%s'\n", argv[0]); 1391130391Sle 1392130391Sle return; 1393130391Sle} 1394130391Sle 1395130391Sle/* 1396130391Sle * The guts of printconfig. This is called from gvinum_printconfig and from 1397130391Sle * gvinum_create when called without an argument, in order to give the user 1398130391Sle * something to edit. 1399130391Sle */ 1400130391Slevoid 1401130391Sleprintconfig(FILE *of, char *comment) 1402130391Sle{ 1403130391Sle struct gctl_req *req; 1404130391Sle struct utsname uname_s; 1405130391Sle const char *errstr; 1406130391Sle time_t now; 1407130391Sle char buf[GV_CFG_LEN + 1]; 1408130391Sle 1409130391Sle uname(&uname_s); 1410130391Sle time(&now); 1411130391Sle 1412130391Sle req = gctl_get_handle(); 1413130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1414130391Sle gctl_ro_param(req, "verb", -1, "getconfig"); 1415130391Sle gctl_ro_param(req, "comment", -1, comment); 1416130391Sle gctl_rw_param(req, "config", sizeof(buf), buf); 1417130391Sle errstr = gctl_issue(req); 1418130391Sle if (errstr != NULL) { 1419130391Sle warnx("can't get configuration: %s", errstr); 1420130391Sle return; 1421130391Sle } 1422130391Sle gctl_free(req); 1423130391Sle 1424130391Sle fprintf(of, "# Vinum configuration of %s, saved at %s", 1425130391Sle uname_s.nodename, 1426130391Sle ctime(&now)); 1427130391Sle 1428130391Sle if (*comment != '\0') 1429130391Sle fprintf(of, "# Current configuration:\n"); 1430130391Sle 1431215704Sbrucec fprintf(of, "%s", buf); 1432130391Sle} 1433