diff options
Diffstat (limited to 'sbin/gvinum/gvinum.c')
-rw-r--r-- | sbin/gvinum/gvinum.c | 1450 |
1 files changed, 0 insertions, 1450 deletions
diff --git a/sbin/gvinum/gvinum.c b/sbin/gvinum/gvinum.c deleted file mode 100644 index c391f5d61c65..000000000000 --- a/sbin/gvinum/gvinum.c +++ /dev/null @@ -1,1450 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2004 Lukas Ertl - * Copyright (c) 2005 Chris Jones - * Copyright (c) 2007 Ulf Lilleengen - * All rights reserved. - * - * Portions of this software were developed for the FreeBSD Project - * by Chris Jones thanks to the support of Google's Summer of Code - * program and mentoring by Lukas Ertl. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/param.h> -#include <sys/linker.h> -#include <sys/lock.h> -#include <sys/module.h> -#include <sys/mutex.h> -#include <sys/queue.h> -#include <sys/utsname.h> - -#include <geom/vinum/geom_vinum_var.h> -#include <geom/vinum/geom_vinum_share.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <libgeom.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <paths.h> -#include <readline/readline.h> -#include <readline/history.h> -#include <unistd.h> - -#include "gvinum.h" - -static void gvinum_attach(int, char * const *); -static void gvinum_concat(int, char * const *); -static void gvinum_create(int, char * const *); -static void gvinum_detach(int, char * const *); -static void gvinum_grow(int, char * const *); -static void gvinum_help(void); -static void gvinum_list(int, char * const *); -static void gvinum_move(int, char * const *); -static void gvinum_mirror(int, char * const *); -static void gvinum_parityop(int, char * const * , int); -static void gvinum_printconfig(int, char * const *); -static void gvinum_raid5(int, char * const *); -static void gvinum_rename(int, char * const *); -static void gvinum_resetconfig(int, char * const *); -static void gvinum_rm(int, char * const *); -static void gvinum_saveconfig(void); -static void gvinum_setstate(int, char * const *); -static void gvinum_start(int, char * const *); -static void gvinum_stop(int, char * const *); -static void gvinum_stripe(int, char * const *); -static void parseline(int, char * const *); -static void printconfig(FILE *, const char *); - -static char *create_drive(const char *); -static void create_volume(int, char * const * , const char *); -static char *find_name(const char *, int, int); -static const char *find_pattern(char *, const char *); -static void copy_device(struct gv_drive *, const char *); -#define find_drive() \ - find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME) - -int -main(int argc, char **argv) -{ - int tokens; - char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS]; - - /* Load the module if necessary. */ - if (modfind(GVINUMMOD) < 0) { - if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0) - err(1, GVINUMKLD ": Kernel module not available"); - } - - /* Arguments given on the command line. */ - if (argc > 1) { - argc--; - argv++; - parseline(argc, argv); - - /* Interactive mode. */ - } else { - for (;;) { - inputline = readline("gvinum -> "); - if (inputline == NULL) { - if (ferror(stdin)) { - err(1, "can't read input"); - } else { - printf("\n"); - exit(0); - } - } else if (*inputline) { - add_history(inputline); - strcpy(buffer, inputline); - free(inputline); - tokens = gv_tokenize(buffer, token, GV_MAXARGS); - if (tokens) - parseline(tokens, token); - } - } - } - exit(0); -} - -/* Attach a plex to a volume or a subdisk to a plex. */ -static void -gvinum_attach(int argc, char * const *argv) -{ - struct gctl_req *req; - const char *errstr; - int rename; - off_t offset; - - rename = 0; - offset = -1; - if (argc < 3) { - warnx("usage:\tattach <subdisk> <plex> [rename] " - "[<plexoffset>]\n" - "\tattach <plex> <volume> [rename]"); - return; - } - if (argc > 3) { - if (!strcmp(argv[3], "rename")) { - rename = 1; - if (argc == 5) - offset = strtol(argv[4], NULL, 0); - } else - offset = strtol(argv[3], NULL, 0); - } - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "attach"); - gctl_ro_param(req, "child", -1, argv[1]); - gctl_ro_param(req, "parent", -1, argv[2]); - gctl_ro_param(req, "offset", sizeof(off_t), &offset); - gctl_ro_param(req, "rename", sizeof(int), &rename); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("attach failed: %s", errstr); - gctl_free(req); -} - -static void -gvinum_create(int argc, char * const *argv) -{ - struct gctl_req *req; - struct gv_drive *d; - struct gv_plex *p; - struct gv_sd *s; - struct gv_volume *v; - FILE *tmp; - int drives, errors, fd, flags, i, line, plexes, plex_in_volume; - int sd_in_plex, status, subdisks, tokens, undeffd, volumes; - const char *errstr; - char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *sdname; - const char *ed; - char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS]; - char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME]; - - tmp = NULL; - flags = 0; - for (i = 1; i < argc; i++) { - /* Force flag used to ignore already created drives. */ - if (!strcmp(argv[i], "-f")) { - flags |= GV_FLAG_F; - /* Else it must be a file. */ - } else { - if ((tmp = fopen(argv[i], "r")) == NULL) { - warn("can't open '%s' for reading", argv[i]); - return; - } - } - } - - /* We didn't get a file. */ - if (tmp == NULL) { - snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX"); - - if ((fd = mkstemp(tmpfile)) == -1) { - warn("temporary file not accessible"); - return; - } - if ((tmp = fdopen(fd, "w")) == NULL) { - warn("can't open '%s' for writing", tmpfile); - return; - } - printconfig(tmp, "# "); - fclose(tmp); - - ed = getenv("EDITOR"); - if (ed == NULL) - ed = _PATH_VI; - - snprintf(commandline, sizeof(commandline), "%s %s", ed, - tmpfile); - status = system(commandline); - if (status != 0) { - warn("couldn't exec %s; status: %d", ed, status); - return; - } - - if ((tmp = fopen(tmpfile, "r")) == NULL) { - warn("can't open '%s' for reading", tmpfile); - return; - } - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "create"); - gctl_ro_param(req, "flags", sizeof(int), &flags); - - drives = volumes = plexes = subdisks = 0; - plex_in_volume = sd_in_plex = undeffd = 0; - plex[0] = '\0'; - errors = 0; - line = 1; - while ((fgets(buf, BUFSIZ, tmp)) != NULL) { - - /* Skip empty lines and comments. */ - if (*buf == '\0' || *buf == '#') { - line++; - continue; - } - - /* Kill off the newline. */ - buf[strlen(buf) - 1] = '\0'; - - /* - * Copy the original input line in case we need it for error - * output. - */ - strlcpy(original, buf, sizeof(original)); - - tokens = gv_tokenize(buf, token, GV_MAXARGS); - if (tokens <= 0) { - line++; - continue; - } - - /* Volume definition. */ - if (!strcmp(token[0], "volume")) { - v = gv_new_volume(tokens, token); - if (v == NULL) { - warnx("line %d: invalid volume definition", - line); - warnx("line %d: '%s'", line, original); - errors++; - line++; - continue; - } - - /* Reset plex count for this volume. */ - plex_in_volume = 0; - - /* - * Set default volume name for following plex - * definitions. - */ - strlcpy(volume, v->name, sizeof(volume)); - - snprintf(buf1, sizeof(buf1), "volume%d", volumes); - gctl_ro_param(req, buf1, sizeof(*v), v); - volumes++; - - /* Plex definition. */ - } else if (!strcmp(token[0], "plex")) { - p = gv_new_plex(tokens, token); - if (p == NULL) { - warnx("line %d: invalid plex definition", line); - warnx("line %d: '%s'", line, original); - errors++; - line++; - continue; - } - - /* Reset subdisk count for this plex. */ - sd_in_plex = 0; - - /* Default name. */ - if (strlen(p->name) == 0) { - snprintf(p->name, sizeof(p->name), "%s.p%d", - volume, plex_in_volume++); - } - - /* Default volume. */ - if (strlen(p->volume) == 0) { - snprintf(p->volume, sizeof(p->volume), "%s", - volume); - } - - /* - * Set default plex name for following subdisk - * definitions. - */ - strlcpy(plex, p->name, sizeof(plex)); - - snprintf(buf1, sizeof(buf1), "plex%d", plexes); - gctl_ro_param(req, buf1, sizeof(*p), p); - plexes++; - - /* Subdisk definition. */ - } else if (!strcmp(token[0], "sd")) { - s = gv_new_sd(tokens, token); - if (s == NULL) { - warnx("line %d: invalid subdisk " - "definition:", line); - warnx("line %d: '%s'", line, original); - errors++; - line++; - continue; - } - - /* Default name. */ - if (strlen(s->name) == 0) { - if (strlen(plex) == 0) { - sdname = find_name("gvinumsubdisk.p", - GV_TYPE_SD, GV_MAXSDNAME); - snprintf(s->name, sizeof(s->name), - "%s.s%d", sdname, undeffd++); - free(sdname); - } else { - snprintf(s->name, sizeof(s->name), - "%s.s%d",plex, sd_in_plex++); - } - } - - /* Default plex. */ - if (strlen(s->plex) == 0) - snprintf(s->plex, sizeof(s->plex), "%s", plex); - - snprintf(buf1, sizeof(buf1), "sd%d", subdisks); - gctl_ro_param(req, buf1, sizeof(*s), s); - subdisks++; - - /* Subdisk definition. */ - } else if (!strcmp(token[0], "drive")) { - d = gv_new_drive(tokens, token); - if (d == NULL) { - warnx("line %d: invalid drive definition:", - line); - warnx("line %d: '%s'", line, original); - errors++; - line++; - continue; - } - - snprintf(buf1, sizeof(buf1), "drive%d", drives); - gctl_ro_param(req, buf1, sizeof(*d), d); - drives++; - - /* Everything else is bogus. */ - } else { - warnx("line %d: invalid definition:", line); - warnx("line %d: '%s'", line, original); - errors++; - } - line++; - } - - fclose(tmp); - unlink(tmpfile); - - if (!errors && (volumes || plexes || subdisks || drives)) { - gctl_ro_param(req, "volumes", sizeof(int), &volumes); - gctl_ro_param(req, "plexes", sizeof(int), &plexes); - gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); - gctl_ro_param(req, "drives", sizeof(int), &drives); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("create failed: %s", errstr); - } - gctl_free(req); -} - -/* Create a concatenated volume. */ -static void -gvinum_concat(int argc, char * const *argv) -{ - - if (argc < 2) { - warnx("usage:\tconcat [-fv] [-n name] drives\n"); - return; - } - create_volume(argc, argv, "concat"); -} - -/* Create a drive quick and dirty. */ -static char * -create_drive(const char *device) -{ - struct gv_drive *d; - struct gctl_req *req; - const char *errstr; - char *drivename, *dname; - int drives, i, flags, volumes, subdisks, plexes; - int found = 0; - - flags = plexes = subdisks = volumes = 0; - drives = 1; - dname = NULL; - - drivename = find_drive(); - if (drivename == NULL) - return (NULL); - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "create"); - d = gv_alloc_drive(); - if (d == NULL) - err(1, "unable to allocate for gv_drive object"); - - strlcpy(d->name, drivename, sizeof(d->name)); - copy_device(d, device); - gctl_ro_param(req, "drive0", sizeof(*d), d); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "drives", sizeof(int), &drives); - gctl_ro_param(req, "volumes", sizeof(int), &volumes); - gctl_ro_param(req, "plexes", sizeof(int), &plexes); - gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("error creating drive: %s", errstr); - drivename = NULL; - } else { - /* XXX: This is needed because we have to make sure the drives - * are created before we return. */ - /* Loop until it's in the config. */ - for (i = 0; i < 100000; i++) { - dname = find_name("gvinumdrive", GV_TYPE_DRIVE, - GV_MAXDRIVENAME); - /* If we got a different name, quit. */ - if (dname == NULL) - continue; - if (strcmp(dname, drivename)) - found = 1; - free(dname); - dname = NULL; - if (found) - break; - usleep(100000); /* Sleep for 0.1s */ - } - if (found == 0) { - warnx("error creating drive"); - drivename = NULL; - } - } - gctl_free(req); - return (drivename); -} - -/* - * General routine for creating a volume. Mainly for use by concat, mirror, - * raid5 and stripe commands. - */ -static void -create_volume(int argc, char * const *argv, const char *verb) -{ - struct gctl_req *req; - const char *errstr; - char buf[BUFSIZ], *drivename, *volname; - int drives, flags, i; - off_t stripesize; - - flags = 0; - drives = 0; - volname = NULL; - stripesize = 262144; - - /* XXX: Should we check for argument length? */ - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-f")) { - flags |= GV_FLAG_F; - } else if (!strcmp(argv[i], "-n")) { - volname = argv[++i]; - } else if (!strcmp(argv[i], "-v")) { - flags |= GV_FLAG_V; - } else if (!strcmp(argv[i], "-s")) { - flags |= GV_FLAG_S; - if (!strcmp(verb, "raid5")) - stripesize = gv_sizespec(argv[++i]); - } else { - /* Assume it's a drive. */ - snprintf(buf, sizeof(buf), "drive%d", drives++); - - /* First we create the drive. */ - drivename = create_drive(argv[i]); - if (drivename == NULL) - goto bad; - /* Then we add it to the request. */ - gctl_ro_param(req, buf, -1, drivename); - } - } - - gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize); - - /* Find a free volume name. */ - if (volname == NULL) - volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME); - - /* Then we send a request to actually create the volumes. */ - gctl_ro_param(req, "verb", -1, verb); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "drives", sizeof(int), &drives); - gctl_ro_param(req, "name", -1, volname); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("creating %s volume failed: %s", verb, errstr); -bad: - gctl_free(req); -} - -/* Parse a line of the config, return the word after <pattern>. */ -static const char * -find_pattern(char *line, const char *pattern) -{ - char *ptr; - - ptr = strsep(&line, " "); - while (ptr != NULL) { - if (!strcmp(ptr, pattern)) { - /* Return the next. */ - ptr = strsep(&line, " "); - return (ptr); - } - ptr = strsep(&line, " "); - } - return (NULL); -} - -/* Find a free name for an object given a prefix. */ -static char * -find_name(const char *prefix, int type, int namelen) -{ - struct gctl_req *req; - char comment[1], buf[GV_CFG_LEN - 1], *sname, *ptr; - const char *errstr, *name; - int i, n, begin, len, conflict; - char line[1024]; - - comment[0] = '\0'; - buf[0] = '\0'; - - /* Find a name. Fetch out configuration first. */ - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "getconfig"); - gctl_ro_param(req, "comment", -1, comment); - gctl_add_param(req, "config", sizeof(buf), buf, - GCTL_PARAM_WR | GCTL_PARAM_ASCII); - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't get configuration: %s", errstr); - return (NULL); - } - gctl_free(req); - - begin = 0; - len = strlen(buf); - i = 0; - sname = malloc(namelen + 1); - - /* XXX: Max object setting? */ - for (n = 0; n < 10000; n++) { - snprintf(sname, namelen, "%s%d", prefix, n); - conflict = 0; - begin = 0; - /* Loop through the configuration line by line. */ - for (i = 0; i < len; i++) { - if (buf[i] == '\n' || buf[i] == '\0') { - ptr = buf + begin; - strlcpy(line, ptr, (i - begin) + 1); - begin = i + 1; - switch (type) { - case GV_TYPE_DRIVE: - name = find_pattern(line, "drive"); - break; - case GV_TYPE_VOL: - name = find_pattern(line, "volume"); - break; - case GV_TYPE_PLEX: - case GV_TYPE_SD: - name = find_pattern(line, "name"); - break; - default: - printf("Invalid type given\n"); - continue; - } - if (name == NULL) - continue; - if (!strcmp(sname, name)) { - conflict = 1; - /* XXX: Could quit the loop earlier. */ - } - } - } - if (!conflict) - return (sname); - } - free(sname); - return (NULL); -} - -static void -copy_device(struct gv_drive *d, const char *device) -{ - - if (strncmp(device, "/dev/", 5) == 0) - strlcpy(d->device, (device + 5), sizeof(d->device)); - else - strlcpy(d->device, device, sizeof(d->device)); -} - -/* Detach a plex or subdisk from its parent. */ -static void -gvinum_detach(int argc, char * const *argv) -{ - const char *errstr; - struct gctl_req *req; - int flags, i; - - flags = 0; - optreset = 1; - optind = 1; - while ((i = getopt(argc, argv, "f")) != -1) { - switch (i) { - case 'f': - flags |= GV_FLAG_F; - break; - default: - warn("invalid flag: %c", i); - return; - } - } - argc -= optind; - argv += optind; - if (argc != 1) { - warnx("usage: detach [-f] <subdisk> | <plex>"); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "detach"); - gctl_ro_param(req, "object", -1, argv[0]); - gctl_ro_param(req, "flags", sizeof(int), &flags); - - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("detach failed: %s", errstr); - gctl_free(req); -} - -static void -gvinum_help(void) -{ - - printf("COMMANDS\n" - "checkparity [-f] plex\n" - " Check the parity blocks of a RAID-5 plex.\n" - "create [-f] description-file\n" - " Create as per description-file or open editor.\n" - "attach plex volume [rename]\n" - "attach subdisk plex [offset] [rename]\n" - " Attach a plex to a volume, or a subdisk to a plex\n" - "concat [-fv] [-n name] drives\n" - " Create a concatenated volume from the specified drives.\n" - "detach [-f] [plex | subdisk]\n" - " Detach a plex or a subdisk from the volume or plex to\n" - " which it is attached.\n" - "grow plex drive\n" - " Grow plex by creating a properly sized subdisk on drive\n" - "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" - " List information about specified objects.\n" - "ld [-r] [-v] [-V] [volume]\n" - " List information about drives.\n" - "ls [-r] [-v] [-V] [subdisk]\n" - " List information about subdisks.\n" - "lp [-r] [-v] [-V] [plex]\n" - " List information about plexes.\n" - "lv [-r] [-v] [-V] [volume]\n" - " List information about volumes.\n" - "mirror [-fsv] [-n name] drives\n" - " Create a mirrored volume from the specified drives.\n" - "move | mv -f drive object ...\n" - " Move the object(s) to the specified drive.\n" - "quit Exit the vinum program when running in interactive mode." - " Nor-\n" - " mally this would be done by entering the EOF character.\n" - "raid5 [-fv] [-s stripesize] [-n name] drives\n" - " Create a RAID-5 volume from the specified drives.\n" - "rename [-r] [drive | subdisk | plex | volume] newname\n" - " Change the name of the specified object.\n" - "rebuildparity plex [-f]\n" - " Rebuild the parity blocks of a RAID-5 plex.\n" - "resetconfig [-f]\n" - " Reset the complete gvinum configuration\n" - "rm [-r] [-f] volume | plex | subdisk | drive\n" - " Remove an object.\n" - "saveconfig\n" - " Save vinum configuration to disk after configuration" - " failures.\n" - "setstate [-f] state [volume | plex | subdisk | drive]\n" - " Set state without influencing other objects, for" - " diagnostic pur-\n" - " poses only.\n" - "start [-S size] volume | plex | subdisk\n" - " Allow the system to access the objects.\n" - "stripe [-fv] [-n name] drives\n" - " Create a striped volume from the specified drives.\n" - ); -} - -static void -gvinum_setstate(int argc, char * const *argv) -{ - struct gctl_req *req; - int flags, i; - const char *errstr; - - flags = 0; - - optreset = 1; - optind = 1; - - while ((i = getopt(argc, argv, "f")) != -1) { - switch (i) { - case 'f': - flags |= GV_FLAG_F; - break; - case '?': - default: - warn("invalid flag: %c", i); - return; - } - } - - argc -= optind; - argv += optind; - - if (argc != 2) { - warnx("usage: setstate [-f] <state> <obj>"); - return; - } - - /* - * XXX: This hack is needed to avoid tripping over (now) invalid - * 'classic' vinum states and will go away later. - */ - if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && - strcmp(argv[0], "stale")) { - warnx("invalid state '%s'", argv[0]); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "setstate"); - gctl_ro_param(req, "state", -1, argv[0]); - gctl_ro_param(req, "object", -1, argv[1]); - gctl_ro_param(req, "flags", sizeof(int), &flags); - - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("%s", errstr); - gctl_free(req); -} - -static void -gvinum_list(int argc, char * const *argv) -{ - struct gctl_req *req; - int flags, i, j; - const char *errstr; - char buf[20], config[GV_CFG_LEN + 1]; - const char *cmd; - - flags = 0; - cmd = "list"; - - if (argc) { - optreset = 1; - optind = 1; - cmd = argv[0]; - while ((j = getopt(argc, argv, "rsvV")) != -1) { - switch (j) { - case 'r': - flags |= GV_FLAG_R; - break; - case 's': - flags |= GV_FLAG_S; - break; - case 'v': - flags |= GV_FLAG_V; - break; - case 'V': - flags |= GV_FLAG_V; - flags |= GV_FLAG_VV; - break; - case '?': - default: - return; - } - } - argc -= optind; - argv += optind; - - } - - config[0] = '\0'; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "list"); - gctl_ro_param(req, "cmd", -1, cmd); - gctl_ro_param(req, "argc", sizeof(int), &argc); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_add_param(req, "config", sizeof(config), config, - GCTL_PARAM_WR | GCTL_PARAM_ASCII); - if (argc) { - for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "argv%d", i); - gctl_ro_param(req, buf, -1, argv[i]); - } - } - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't get configuration: %s", errstr); - gctl_free(req); - return; - } - - printf("%s", config); - gctl_free(req); -} - -/* Create a mirrored volume. */ -static void -gvinum_mirror(int argc, char * const *argv) -{ - - if (argc < 2) { - warnx("usage\tmirror [-fsv] [-n name] drives\n"); - return; - } - create_volume(argc, argv, "mirror"); -} - -/* Note that move is currently of form '[-r] target object [...]' */ -static void -gvinum_move(int argc, char * const *argv) -{ - struct gctl_req *req; - const char *errstr; - char buf[20]; - int flags, i, j; - - flags = 0; - if (argc) { - optreset = 1; - optind = 1; - while ((j = getopt(argc, argv, "f")) != -1) { - switch (j) { - case 'f': - flags |= GV_FLAG_F; - break; - case '?': - default: - return; - } - } - argc -= optind; - argv += optind; - } - - switch (argc) { - case 0: - warnx("no destination or object(s) to move specified"); - return; - case 1: - warnx("no object(s) to move specified"); - return; - default: - break; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "move"); - gctl_ro_param(req, "argc", sizeof(int), &argc); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "destination", -1, argv[0]); - for (i = 1; i < argc; i++) { - snprintf(buf, sizeof(buf), "argv%d", i); - gctl_ro_param(req, buf, -1, argv[i]); - } - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("can't move object(s): %s", errstr); - gctl_free(req); -} - -static void -gvinum_printconfig(int argc __unused, char * const *argv __unused) -{ - - printconfig(stdout, ""); -} - -static void -gvinum_parityop(int argc, char * const *argv, int rebuild) -{ - struct gctl_req *req; - int flags, i; - const char *errstr; - const char *op; - - if (rebuild) { - op = "rebuildparity"; - } else { - op = "checkparity"; - } - - optreset = 1; - optind = 1; - flags = 0; - while ((i = getopt(argc, argv, "fv")) != -1) { - switch (i) { - case 'f': - flags |= GV_FLAG_F; - break; - case 'v': - flags |= GV_FLAG_V; - break; - default: - warnx("invalid flag '%c'", i); - return; - } - } - argc -= optind; - argv += optind; - - if (argc != 1) { - warn("usage: %s [-f] [-v] <plex>", op); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, op); - gctl_ro_param(req, "rebuild", sizeof(int), &rebuild); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "plex", -1, argv[0]); - - errstr = gctl_issue(req); - if (errstr) - warnx("%s\n", errstr); - gctl_free(req); -} - -/* Create a RAID-5 volume. */ -static void -gvinum_raid5(int argc, char * const *argv) -{ - - if (argc < 2) { - warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n"); - return; - } - create_volume(argc, argv, "raid5"); -} - -static void -gvinum_rename(int argc, char * const *argv) -{ - struct gctl_req *req; - const char *errstr; - int flags, j; - - flags = 0; - - if (argc) { - optreset = 1; - optind = 1; - while ((j = getopt(argc, argv, "r")) != -1) { - switch (j) { - case 'r': - flags |= GV_FLAG_R; - break; - default: - return; - } - } - argc -= optind; - argv += optind; - } - - switch (argc) { - case 0: - warnx("no object to rename specified"); - return; - case 1: - warnx("no new name specified"); - return; - case 2: - break; - default: - warnx("more than one new name specified"); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "rename"); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "object", -1, argv[0]); - gctl_ro_param(req, "newname", -1, argv[1]); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("can't rename object: %s", errstr); - gctl_free(req); -} - -static void -gvinum_rm(int argc, char * const *argv) -{ - struct gctl_req *req; - int flags, i, j; - const char *errstr; - char buf[20]; - - flags = 0; - optreset = 1; - optind = 1; - while ((j = getopt(argc, argv, "rf")) != -1) { - switch (j) { - case 'f': - flags |= GV_FLAG_F; - break; - case 'r': - flags |= GV_FLAG_R; - break; - default: - return; - } - } - argc -= optind; - argv += optind; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "remove"); - gctl_ro_param(req, "argc", sizeof(int), &argc); - gctl_ro_param(req, "flags", sizeof(int), &flags); - if (argc) { - for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "argv%d", i); - gctl_ro_param(req, buf, -1, argv[i]); - } - } - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't remove: %s", errstr); - gctl_free(req); - return; - } - gctl_free(req); -} - -static void -gvinum_resetconfig(int argc, char * const *argv) -{ - struct gctl_req *req; - const char *errstr; - char reply[32]; - int flags, i; - - flags = 0; - while ((i = getopt(argc, argv, "f")) != -1) { - switch (i) { - case 'f': - flags |= GV_FLAG_F; - break; - default: - warn("invalid flag: %c", i); - return; - } - } - if ((flags & GV_FLAG_F) == 0) { - if (!isatty(STDIN_FILENO)) { - warn("Please enter this command from a tty device\n"); - return; - } - printf(" WARNING! This command will completely wipe out" - " your gvinum configuration.\n" - " All data will be lost. If you really want to do this," - " enter the text\n\n" - " NO FUTURE\n" - " Enter text -> "); - fgets(reply, sizeof(reply), stdin); - if (strcmp(reply, "NO FUTURE\n")) { - printf("\n No change\n"); - return; - } - } - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "resetconfig"); - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't reset config: %s", errstr); - gctl_free(req); - return; - } - gctl_free(req); - printf("gvinum configuration obliterated\n"); -} - -static void -gvinum_saveconfig(void) -{ - struct gctl_req *req; - const char *errstr; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "saveconfig"); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("can't save configuration: %s", errstr); - gctl_free(req); -} - -static void -gvinum_start(int argc, char * const *argv) -{ - struct gctl_req *req; - int i, initsize, j; - const char *errstr; - char buf[20]; - - /* 'start' with no arguments is a no-op. */ - if (argc == 1) - return; - - initsize = 0; - - optreset = 1; - optind = 1; - while ((j = getopt(argc, argv, "S")) != -1) { - switch (j) { - case 'S': - initsize = atoi(optarg); - break; - default: - return; - } - } - argc -= optind; - argv += optind; - - if (!initsize) - initsize = 512; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "start"); - gctl_ro_param(req, "argc", sizeof(int), &argc); - gctl_ro_param(req, "initsize", sizeof(int), &initsize); - if (argc) { - for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "argv%d", i); - gctl_ro_param(req, buf, -1, argv[i]); - } - } - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't start: %s", errstr); - gctl_free(req); - return; - } - - gctl_free(req); -} - -static void -gvinum_stop(int argc __unused, char * const *argv __unused) -{ - int err, fileid; - - fileid = kldfind(GVINUMKLD); - if (fileid == -1) { - if (modfind(GVINUMMOD) < 0) - warn("cannot find " GVINUMKLD); - return; - } - - /* - * This little hack prevents that we end up in an infinite loop in - * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM - * event thread will be free for the g_wither_geom() call from - * gv_unload(). It's silly, but it works. - */ - printf("unloading " GVINUMKLD " kernel module... "); - fflush(stdout); - if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) { - sleep(1); - err = kldunload(fileid); - } - if (err != 0) { - printf(" failed!\n"); - warn("cannot unload " GVINUMKLD); - return; - } - - printf("done\n"); - exit(0); -} - -/* Create a striped volume. */ -static void -gvinum_stripe(int argc, char * const *argv) -{ - - if (argc < 2) { - warnx("usage:\tstripe [-fv] [-n name] drives\n"); - return; - } - create_volume(argc, argv, "stripe"); -} - -/* Grow a subdisk by adding disk backed by provider. */ -static void -gvinum_grow(int argc, char * const *argv) -{ - struct gctl_req *req; - char *drive, *sdname; - char sdprefix[GV_MAXSDNAME]; - struct gv_drive *d; - struct gv_sd *s; - const char *errstr; - int drives, volumes, plexes, subdisks, flags; - - flags = 0; - drives = volumes = plexes = subdisks = 0; - if (argc < 3) { - warnx("usage:\tgrow plex drive\n"); - return; - } - - s = gv_alloc_sd(); - if (s == NULL) { - warn("unable to create subdisk"); - return; - } - d = gv_alloc_drive(); - if (d == NULL) { - warn("unable to create drive"); - free(s); - return; - } - /* Lookup device and set an appropriate drive name. */ - drive = find_drive(); - if (drive == NULL) { - warn("unable to find an appropriate drive name"); - free(s); - free(d); - return; - } - strlcpy(d->name, drive, sizeof(d->name)); - copy_device(d, argv[2]); - - drives = 1; - - /* We try to use the plex name as basis for the subdisk name. */ - snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]); - sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME); - if (sdname == NULL) { - warn("unable to find an appropriate subdisk name"); - free(s); - free(d); - free(drive); - return; - } - strlcpy(s->name, sdname, sizeof(s->name)); - free(sdname); - strlcpy(s->plex, argv[1], sizeof(s->plex)); - strlcpy(s->drive, d->name, sizeof(s->drive)); - subdisks = 1; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "create"); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "volumes", sizeof(int), &volumes); - gctl_ro_param(req, "plexes", sizeof(int), &plexes); - gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); - gctl_ro_param(req, "drives", sizeof(int), &drives); - gctl_ro_param(req, "drive0", sizeof(*d), d); - gctl_ro_param(req, "sd0", sizeof(*s), s); - errstr = gctl_issue(req); - free(drive); - if (errstr != NULL) { - warnx("unable to grow plex: %s", errstr); - free(s); - free(d); - return; - } - gctl_free(req); -} - -static void -parseline(int argc, char * const *argv) -{ - - if (argc <= 0) - return; - - if (!strcmp(argv[0], "create")) - gvinum_create(argc, argv); - else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) - exit(0); - else if (!strcmp(argv[0], "attach")) - gvinum_attach(argc, argv); - else if (!strcmp(argv[0], "detach")) - gvinum_detach(argc, argv); - else if (!strcmp(argv[0], "concat")) - gvinum_concat(argc, argv); - else if (!strcmp(argv[0], "grow")) - gvinum_grow(argc, argv); - else if (!strcmp(argv[0], "help")) - gvinum_help(); - else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "ld")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "lp")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "ls")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "lv")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "mirror")) - gvinum_mirror(argc, argv); - else if (!strcmp(argv[0], "move")) - gvinum_move(argc, argv); - else if (!strcmp(argv[0], "mv")) - gvinum_move(argc, argv); - else if (!strcmp(argv[0], "printconfig")) - gvinum_printconfig(argc, argv); - else if (!strcmp(argv[0], "raid5")) - gvinum_raid5(argc, argv); - else if (!strcmp(argv[0], "rename")) - gvinum_rename(argc, argv); - else if (!strcmp(argv[0], "resetconfig")) - gvinum_resetconfig(argc, argv); - else if (!strcmp(argv[0], "rm")) - gvinum_rm(argc, argv); - else if (!strcmp(argv[0], "saveconfig")) - gvinum_saveconfig(); - else if (!strcmp(argv[0], "setstate")) - gvinum_setstate(argc, argv); - else if (!strcmp(argv[0], "start")) - gvinum_start(argc, argv); - else if (!strcmp(argv[0], "stop")) - gvinum_stop(argc, argv); - else if (!strcmp(argv[0], "stripe")) - gvinum_stripe(argc, argv); - else if (!strcmp(argv[0], "checkparity")) - gvinum_parityop(argc, argv, 0); - else if (!strcmp(argv[0], "rebuildparity")) - gvinum_parityop(argc, argv, 1); - else - printf("unknown command '%s'\n", argv[0]); -} - -/* - * The guts of printconfig. This is called from gvinum_printconfig and from - * gvinum_create when called without an argument, in order to give the user - * something to edit. - */ -static void -printconfig(FILE *of, const char *comment) -{ - struct gctl_req *req; - struct utsname uname_s; - const char *errstr; - time_t now; - char buf[GV_CFG_LEN + 1]; - - uname(&uname_s); - time(&now); - buf[0] = '\0'; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "getconfig"); - gctl_ro_param(req, "comment", -1, comment); - gctl_add_param(req, "config", sizeof(buf), buf, - GCTL_PARAM_WR | GCTL_PARAM_ASCII); - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't get configuration: %s", errstr); - return; - } - gctl_free(req); - - fprintf(of, "# Vinum configuration of %s, saved at %s", - uname_s.nodename, - ctime(&now)); - - if (*comment != '\0') - fprintf(of, "# Current configuration:\n"); - - fprintf(of, "%s", buf); -} |