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 61266043Smariusstatic void gvinum_attach(int, char * const *); 62266043Smariusstatic void gvinum_concat(int, char * const *); 63266043Smariusstatic void gvinum_create(int, char * const *); 64266043Smariusstatic void gvinum_detach(int, char * const *); 65266043Smariusstatic void gvinum_grow(int, char * const *); 66266043Smariusstatic void gvinum_help(void); 67266043Smariusstatic void gvinum_list(int, char * const *); 68266043Smariusstatic void gvinum_move(int, char * const *); 69266043Smariusstatic void gvinum_mirror(int, char * const *); 70266043Smariusstatic void gvinum_parityop(int, char * const * , int); 71266043Smariusstatic void gvinum_printconfig(int, char * const *); 72266043Smariusstatic void gvinum_raid5(int, char * const *); 73266043Smariusstatic void gvinum_rename(int, char * const *); 74266043Smariusstatic void gvinum_resetconfig(int, char * const *); 75266043Smariusstatic void gvinum_rm(int, char * const *); 76266043Smariusstatic void gvinum_saveconfig(void); 77266043Smariusstatic void gvinum_setstate(int, char * const *); 78266043Smariusstatic void gvinum_start(int, char * const *); 79266043Smariusstatic void gvinum_stop(int, char * const *); 80266043Smariusstatic void gvinum_stripe(int, char * const *); 81266043Smariusstatic void parseline(int, char * const *); 82266043Smariusstatic void printconfig(FILE *, const char *); 83130391Sle 84266043Smariusstatic char *create_drive(const char *); 85266043Smariusstatic void create_volume(int, char * const * , const char *); 86266043Smariusstatic char *find_name(const char *, int, int); 87266043Smariusstatic const char *find_pattern(char *, const char *); 88266043Smariusstatic void copy_device(struct gv_drive *, const char *); 89266043Smarius#define find_drive() \ 90266043Smarius find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME) 91190507Slulf 92130391Sleint 93130391Slemain(int argc, char **argv) 94130391Sle{ 95130391Sle int line, tokens; 96130391Sle char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS]; 97130391Sle 98130391Sle /* Load the module if necessary. */ 99265536Smarius if (modfind(GVINUMMOD) < 0) { 100265536Smarius if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0) 101265536Smarius err(1, GVINUMKLD ": Kernel module not available"); 102265536Smarius } 103130391Sle 104130391Sle /* Arguments given on the command line. */ 105130391Sle if (argc > 1) { 106130391Sle argc--; 107130391Sle argv++; 108130391Sle parseline(argc, argv); 109130391Sle 110130391Sle /* Interactive mode. */ 111130391Sle } else { 112130391Sle for (;;) { 113130391Sle inputline = readline("gvinum -> "); 114130391Sle if (inputline == NULL) { 115130391Sle if (ferror(stdin)) { 116130391Sle err(1, "can't read input"); 117130391Sle } else { 118130391Sle printf("\n"); 119130391Sle exit(0); 120130391Sle } 121130391Sle } else if (*inputline) { 122130391Sle add_history(inputline); 123130391Sle strcpy(buffer, inputline); 124130391Sle free(inputline); 125130391Sle line++; /* count the lines */ 126130391Sle tokens = gv_tokenize(buffer, token, GV_MAXARGS); 127130391Sle if (tokens) 128130391Sle parseline(tokens, token); 129130391Sle } 130130391Sle } 131130391Sle } 132130391Sle exit(0); 133130391Sle} 134130391Sle 135190507Slulf/* Attach a plex to a volume or a subdisk to a plex. */ 136266043Smariusstatic void 137266043Smariusgvinum_attach(int argc, char * const *argv) 138190507Slulf{ 139190507Slulf struct gctl_req *req; 140190507Slulf const char *errstr; 141190507Slulf int rename; 142190507Slulf off_t offset; 143190507Slulf 144190507Slulf rename = 0; 145190507Slulf offset = -1; 146190507Slulf if (argc < 3) { 147190507Slulf warnx("usage:\tattach <subdisk> <plex> [rename] " 148190507Slulf "[<plexoffset>]\n" 149190507Slulf "\tattach <plex> <volume> [rename]"); 150190507Slulf return; 151190507Slulf } 152190507Slulf if (argc > 3) { 153190507Slulf if (!strcmp(argv[3], "rename")) { 154190507Slulf rename = 1; 155190507Slulf if (argc == 5) 156190507Slulf offset = strtol(argv[4], NULL, 0); 157190507Slulf } else 158190507Slulf offset = strtol(argv[3], NULL, 0); 159190507Slulf } 160190507Slulf req = gctl_get_handle(); 161190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 162190507Slulf gctl_ro_param(req, "verb", -1, "attach"); 163190507Slulf gctl_ro_param(req, "child", -1, argv[1]); 164190507Slulf gctl_ro_param(req, "parent", -1, argv[2]); 165190507Slulf gctl_ro_param(req, "offset", sizeof(off_t), &offset); 166190507Slulf gctl_ro_param(req, "rename", sizeof(int), &rename); 167190507Slulf errstr = gctl_issue(req); 168190507Slulf if (errstr != NULL) 169190507Slulf warnx("attach failed: %s", errstr); 170190507Slulf gctl_free(req); 171190507Slulf} 172190507Slulf 173266043Smariusstatic void 174266043Smariusgvinum_create(int argc, char * const *argv) 175130391Sle{ 176130391Sle struct gctl_req *req; 177130391Sle struct gv_drive *d; 178130391Sle struct gv_plex *p; 179130391Sle struct gv_sd *s; 180130391Sle struct gv_volume *v; 181130391Sle FILE *tmp; 182190507Slulf int drives, errors, fd, flags, i, line, plexes, plex_in_volume; 183190507Slulf int sd_in_plex, status, subdisks, tokens, undeffd, volumes; 184130391Sle const char *errstr; 185190507Slulf char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *ed, *sdname; 186130391Sle char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS]; 187130391Sle char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME]; 188130391Sle 189190507Slulf tmp = NULL; 190190507Slulf flags = 0; 191190507Slulf for (i = 1; i < argc; i++) { 192190507Slulf /* Force flag used to ignore already created drives. */ 193190507Slulf if (!strcmp(argv[i], "-f")) { 194190507Slulf flags |= GV_FLAG_F; 195190507Slulf /* Else it must be a file. */ 196190507Slulf } else { 197266014Smarius if ((tmp = fopen(argv[i], "r")) == NULL) { 198266014Smarius warn("can't open '%s' for reading", argv[i]); 199190507Slulf return; 200190507Slulf } 201190507Slulf } 202190507Slulf } 203190507Slulf 204190507Slulf /* We didn't get a file. */ 205190507Slulf if (tmp == NULL) { 206133097Sle snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX"); 207133097Sle 208133097Sle if ((fd = mkstemp(tmpfile)) == -1) { 209133097Sle warn("temporary file not accessible"); 210133097Sle return; 211133097Sle } 212133097Sle if ((tmp = fdopen(fd, "w")) == NULL) { 213133097Sle warn("can't open '%s' for writing", tmpfile); 214133097Sle return; 215133097Sle } 216133097Sle printconfig(tmp, "# "); 217133097Sle fclose(tmp); 218133097Sle 219133097Sle ed = getenv("EDITOR"); 220133097Sle if (ed == NULL) 221133097Sle ed = _PATH_VI; 222133097Sle 223133097Sle snprintf(commandline, sizeof(commandline), "%s %s", ed, 224133097Sle tmpfile); 225133097Sle status = system(commandline); 226133097Sle if (status != 0) { 227133097Sle warn("couldn't exec %s; status: %d", ed, status); 228133097Sle return; 229133097Sle } 230133097Sle 231133097Sle if ((tmp = fopen(tmpfile, "r")) == NULL) { 232133097Sle warn("can't open '%s' for reading", tmpfile); 233133097Sle return; 234133097Sle } 235130391Sle } 236130391Sle 237130391Sle req = gctl_get_handle(); 238130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 239130391Sle gctl_ro_param(req, "verb", -1, "create"); 240190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 241130391Sle 242130391Sle drives = volumes = plexes = subdisks = 0; 243190507Slulf plex_in_volume = sd_in_plex = undeffd = 0; 244190507Slulf plex[0] = '\0'; 245130391Sle errors = 0; 246130391Sle line = 1; 247130391Sle while ((fgets(buf, BUFSIZ, tmp)) != NULL) { 248130391Sle 249130391Sle /* Skip empty lines and comments. */ 250130391Sle if (*buf == '\0' || *buf == '#') { 251130391Sle line++; 252130391Sle continue; 253130391Sle } 254130391Sle 255130391Sle /* Kill off the newline. */ 256130391Sle buf[strlen(buf) - 1] = '\0'; 257130391Sle 258130391Sle /* 259130391Sle * Copy the original input line in case we need it for error 260130391Sle * output. 261130391Sle */ 262190507Slulf strlcpy(original, buf, sizeof(original)); 263130391Sle 264130391Sle tokens = gv_tokenize(buf, token, GV_MAXARGS); 265150044Sle if (tokens <= 0) { 266150044Sle line++; 267150044Sle continue; 268150044Sle } 269130391Sle 270150044Sle /* Volume definition. */ 271150044Sle if (!strcmp(token[0], "volume")) { 272150044Sle v = gv_new_volume(tokens, token); 273150044Sle if (v == NULL) { 274150044Sle warnx("line %d: invalid volume definition", 275150044Sle line); 276150044Sle warnx("line %d: '%s'", line, original); 277150044Sle errors++; 278150044Sle line++; 279150044Sle continue; 280150044Sle } 281130391Sle 282150044Sle /* Reset plex count for this volume. */ 283150044Sle plex_in_volume = 0; 284130391Sle 285150044Sle /* 286150044Sle * Set default volume name for following plex 287150044Sle * definitions. 288150044Sle */ 289190507Slulf strlcpy(volume, v->name, sizeof(volume)); 290130391Sle 291150044Sle snprintf(buf1, sizeof(buf1), "volume%d", volumes); 292150044Sle gctl_ro_param(req, buf1, sizeof(*v), v); 293150044Sle volumes++; 294130391Sle 295150044Sle /* Plex definition. */ 296150044Sle } else if (!strcmp(token[0], "plex")) { 297150044Sle p = gv_new_plex(tokens, token); 298150044Sle if (p == NULL) { 299150044Sle warnx("line %d: invalid plex definition", line); 300150044Sle warnx("line %d: '%s'", line, original); 301150044Sle errors++; 302150044Sle line++; 303150044Sle continue; 304150044Sle } 305130391Sle 306150044Sle /* Reset subdisk count for this plex. */ 307150044Sle sd_in_plex = 0; 308130391Sle 309150044Sle /* Default name. */ 310150044Sle if (strlen(p->name) == 0) { 311190507Slulf snprintf(p->name, sizeof(p->name), "%s.p%d", 312150044Sle volume, plex_in_volume++); 313150044Sle } 314130391Sle 315150044Sle /* Default volume. */ 316150044Sle if (strlen(p->volume) == 0) { 317190507Slulf snprintf(p->volume, sizeof(p->volume), "%s", 318150044Sle volume); 319150044Sle } 320130391Sle 321150044Sle /* 322150044Sle * Set default plex name for following subdisk 323150044Sle * definitions. 324150044Sle */ 325190507Slulf strlcpy(plex, p->name, sizeof(plex)); 326130391Sle 327150044Sle snprintf(buf1, sizeof(buf1), "plex%d", plexes); 328150044Sle gctl_ro_param(req, buf1, sizeof(*p), p); 329150044Sle plexes++; 330130391Sle 331150044Sle /* Subdisk definition. */ 332150044Sle } else if (!strcmp(token[0], "sd")) { 333150044Sle s = gv_new_sd(tokens, token); 334150044Sle if (s == NULL) { 335150044Sle warnx("line %d: invalid subdisk " 336150044Sle "definition:", line); 337150044Sle warnx("line %d: '%s'", line, original); 338150044Sle errors++; 339150044Sle line++; 340150044Sle continue; 341150044Sle } 342130391Sle 343150044Sle /* Default name. */ 344150044Sle if (strlen(s->name) == 0) { 345190507Slulf if (strlen(plex) == 0) { 346190507Slulf sdname = find_name("gvinumsubdisk.p", 347190507Slulf GV_TYPE_SD, GV_MAXSDNAME); 348190507Slulf snprintf(s->name, sizeof(s->name), 349190507Slulf "%s.s%d", sdname, undeffd++); 350190507Slulf free(sdname); 351190507Slulf } else { 352190507Slulf snprintf(s->name, sizeof(s->name), 353190507Slulf "%s.s%d",plex, sd_in_plex++); 354190507Slulf } 355150044Sle } 356150044Sle 357150044Sle /* Default plex. */ 358150044Sle if (strlen(s->plex) == 0) 359190507Slulf snprintf(s->plex, sizeof(s->plex), "%s", plex); 360150044Sle 361150044Sle snprintf(buf1, sizeof(buf1), "sd%d", subdisks); 362150044Sle gctl_ro_param(req, buf1, sizeof(*s), s); 363150044Sle subdisks++; 364150044Sle 365150044Sle /* Subdisk definition. */ 366150044Sle } else if (!strcmp(token[0], "drive")) { 367150044Sle d = gv_new_drive(tokens, token); 368150044Sle if (d == NULL) { 369150044Sle warnx("line %d: invalid drive definition:", 370150044Sle line); 371130391Sle warnx("line %d: '%s'", line, original); 372130391Sle errors++; 373150044Sle line++; 374150044Sle continue; 375130391Sle } 376150044Sle 377150044Sle snprintf(buf1, sizeof(buf1), "drive%d", drives); 378150044Sle gctl_ro_param(req, buf1, sizeof(*d), d); 379150044Sle drives++; 380150044Sle 381150044Sle /* Everything else is bogus. */ 382150044Sle } else { 383150044Sle warnx("line %d: invalid definition:", line); 384150044Sle warnx("line %d: '%s'", line, original); 385150044Sle errors++; 386130391Sle } 387130391Sle line++; 388130391Sle } 389130391Sle 390130391Sle fclose(tmp); 391130391Sle unlink(tmpfile); 392130391Sle 393130391Sle if (!errors && (volumes || plexes || subdisks || drives)) { 394130391Sle gctl_ro_param(req, "volumes", sizeof(int), &volumes); 395130391Sle gctl_ro_param(req, "plexes", sizeof(int), &plexes); 396130391Sle gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 397130391Sle gctl_ro_param(req, "drives", sizeof(int), &drives); 398130391Sle errstr = gctl_issue(req); 399130391Sle if (errstr != NULL) 400130391Sle warnx("create failed: %s", errstr); 401130391Sle } 402130391Sle gctl_free(req); 403130391Sle} 404130391Sle 405190507Slulf/* Create a concatenated volume. */ 406266043Smariusstatic void 407266043Smariusgvinum_concat(int argc, char * const *argv) 408190507Slulf{ 409190507Slulf 410190507Slulf if (argc < 2) { 411190507Slulf warnx("usage:\tconcat [-fv] [-n name] drives\n"); 412190507Slulf return; 413190507Slulf } 414190507Slulf create_volume(argc, argv, "concat"); 415190507Slulf} 416190507Slulf 417190507Slulf/* Create a drive quick and dirty. */ 418266043Smariusstatic char * 419266043Smariuscreate_drive(const 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; 426266042Smarius int found = 0; 427190507Slulf 428190507Slulf flags = plexes = subdisks = volumes = 0; 429190507Slulf drives = 1; 430190507Slulf dname = NULL; 431190507Slulf 432204665Slulf drivename = find_drive(); 433190507Slulf if (drivename == NULL) 434190507Slulf return (NULL); 435190507Slulf 436190507Slulf req = gctl_get_handle(); 437190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 438190507Slulf gctl_ro_param(req, "verb", -1, "create"); 439190881Slulf d = gv_alloc_drive(); 440190507Slulf if (d == NULL) 441190507Slulf err(1, "unable to allocate for gv_drive object"); 442190507Slulf 443190507Slulf strlcpy(d->name, drivename, sizeof(d->name)); 444204665Slulf copy_device(d, device); 445190507Slulf gctl_ro_param(req, "drive0", sizeof(*d), d); 446190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 447190507Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 448190507Slulf gctl_ro_param(req, "volumes", sizeof(int), &volumes); 449190507Slulf gctl_ro_param(req, "plexes", sizeof(int), &plexes); 450190507Slulf gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 451190507Slulf errstr = gctl_issue(req); 452190507Slulf if (errstr != NULL) { 453190507Slulf warnx("error creating drive: %s", errstr); 454266042Smarius drivename = NULL; 455190507Slulf } else { 456190507Slulf /* XXX: This is needed because we have to make sure the drives 457190507Slulf * are created before we return. */ 458190507Slulf /* Loop until it's in the config. */ 459190507Slulf for (i = 0; i < 100000; i++) { 460190507Slulf dname = find_name("gvinumdrive", GV_TYPE_DRIVE, 461190507Slulf GV_MAXDRIVENAME); 462190507Slulf /* If we got a different name, quit. */ 463190507Slulf if (dname == NULL) 464190507Slulf continue; 465266042Smarius if (strcmp(dname, drivename)) 466266042Smarius found = 1; 467190507Slulf free(dname); 468190507Slulf dname = NULL; 469266042Smarius if (found) 470266042Smarius break; 471190507Slulf usleep(100000); /* Sleep for 0.1s */ 472190507Slulf } 473266042Smarius if (found == 0) { 474266042Smarius warnx("error creating drive"); 475266042Smarius drivename = NULL; 476266042Smarius } 477190507Slulf } 478190507Slulf gctl_free(req); 479190507Slulf return (drivename); 480190507Slulf} 481190507Slulf 482266043Smarius/* 483190507Slulf * General routine for creating a volume. Mainly for use by concat, mirror, 484190507Slulf * raid5 and stripe commands. 485190507Slulf */ 486266043Smariusstatic void 487266043Smariuscreate_volume(int argc, char * const *argv, const char *verb) 488190507Slulf{ 489190507Slulf struct gctl_req *req; 490190507Slulf const char *errstr; 491190507Slulf char buf[BUFSIZ], *drivename, *volname; 492190507Slulf int drives, flags, i; 493190507Slulf off_t stripesize; 494190507Slulf 495190507Slulf flags = 0; 496190507Slulf drives = 0; 497190507Slulf volname = NULL; 498190507Slulf stripesize = 262144; 499190507Slulf 500190507Slulf /* XXX: Should we check for argument length? */ 501190507Slulf 502190507Slulf req = gctl_get_handle(); 503190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 504190507Slulf 505190507Slulf for (i = 1; i < argc; i++) { 506190507Slulf if (!strcmp(argv[i], "-f")) { 507190507Slulf flags |= GV_FLAG_F; 508190507Slulf } else if (!strcmp(argv[i], "-n")) { 509190507Slulf volname = argv[++i]; 510190507Slulf } else if (!strcmp(argv[i], "-v")) { 511190507Slulf flags |= GV_FLAG_V; 512190507Slulf } else if (!strcmp(argv[i], "-s")) { 513190507Slulf flags |= GV_FLAG_S; 514190507Slulf if (!strcmp(verb, "raid5")) 515190507Slulf stripesize = gv_sizespec(argv[++i]); 516190507Slulf } else { 517190507Slulf /* Assume it's a drive. */ 518190507Slulf snprintf(buf, sizeof(buf), "drive%d", drives++); 519190507Slulf 520190507Slulf /* First we create the drive. */ 521266043Smarius drivename = create_drive(argv[i]); 522190507Slulf if (drivename == NULL) 523190507Slulf goto bad; 524190507Slulf /* Then we add it to the request. */ 525190507Slulf gctl_ro_param(req, buf, -1, drivename); 526190507Slulf } 527190507Slulf } 528190507Slulf 529190507Slulf gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize); 530190507Slulf 531190507Slulf /* Find a free volume name. */ 532190507Slulf if (volname == NULL) 533190507Slulf volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME); 534190507Slulf 535190507Slulf /* Then we send a request to actually create the volumes. */ 536190507Slulf gctl_ro_param(req, "verb", -1, verb); 537266043Smarius gctl_ro_param(req, "flags", sizeof(int), &flags); 538190507Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 539190507Slulf gctl_ro_param(req, "name", -1, volname); 540190507Slulf errstr = gctl_issue(req); 541190507Slulf if (errstr != NULL) 542190507Slulf warnx("creating %s volume failed: %s", verb, errstr); 543190507Slulfbad: 544190507Slulf gctl_free(req); 545190507Slulf} 546190507Slulf 547190507Slulf/* Parse a line of the config, return the word after <pattern>. */ 548266043Smariusstatic const char * 549266043Smariusfind_pattern(char *line, const char *pattern) 550190507Slulf{ 551190507Slulf char *ptr; 552190507Slulf 553190507Slulf ptr = strsep(&line, " "); 554190507Slulf while (ptr != NULL) { 555190507Slulf if (!strcmp(ptr, pattern)) { 556190507Slulf /* Return the next. */ 557190507Slulf ptr = strsep(&line, " "); 558190507Slulf return (ptr); 559190507Slulf } 560190507Slulf ptr = strsep(&line, " "); 561190507Slulf } 562190507Slulf return (NULL); 563190507Slulf} 564190507Slulf 565227489Seadler/* Find a free name for an object given a prefix. */ 566266043Smariusstatic char * 567190507Slulffind_name(const char *prefix, int type, int namelen) 568190507Slulf{ 569190507Slulf struct gctl_req *req; 570266043Smarius char comment[1], buf[GV_CFG_LEN - 1], *sname, *ptr; 571266043Smarius const char *errstr, *name; 572190507Slulf int i, n, begin, len, conflict; 573190507Slulf char line[1024]; 574190507Slulf 575190507Slulf comment[0] = '\0'; 576190507Slulf 577190507Slulf /* Find a name. Fetch out configuration first. */ 578190507Slulf req = gctl_get_handle(); 579190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 580190507Slulf gctl_ro_param(req, "verb", -1, "getconfig"); 581190507Slulf gctl_ro_param(req, "comment", -1, comment); 582190507Slulf gctl_rw_param(req, "config", sizeof(buf), buf); 583190507Slulf errstr = gctl_issue(req); 584190507Slulf if (errstr != NULL) { 585190507Slulf warnx("can't get configuration: %s", errstr); 586190507Slulf return (NULL); 587190507Slulf } 588190507Slulf gctl_free(req); 589190507Slulf 590190507Slulf begin = 0; 591190507Slulf len = strlen(buf); 592190507Slulf i = 0; 593190507Slulf sname = malloc(namelen + 1); 594190507Slulf 595190507Slulf /* XXX: Max object setting? */ 596190507Slulf for (n = 0; n < 10000; n++) { 597190507Slulf snprintf(sname, namelen, "%s%d", prefix, n); 598190507Slulf conflict = 0; 599190507Slulf begin = 0; 600190507Slulf /* Loop through the configuration line by line. */ 601190507Slulf for (i = 0; i < len; i++) { 602190507Slulf if (buf[i] == '\n' || buf[i] == '\0') { 603190507Slulf ptr = buf + begin; 604190507Slulf strlcpy(line, ptr, (i - begin) + 1); 605190507Slulf begin = i + 1; 606190507Slulf switch (type) { 607190507Slulf case GV_TYPE_DRIVE: 608190507Slulf name = find_pattern(line, "drive"); 609190507Slulf break; 610190507Slulf case GV_TYPE_VOL: 611190507Slulf name = find_pattern(line, "volume"); 612190507Slulf break; 613190507Slulf case GV_TYPE_PLEX: 614190507Slulf case GV_TYPE_SD: 615190507Slulf name = find_pattern(line, "name"); 616190507Slulf break; 617190507Slulf default: 618190507Slulf printf("Invalid type given\n"); 619190507Slulf continue; 620190507Slulf } 621190507Slulf if (name == NULL) 622190507Slulf continue; 623190507Slulf if (!strcmp(sname, name)) { 624190507Slulf conflict = 1; 625190507Slulf /* XXX: Could quit the loop earlier. */ 626190507Slulf } 627190507Slulf } 628190507Slulf } 629190507Slulf if (!conflict) 630190507Slulf return (sname); 631190507Slulf } 632190507Slulf free(sname); 633190507Slulf return (NULL); 634190507Slulf} 635190507Slulf 636266043Smariusstatic void 637204665Slulfcopy_device(struct gv_drive *d, const char *device) 638190882Slulf{ 639266043Smarius 640190882Slulf if (strncmp(device, "/dev/", 5) == 0) 641204665Slulf strlcpy(d->device, (device + 5), sizeof(d->device)); 642204665Slulf else 643204665Slulf strlcpy(d->device, device, sizeof(d->device)); 644190882Slulf} 645190882Slulf 646190507Slulf/* Detach a plex or subdisk from its parent. */ 647266043Smariusstatic void 648266043Smariusgvinum_detach(int argc, char * const *argv) 649190507Slulf{ 650190507Slulf const char *errstr; 651190507Slulf struct gctl_req *req; 652190507Slulf int flags, i; 653190507Slulf 654209051Suqs flags = 0; 655190507Slulf optreset = 1; 656190507Slulf optind = 1; 657190507Slulf while ((i = getopt(argc, argv, "f")) != -1) { 658266043Smarius switch (i) { 659190507Slulf case 'f': 660190507Slulf flags |= GV_FLAG_F; 661190507Slulf break; 662190507Slulf default: 663190507Slulf warn("invalid flag: %c", i); 664190507Slulf return; 665190507Slulf } 666190507Slulf } 667190507Slulf argc -= optind; 668190507Slulf argv += optind; 669190507Slulf if (argc != 1) { 670190507Slulf warnx("usage: detach [-f] <subdisk> | <plex>"); 671190507Slulf return; 672190507Slulf } 673190507Slulf 674190507Slulf req = gctl_get_handle(); 675190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 676190507Slulf gctl_ro_param(req, "verb", -1, "detach"); 677190507Slulf gctl_ro_param(req, "object", -1, argv[0]); 678190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 679190507Slulf 680190507Slulf errstr = gctl_issue(req); 681190507Slulf if (errstr != NULL) 682190507Slulf warnx("detach failed: %s", errstr); 683190507Slulf gctl_free(req); 684190507Slulf} 685190507Slulf 686266043Smariusstatic void 687130391Slegvinum_help(void) 688130391Sle{ 689266043Smarius 690130391Sle printf("COMMANDS\n" 691152616Sle "checkparity [-f] plex\n" 692152616Sle " Check the parity blocks of a RAID-5 plex.\n" 693190507Slulf "create [-f] description-file\n" 694152616Sle " Create as per description-file or open editor.\n" 695190507Slulf "attach plex volume [rename]\n" 696190507Slulf "attach subdisk plex [offset] [rename]\n" 697190507Slulf " Attach a plex to a volume, or a subdisk to a plex\n" 698190507Slulf "concat [-fv] [-n name] drives\n" 699190507Slulf " Create a concatenated volume from the specified drives.\n" 700190507Slulf "detach [-f] [plex | subdisk]\n" 701190507Slulf " Detach a plex or a subdisk from the volume or plex to\n" 702190507Slulf " which it is attached.\n" 703190884Slulf "grow plex drive\n" 704190884Slulf " Grow plex by creating a properly sized subdisk on drive\n" 705152616Sle "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" 706130391Sle " List information about specified objects.\n" 707152616Sle "ld [-r] [-v] [-V] [volume]\n" 708130391Sle " List information about drives.\n" 709152616Sle "ls [-r] [-v] [-V] [subdisk]\n" 710130391Sle " List information about subdisks.\n" 711152616Sle "lp [-r] [-v] [-V] [plex]\n" 712130391Sle " List information about plexes.\n" 713152616Sle "lv [-r] [-v] [-V] [volume]\n" 714130391Sle " List information about volumes.\n" 715190507Slulf "mirror [-fsv] [-n name] drives\n" 716190507Slulf " Create a mirrored volume from the specified drives.\n" 717130391Sle "move | mv -f drive object ...\n" 718130391Sle " Move the object(s) to the specified drive.\n" 719130391Sle "quit Exit the vinum program when running in interactive mode." 720130391Sle " Nor-\n" 721130391Sle " mally this would be done by entering the EOF character.\n" 722190507Slulf "raid5 [-fv] [-s stripesize] [-n name] drives\n" 723190507Slulf " Create a RAID-5 volume from the specified drives.\n" 724130391Sle "rename [-r] [drive | subdisk | plex | volume] newname\n" 725130391Sle " Change the name of the specified object.\n" 726152616Sle "rebuildparity plex [-f]\n" 727152616Sle " Rebuild the parity blocks of a RAID-5 plex.\n" 728266014Smarius "resetconfig [-f]\n" 729157052Sle " Reset the complete gvinum configuration\n" 730190507Slulf "rm [-r] [-f] volume | plex | subdisk | drive\n" 731130391Sle " Remove an object.\n" 732130391Sle "saveconfig\n" 733130391Sle " Save vinum configuration to disk after configuration" 734130391Sle " failures.\n" 735152616Sle "setstate [-f] state [volume | plex | subdisk | drive]\n" 736130391Sle " Set state without influencing other objects, for" 737130391Sle " diagnostic pur-\n" 738130391Sle " poses only.\n" 739152616Sle "start [-S size] volume | plex | subdisk\n" 740130391Sle " Allow the system to access the objects.\n" 741190507Slulf "stripe [-fv] [-n name] drives\n" 742190507Slulf " Create a striped volume from the specified drives.\n" 743130391Sle ); 744130391Sle} 745130391Sle 746266043Smariusstatic void 747266043Smariusgvinum_setstate(int argc, char * const *argv) 748138112Sle{ 749138112Sle struct gctl_req *req; 750138112Sle int flags, i; 751138112Sle const char *errstr; 752138112Sle 753138112Sle flags = 0; 754138112Sle 755138112Sle optreset = 1; 756138112Sle optind = 1; 757138112Sle 758138112Sle while ((i = getopt(argc, argv, "f")) != -1) { 759138112Sle switch (i) { 760138112Sle case 'f': 761138112Sle flags |= GV_FLAG_F; 762138112Sle break; 763138112Sle case '?': 764138112Sle default: 765138112Sle warn("invalid flag: %c", i); 766138112Sle return; 767138112Sle } 768138112Sle } 769138112Sle 770138112Sle argc -= optind; 771138112Sle argv += optind; 772138112Sle 773138112Sle if (argc != 2) { 774138112Sle warnx("usage: setstate [-f] <state> <obj>"); 775138112Sle return; 776138112Sle } 777138112Sle 778138112Sle /* 779138112Sle * XXX: This hack is needed to avoid tripping over (now) invalid 780138112Sle * 'classic' vinum states and will go away later. 781138112Sle */ 782138112Sle if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && 783138112Sle strcmp(argv[0], "stale")) { 784138112Sle warnx("invalid state '%s'", argv[0]); 785138112Sle return; 786138112Sle } 787138112Sle 788138112Sle req = gctl_get_handle(); 789138112Sle gctl_ro_param(req, "class", -1, "VINUM"); 790138112Sle gctl_ro_param(req, "verb", -1, "setstate"); 791138112Sle gctl_ro_param(req, "state", -1, argv[0]); 792138112Sle gctl_ro_param(req, "object", -1, argv[1]); 793138112Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 794138112Sle 795138112Sle errstr = gctl_issue(req); 796138112Sle if (errstr != NULL) 797138112Sle warnx("%s", errstr); 798138112Sle gctl_free(req); 799138112Sle} 800138112Sle 801266043Smariusstatic void 802266043Smariusgvinum_list(int argc, char * const *argv) 803130391Sle{ 804130391Sle struct gctl_req *req; 805130391Sle int flags, i, j; 806130391Sle const char *errstr; 807130391Sle char buf[20], *cmd, config[GV_CFG_LEN + 1]; 808130391Sle 809130391Sle flags = 0; 810130391Sle cmd = "list"; 811130391Sle 812130391Sle if (argc) { 813130391Sle optreset = 1; 814130391Sle optind = 1; 815130391Sle cmd = argv[0]; 816130391Sle while ((j = getopt(argc, argv, "rsvV")) != -1) { 817130391Sle switch (j) { 818130391Sle case 'r': 819130391Sle flags |= GV_FLAG_R; 820130391Sle break; 821130391Sle case 's': 822130391Sle flags |= GV_FLAG_S; 823130391Sle break; 824130391Sle case 'v': 825130391Sle flags |= GV_FLAG_V; 826130391Sle break; 827130391Sle case 'V': 828130391Sle flags |= GV_FLAG_V; 829130391Sle flags |= GV_FLAG_VV; 830130391Sle break; 831130391Sle case '?': 832130391Sle default: 833130391Sle return; 834130391Sle } 835130391Sle } 836130391Sle argc -= optind; 837130391Sle argv += optind; 838130391Sle 839130391Sle } 840130391Sle 841130391Sle req = gctl_get_handle(); 842130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 843130391Sle gctl_ro_param(req, "verb", -1, "list"); 844130391Sle gctl_ro_param(req, "cmd", -1, cmd); 845130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 846130391Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 847130391Sle gctl_rw_param(req, "config", sizeof(config), config); 848130391Sle if (argc) { 849130391Sle for (i = 0; i < argc; i++) { 850130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 851130391Sle gctl_ro_param(req, buf, -1, argv[i]); 852130391Sle } 853130391Sle } 854130391Sle errstr = gctl_issue(req); 855130391Sle if (errstr != NULL) { 856130391Sle warnx("can't get configuration: %s", errstr); 857130391Sle gctl_free(req); 858130391Sle return; 859130391Sle } 860130391Sle 861130391Sle printf("%s", config); 862130391Sle gctl_free(req); 863130391Sle} 864130391Sle 865190507Slulf/* Create a mirrored volume. */ 866266043Smariusstatic void 867266043Smariusgvinum_mirror(int argc, char * const *argv) 868190507Slulf{ 869190507Slulf 870190507Slulf if (argc < 2) { 871190507Slulf warnx("usage\tmirror [-fsv] [-n name] drives\n"); 872190507Slulf return; 873190507Slulf } 874190507Slulf create_volume(argc, argv, "mirror"); 875190507Slulf} 876190507Slulf 877152616Sle/* Note that move is currently of form '[-r] target object [...]' */ 878266043Smariusstatic void 879266043Smariusgvinum_move(int argc, char * const *argv) 880152616Sle{ 881152616Sle struct gctl_req *req; 882152616Sle const char *errstr; 883152616Sle char buf[20]; 884152616Sle int flags, i, j; 885152616Sle 886152616Sle flags = 0; 887152616Sle if (argc) { 888152616Sle optreset = 1; 889152616Sle optind = 1; 890152616Sle while ((j = getopt(argc, argv, "f")) != -1) { 891152616Sle switch (j) { 892152616Sle case 'f': 893152616Sle flags |= GV_FLAG_F; 894152616Sle break; 895152616Sle case '?': 896152616Sle default: 897152616Sle return; 898152616Sle } 899152616Sle } 900152616Sle argc -= optind; 901152616Sle argv += optind; 902152616Sle } 903152616Sle 904152616Sle switch (argc) { 905152616Sle case 0: 906152616Sle warnx("no destination or object(s) to move specified"); 907152616Sle return; 908152616Sle case 1: 909152616Sle warnx("no object(s) to move specified"); 910152616Sle return; 911152616Sle default: 912152616Sle break; 913152616Sle } 914152616Sle 915152616Sle req = gctl_get_handle(); 916152616Sle gctl_ro_param(req, "class", -1, "VINUM"); 917152616Sle gctl_ro_param(req, "verb", -1, "move"); 918152616Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 919152616Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 920152616Sle gctl_ro_param(req, "destination", -1, argv[0]); 921152616Sle for (i = 1; i < argc; i++) { 922152616Sle snprintf(buf, sizeof(buf), "argv%d", i); 923152616Sle gctl_ro_param(req, buf, -1, argv[i]); 924152616Sle } 925152631Sle errstr = gctl_issue(req); 926152616Sle if (errstr != NULL) 927152616Sle warnx("can't move object(s): %s", errstr); 928152616Sle gctl_free(req); 929152616Sle} 930152616Sle 931266043Smariusstatic void 932266043Smariusgvinum_printconfig(int argc, char * const *argv) 933130391Sle{ 934266043Smarius 935130391Sle printconfig(stdout, ""); 936130391Sle} 937130391Sle 938266043Smariusstatic void 939266043Smariusgvinum_parityop(int argc, char * const *argv, int rebuild) 940138110Sle{ 941138110Sle struct gctl_req *req; 942190507Slulf int flags, i; 943138110Sle const char *errstr; 944229915Seadler char *op; 945138110Sle 946138110Sle if (rebuild) { 947138110Sle op = "rebuildparity"; 948138110Sle } else { 949138110Sle op = "checkparity"; 950138110Sle } 951138110Sle 952138110Sle optreset = 1; 953138110Sle optind = 1; 954138110Sle flags = 0; 955138110Sle while ((i = getopt(argc, argv, "fv")) != -1) { 956138110Sle switch (i) { 957138110Sle case 'f': 958138110Sle flags |= GV_FLAG_F; 959138110Sle break; 960138110Sle case 'v': 961138110Sle flags |= GV_FLAG_V; 962138110Sle break; 963138110Sle default: 964138110Sle warnx("invalid flag '%c'", i); 965138110Sle return; 966138110Sle } 967138110Sle } 968138110Sle argc -= optind; 969138110Sle argv += optind; 970138110Sle 971138110Sle if (argc != 1) { 972138110Sle warn("usage: %s [-f] [-v] <plex>", op); 973138110Sle return; 974138110Sle } 975138110Sle 976190507Slulf req = gctl_get_handle(); 977190507Slulf gctl_ro_param(req, "class", -1, "VINUM"); 978190507Slulf gctl_ro_param(req, "verb", -1, op); 979190507Slulf gctl_ro_param(req, "rebuild", sizeof(int), &rebuild); 980190507Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 981190507Slulf gctl_ro_param(req, "plex", -1, argv[0]); 982138110Sle 983190507Slulf errstr = gctl_issue(req); 984190507Slulf if (errstr) 985190507Slulf warnx("%s\n", errstr); 986190507Slulf gctl_free(req); 987190507Slulf} 988138110Sle 989190507Slulf/* Create a RAID-5 volume. */ 990266043Smariusstatic void 991266043Smariusgvinum_raid5(int argc, char * const *argv) 992190507Slulf{ 993190507Slulf 994190507Slulf if (argc < 2) { 995190507Slulf warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n"); 996190507Slulf return; 997138110Sle } 998190507Slulf create_volume(argc, argv, "raid5"); 999138110Sle} 1000138110Sle 1001266043Smariusstatic void 1002266043Smariusgvinum_rename(int argc, char * const *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 default: 1019152616Sle return; 1020152616Sle } 1021152616Sle } 1022152616Sle argc -= optind; 1023152616Sle argv += optind; 1024152616Sle } 1025152616Sle 1026152616Sle switch (argc) { 1027152616Sle case 0: 1028152616Sle warnx("no object to rename specified"); 1029152616Sle return; 1030152616Sle case 1: 1031152616Sle warnx("no new name specified"); 1032152616Sle return; 1033152616Sle case 2: 1034152616Sle break; 1035152616Sle default: 1036152616Sle warnx("more than one new name specified"); 1037152616Sle return; 1038152616Sle } 1039152616Sle 1040152616Sle req = gctl_get_handle(); 1041152616Sle gctl_ro_param(req, "class", -1, "VINUM"); 1042152616Sle gctl_ro_param(req, "verb", -1, "rename"); 1043152616Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 1044152616Sle gctl_ro_param(req, "object", -1, argv[0]); 1045152616Sle gctl_ro_param(req, "newname", -1, argv[1]); 1046152631Sle errstr = gctl_issue(req); 1047152616Sle if (errstr != NULL) 1048152616Sle warnx("can't rename object: %s", errstr); 1049152616Sle gctl_free(req); 1050152616Sle} 1051152616Sle 1052266043Smariusstatic void 1053266043Smariusgvinum_rm(int argc, char * const *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 default: 1072130391Sle return; 1073130391Sle } 1074130391Sle } 1075130391Sle argc -= optind; 1076130391Sle argv += optind; 1077130391Sle 1078130391Sle req = gctl_get_handle(); 1079130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1080130391Sle gctl_ro_param(req, "verb", -1, "remove"); 1081130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 1082130391Sle gctl_ro_param(req, "flags", sizeof(int), &flags); 1083130391Sle if (argc) { 1084130391Sle for (i = 0; i < argc; i++) { 1085130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 1086130391Sle gctl_ro_param(req, buf, -1, argv[i]); 1087130391Sle } 1088130391Sle } 1089130391Sle errstr = gctl_issue(req); 1090130391Sle if (errstr != NULL) { 1091130391Sle warnx("can't remove: %s", errstr); 1092130391Sle gctl_free(req); 1093130391Sle return; 1094130391Sle } 1095130391Sle gctl_free(req); 1096130391Sle} 1097130391Sle 1098266043Smariusstatic void 1099266043Smariusgvinum_resetconfig(int argc, char * const *argv) 1100157052Sle{ 1101157052Sle struct gctl_req *req; 1102157052Sle const char *errstr; 1103157052Sle char reply[32]; 1104266014Smarius int flags, i; 1105157052Sle 1106266014Smarius flags = 0; 1107266014Smarius while ((i = getopt(argc, argv, "f")) != -1) { 1108266014Smarius switch (i) { 1109266014Smarius case 'f': 1110266014Smarius flags |= GV_FLAG_F; 1111266014Smarius break; 1112266014Smarius default: 1113266014Smarius warn("invalid flag: %c", i); 1114266014Smarius return; 1115266014Smarius } 1116157052Sle } 1117266014Smarius if ((flags & GV_FLAG_F) == 0) { 1118266014Smarius if (!isatty(STDIN_FILENO)) { 1119266014Smarius warn("Please enter this command from a tty device\n"); 1120266014Smarius return; 1121266014Smarius } 1122266014Smarius printf(" WARNING! This command will completely wipe out" 1123266014Smarius " your gvinum configuration.\n" 1124266014Smarius " All data will be lost. If you really want to do this," 1125266014Smarius " enter the text\n\n" 1126266014Smarius " NO FUTURE\n" 1127266014Smarius " Enter text -> "); 1128266014Smarius fgets(reply, sizeof(reply), stdin); 1129266014Smarius if (strcmp(reply, "NO FUTURE\n")) { 1130266014Smarius printf("\n No change\n"); 1131266014Smarius return; 1132266014Smarius } 1133157052Sle } 1134157052Sle req = gctl_get_handle(); 1135157052Sle gctl_ro_param(req, "class", -1, "VINUM"); 1136157052Sle gctl_ro_param(req, "verb", -1, "resetconfig"); 1137157052Sle errstr = gctl_issue(req); 1138157052Sle if (errstr != NULL) { 1139157052Sle warnx("can't reset config: %s", errstr); 1140157052Sle gctl_free(req); 1141157052Sle return; 1142157052Sle } 1143157052Sle gctl_free(req); 1144157052Sle printf("gvinum configuration obliterated\n"); 1145157052Sle} 1146157052Sle 1147266043Smariusstatic void 1148130391Slegvinum_saveconfig(void) 1149130391Sle{ 1150130391Sle struct gctl_req *req; 1151130391Sle const char *errstr; 1152130391Sle 1153130391Sle req = gctl_get_handle(); 1154130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1155130391Sle gctl_ro_param(req, "verb", -1, "saveconfig"); 1156130391Sle errstr = gctl_issue(req); 1157130391Sle if (errstr != NULL) 1158130391Sle warnx("can't save configuration: %s", errstr); 1159130391Sle gctl_free(req); 1160130391Sle} 1161130391Sle 1162266043Smariusstatic void 1163266043Smariusgvinum_start(int argc, char * const *argv) 1164130391Sle{ 1165130391Sle struct gctl_req *req; 1166130391Sle int i, initsize, j; 1167130391Sle const char *errstr; 1168130391Sle char buf[20]; 1169130391Sle 1170130391Sle /* 'start' with no arguments is a no-op. */ 1171130391Sle if (argc == 1) 1172130391Sle return; 1173130391Sle 1174130391Sle initsize = 0; 1175130391Sle 1176130391Sle optreset = 1; 1177130391Sle optind = 1; 1178130391Sle while ((j = getopt(argc, argv, "S")) != -1) { 1179130391Sle switch (j) { 1180130391Sle case 'S': 1181130391Sle initsize = atoi(optarg); 1182130391Sle break; 1183130391Sle default: 1184130391Sle return; 1185130391Sle } 1186130391Sle } 1187130391Sle argc -= optind; 1188130391Sle argv += optind; 1189130391Sle 1190130391Sle if (!initsize) 1191130391Sle initsize = 512; 1192130391Sle 1193130391Sle req = gctl_get_handle(); 1194130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1195130391Sle gctl_ro_param(req, "verb", -1, "start"); 1196130391Sle gctl_ro_param(req, "argc", sizeof(int), &argc); 1197130391Sle gctl_ro_param(req, "initsize", sizeof(int), &initsize); 1198130391Sle if (argc) { 1199130391Sle for (i = 0; i < argc; i++) { 1200130391Sle snprintf(buf, sizeof(buf), "argv%d", i); 1201130391Sle gctl_ro_param(req, buf, -1, argv[i]); 1202130391Sle } 1203130391Sle } 1204130391Sle errstr = gctl_issue(req); 1205130391Sle if (errstr != NULL) { 1206130391Sle warnx("can't start: %s", errstr); 1207130391Sle gctl_free(req); 1208130391Sle return; 1209130391Sle } 1210130391Sle 1211130391Sle gctl_free(req); 1212130391Sle} 1213130391Sle 1214266043Smariusstatic void 1215266043Smariusgvinum_stop(int argc, char * const *argv) 1216130391Sle{ 1217190507Slulf int err, fileid; 1218130391Sle 1219265536Smarius fileid = kldfind(GVINUMKLD); 1220130391Sle if (fileid == -1) { 1221265536Smarius if (modfind(GVINUMMOD) < 0) 1222265536Smarius warn("cannot find " GVINUMKLD); 1223130391Sle return; 1224130391Sle } 1225190507Slulf 1226190507Slulf /* 1227190507Slulf * This little hack prevents that we end up in an infinite loop in 1228190507Slulf * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM 1229190507Slulf * event thread will be free for the g_wither_geom() call from 1230190507Slulf * gv_unload(). It's silly, but it works. 1231190507Slulf */ 1232265536Smarius printf("unloading " GVINUMKLD " kernel module... "); 1233190507Slulf fflush(stdout); 1234190507Slulf if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) { 1235190507Slulf sleep(1); 1236190507Slulf err = kldunload(fileid); 1237190507Slulf } 1238190507Slulf if (err != 0) { 1239190507Slulf printf(" failed!\n"); 1240265536Smarius warn("cannot unload " GVINUMKLD); 1241130391Sle return; 1242130391Sle } 1243130391Sle 1244190507Slulf printf("done\n"); 1245130391Sle exit(0); 1246130391Sle} 1247130391Sle 1248190507Slulf/* Create a striped volume. */ 1249266043Smariusstatic void 1250266043Smariusgvinum_stripe(int argc, char * const *argv) 1251190507Slulf{ 1252190507Slulf 1253190507Slulf if (argc < 2) { 1254190507Slulf warnx("usage:\tstripe [-fv] [-n name] drives\n"); 1255190507Slulf return; 1256190507Slulf } 1257190507Slulf create_volume(argc, argv, "stripe"); 1258190507Slulf} 1259190507Slulf 1260190884Slulf/* Grow a subdisk by adding disk backed by provider. */ 1261266043Smariusstatic void 1262266043Smariusgvinum_grow(int argc, char * const *argv) 1263190884Slulf{ 1264190884Slulf struct gctl_req *req; 1265190884Slulf char *drive, *sdname; 1266190884Slulf char sdprefix[GV_MAXSDNAME]; 1267190884Slulf struct gv_drive *d; 1268190884Slulf struct gv_sd *s; 1269190884Slulf const char *errstr; 1270190884Slulf int drives, volumes, plexes, subdisks, flags; 1271190884Slulf 1272266014Smarius flags = 0; 1273190884Slulf drives = volumes = plexes = subdisks = 0; 1274190884Slulf if (argc < 3) { 1275190884Slulf warnx("usage:\tgrow plex drive\n"); 1276190884Slulf return; 1277190884Slulf } 1278190884Slulf 1279190884Slulf s = gv_alloc_sd(); 1280190884Slulf if (s == NULL) { 1281190884Slulf warn("unable to create subdisk"); 1282190884Slulf return; 1283190884Slulf } 1284190884Slulf d = gv_alloc_drive(); 1285190884Slulf if (d == NULL) { 1286190884Slulf warn("unable to create drive"); 1287190884Slulf free(s); 1288190884Slulf return; 1289190884Slulf } 1290190884Slulf /* Lookup device and set an appropriate drive name. */ 1291204665Slulf drive = find_drive(); 1292190884Slulf if (drive == NULL) { 1293190884Slulf warn("unable to find an appropriate drive name"); 1294190884Slulf free(s); 1295190884Slulf free(d); 1296190884Slulf return; 1297190884Slulf } 1298190884Slulf strlcpy(d->name, drive, sizeof(d->name)); 1299204665Slulf copy_device(d, argv[2]); 1300204665Slulf 1301190884Slulf drives = 1; 1302190884Slulf 1303190884Slulf /* We try to use the plex name as basis for the subdisk name. */ 1304190884Slulf snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]); 1305190884Slulf sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME); 1306190884Slulf if (sdname == NULL) { 1307190884Slulf warn("unable to find an appropriate subdisk name"); 1308190884Slulf free(s); 1309190884Slulf free(d); 1310190884Slulf free(drive); 1311190884Slulf return; 1312190884Slulf } 1313190884Slulf strlcpy(s->name, sdname, sizeof(s->name)); 1314190884Slulf free(sdname); 1315190884Slulf strlcpy(s->plex, argv[1], sizeof(s->plex)); 1316190884Slulf strlcpy(s->drive, d->name, sizeof(s->drive)); 1317190884Slulf subdisks = 1; 1318190884Slulf 1319190884Slulf req = gctl_get_handle(); 1320190884Slulf gctl_ro_param(req, "class", -1, "VINUM"); 1321190884Slulf gctl_ro_param(req, "verb", -1, "create"); 1322190884Slulf gctl_ro_param(req, "flags", sizeof(int), &flags); 1323190884Slulf gctl_ro_param(req, "volumes", sizeof(int), &volumes); 1324190884Slulf gctl_ro_param(req, "plexes", sizeof(int), &plexes); 1325190884Slulf gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); 1326190884Slulf gctl_ro_param(req, "drives", sizeof(int), &drives); 1327190884Slulf gctl_ro_param(req, "drive0", sizeof(*d), d); 1328190884Slulf gctl_ro_param(req, "sd0", sizeof(*s), s); 1329190884Slulf errstr = gctl_issue(req); 1330190884Slulf free(drive); 1331190884Slulf if (errstr != NULL) { 1332190884Slulf warnx("unable to grow plex: %s", errstr); 1333190884Slulf free(s); 1334190884Slulf free(d); 1335190884Slulf return; 1336190884Slulf } 1337190884Slulf gctl_free(req); 1338190884Slulf} 1339190884Slulf 1340266043Smariusstatic void 1341266043Smariusparseline(int argc, char * const *argv) 1342130391Sle{ 1343266043Smarius 1344130391Sle if (argc <= 0) 1345130391Sle return; 1346130391Sle 1347150044Sle if (!strcmp(argv[0], "create")) 1348130391Sle gvinum_create(argc, argv); 1349130391Sle else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) 1350130391Sle exit(0); 1351190507Slulf else if (!strcmp(argv[0], "attach")) 1352190507Slulf gvinum_attach(argc, argv); 1353190507Slulf else if (!strcmp(argv[0], "detach")) 1354190507Slulf gvinum_detach(argc, argv); 1355190507Slulf else if (!strcmp(argv[0], "concat")) 1356190507Slulf gvinum_concat(argc, argv); 1357190884Slulf else if (!strcmp(argv[0], "grow")) 1358190884Slulf gvinum_grow(argc, argv); 1359130391Sle else if (!strcmp(argv[0], "help")) 1360130391Sle gvinum_help(); 1361130391Sle else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) 1362130391Sle gvinum_list(argc, argv); 1363130391Sle else if (!strcmp(argv[0], "ld")) 1364130391Sle gvinum_list(argc, argv); 1365130391Sle else if (!strcmp(argv[0], "lp")) 1366130391Sle gvinum_list(argc, argv); 1367130391Sle else if (!strcmp(argv[0], "ls")) 1368130391Sle gvinum_list(argc, argv); 1369130391Sle else if (!strcmp(argv[0], "lv")) 1370130391Sle gvinum_list(argc, argv); 1371190507Slulf else if (!strcmp(argv[0], "mirror")) 1372190507Slulf gvinum_mirror(argc, argv); 1373152616Sle else if (!strcmp(argv[0], "move")) 1374152616Sle gvinum_move(argc, argv); 1375152616Sle else if (!strcmp(argv[0], "mv")) 1376152616Sle gvinum_move(argc, argv); 1377130391Sle else if (!strcmp(argv[0], "printconfig")) 1378130391Sle gvinum_printconfig(argc, argv); 1379190507Slulf else if (!strcmp(argv[0], "raid5")) 1380190507Slulf gvinum_raid5(argc, argv); 1381152616Sle else if (!strcmp(argv[0], "rename")) 1382152616Sle gvinum_rename(argc, argv); 1383157052Sle else if (!strcmp(argv[0], "resetconfig")) 1384266014Smarius gvinum_resetconfig(argc, argv); 1385130391Sle else if (!strcmp(argv[0], "rm")) 1386130391Sle gvinum_rm(argc, argv); 1387130391Sle else if (!strcmp(argv[0], "saveconfig")) 1388130391Sle gvinum_saveconfig(); 1389138112Sle else if (!strcmp(argv[0], "setstate")) 1390138112Sle gvinum_setstate(argc, argv); 1391130391Sle else if (!strcmp(argv[0], "start")) 1392130391Sle gvinum_start(argc, argv); 1393130391Sle else if (!strcmp(argv[0], "stop")) 1394130391Sle gvinum_stop(argc, argv); 1395190507Slulf else if (!strcmp(argv[0], "stripe")) 1396190507Slulf gvinum_stripe(argc, argv); 1397138110Sle else if (!strcmp(argv[0], "checkparity")) 1398138110Sle gvinum_parityop(argc, argv, 0); 1399138110Sle else if (!strcmp(argv[0], "rebuildparity")) 1400138110Sle gvinum_parityop(argc, argv, 1); 1401130391Sle else 1402130391Sle printf("unknown command '%s'\n", argv[0]); 1403130391Sle} 1404130391Sle 1405130391Sle/* 1406130391Sle * The guts of printconfig. This is called from gvinum_printconfig and from 1407130391Sle * gvinum_create when called without an argument, in order to give the user 1408130391Sle * something to edit. 1409130391Sle */ 1410266043Smariusstatic void 1411266043Smariusprintconfig(FILE *of, const char *comment) 1412130391Sle{ 1413130391Sle struct gctl_req *req; 1414130391Sle struct utsname uname_s; 1415130391Sle const char *errstr; 1416130391Sle time_t now; 1417130391Sle char buf[GV_CFG_LEN + 1]; 1418130391Sle 1419130391Sle uname(&uname_s); 1420130391Sle time(&now); 1421130391Sle 1422130391Sle req = gctl_get_handle(); 1423130391Sle gctl_ro_param(req, "class", -1, "VINUM"); 1424130391Sle gctl_ro_param(req, "verb", -1, "getconfig"); 1425130391Sle gctl_ro_param(req, "comment", -1, comment); 1426130391Sle gctl_rw_param(req, "config", sizeof(buf), buf); 1427130391Sle errstr = gctl_issue(req); 1428130391Sle if (errstr != NULL) { 1429130391Sle warnx("can't get configuration: %s", errstr); 1430130391Sle return; 1431130391Sle } 1432130391Sle gctl_free(req); 1433130391Sle 1434130391Sle fprintf(of, "# Vinum configuration of %s, saved at %s", 1435130391Sle uname_s.nodename, 1436130391Sle ctime(&now)); 1437130391Sle 1438130391Sle if (*comment != '\0') 1439130391Sle fprintf(of, "# Current configuration:\n"); 1440130391Sle 1441215704Sbrucec fprintf(of, "%s", buf); 1442130391Sle} 1443