diff options
Diffstat (limited to 'sbin/gvinum')
-rw-r--r-- | sbin/gvinum/Makefile | 15 | ||||
-rw-r--r-- | sbin/gvinum/gvinum.8 | 441 | ||||
-rw-r--r-- | sbin/gvinum/gvinum.c | 1432 | ||||
-rw-r--r-- | sbin/gvinum/gvinum.h | 39 |
4 files changed, 1927 insertions, 0 deletions
diff --git a/sbin/gvinum/Makefile b/sbin/gvinum/Makefile new file mode 100644 index 000000000000..8cccf56ecdbb --- /dev/null +++ b/sbin/gvinum/Makefile @@ -0,0 +1,15 @@ +# $FreeBSD$ + +PROG= gvinum +SRCS= gvinum.c gvinum.h geom_vinum_share.c +MAN= gvinum.8 + +WARNS?= 2 +CFLAGS= -I${.CURDIR}/../../sys -I${DESTDIR}/${INCLUDEDIR}/edit + +DPADD= ${LIBEDIT} ${LIBTERMCAP} ${LIBDEVSTAT} ${LIBKVM} ${LIBGEOM} +LDADD= -ledit -ltermcap -ldevstat -lkvm -lgeom + +.PATH: ${.CURDIR}/../../sys/geom/vinum + +.include <bsd.prog.mk> diff --git a/sbin/gvinum/gvinum.8 b/sbin/gvinum/gvinum.8 new file mode 100644 index 000000000000..c8e770926949 --- /dev/null +++ b/sbin/gvinum/gvinum.8 @@ -0,0 +1,441 @@ +.\" Copyright (c) 2005 Chris Jones +.\" All rights reserved. +.\" +.\" This software was 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. +.\" +.\" $FreeBSD$ +.\" +.Dd April 10, 2009 +.Dt GVINUM 8 +.Os +.Sh NAME +.Nm gvinum +.Nd Logical Volume Manager control program +.Sh SYNOPSIS +.Nm +.Op Ar command +.Op Fl options +.Sh COMMANDS +.Bl -tag -width indent +.It Ic attach Ar plex volume Op Cm rename +.It Ic attach Ar subdisk plex Oo Ar offset Oc Op Cm rename +Attach a plex to a volume, or a subdisk to a plex. +If offset is specified, the subdisk will be attached to the given offset within +the plex. +If rename is specified, the subdisk or plex will change name according to the +object it attaches to. +.It Ic checkparity Oo Fl f Oc Ar plex +Check the parity blocks of a RAID-5 plex. +The parity check will start at the +beginning of the plex if the +.Fl f +flag is specified, or otherwise at the location of the parity check pointer, +the first location at which plex's parity is incorrect. +All subdisks in the +plex must be up for a parity check. +.It Ic concat Oo Fl fv Oc Oo Fl n Ar name Oc Ar drives +Create a concatenated volume from the specified drives. +If no name is specified, a unique name will be set by gvinum. +.It Ic create Oo Fl f Oc Op Ar description-file +Create a volume as described in +.Ar description-file . +If no +.Ar description-file +provided, opens an editor and provides the current +.Nm +configuration for editing. +The +.Fl f +flag will make gvinum ignore any errors regarding creating objects that already +exists. +However, in contrast to vinum, objects that are not properly named in the +.Ar description-file +will not be created when the +.Fl f +flag is given. +.It Ic detach Oo Fl f Oc Op Ar plex | subdisk +Detach a plex or subdisk from the volume or plex to which it is +attached. +.It Ic grow Ar plex device +Grow a plex by creating a gvinum drive and subdisk on device and attach it to +the plex. +If required by the plex organization, it will be put into the growable state. +.It Ic help +Provides a synopsis of +.Nm +commands and arguments. +.It Ic l | list Oo Fl rvV Oc Op Ar volume | plex | subdisk +.It Ic ld Oo Fl rvV Oc Op Ar drive ... +.It Ic ls Oo Fl rvV Oc Op Ar subdisk ... +.It Ic lp Oo Fl rvV Oc Op Ar plex ... +.It Ic lv Oo Fl rvV Oc Op Ar volume ... +List information about the relevant object(s). +The +.Fl r +flag provides recursive display, showing each object's subordinate objects in +proper relation. +The +.Fl v +and +.Fl V +flags provide progressively more detailed output. +.It Ic mirror Oo Fl fsv Oc Oo Fl n Ar name Oc Ar drives +Create a mirrored volume from the specified drives. +It requires at least a multiple of 2 drives. +If no name is specified, a unique name will be set by gvinum. +If the +.Fl s +flag is specified, a striped mirror will be created, and thus requires a +multiple of 4 drives. +.It Ic move | mv Fl f Ar drive subdisk Op Ar ... +Move the subdisk(s) to the specified drive. +The +.Fl f +flag is required, as all data on the indicated subdisk(s) will be destroyed as +part of the move. +This can currently only be done when the subdisk is +not being accessed. +.Pp +If a single subdisk is moved, and it forms a part of a RAID-5 plex, the moved +subdisks will need to be set to the +.Dq stale +state, and the plex will require a +.Ic start +command. +If multiple subdisk(s) is moved, and form part of a RAID-5 plex, the +moved disk(s) will need to be set to the +.Dq up +state and the plex will require a +.Ic rebuildparity +command. +If the subdisk(s) form part of a plex that is mirrored with other +plexes, the plex will require restarting and will sync once restarted. +Moving +more than one subdisk in a RAID-5 plex or subdisks from both sides of a +mirrored plex volume will destroy data. +Note that parity rebuilds and syncing +must be started manually after a move. +.It Ic printconfig +Write a copy of the current configuration to standard output. +.It Ic quit +Exit +.Nm +when running in interactive mode. +Normally this would be done by entering the +EOF character. +.It Ic raid5 Oo Fl fv Oc Oo Fl s Ar stripesize Oc Oo Fl n Ar name Oc Ar drives +Create a RAID-5 volume from the specified drives. +If no name is specified,a unique name will be set by +.Ic gvinum. +This organization requires at least three drives. +.It Ic rename Oo Fl r Oc Ar drive | subdisk | plex | volume newname +Change the name of the specified object. +The +.Fl r +flag will recursively rename subordinate objects. +.Pp +Note that device nodes will not be renamed until +.Nm +is restarted. +.It Ic rebuildparity Oo Fl f Oc Ar plex +Rebuild the parity blocks of a RAID-5 plex. +The parity rebuild will start at +the beginning of the plex if the +.Fl f +flag is specified, or otherwise at the location of the parity check pointer. +All subdisks in the plex must be up for a parity check. +.It Ic resetconfig +Reset the complete +.Nm +configuration. +.It Ic rm Oo Fl r Oc Ar volume | plex | subdisk +Remove an object and, if +.Fl r +is specified, its subordinate objects. +.It Ic saveconfig +Save +.Nm +configuration to disk after configuration failures. +.It Ic setstate Oo Fl f Oc Ar state volume | plex | subdisk | drive +Set state without influencing other objects, for diagnostic purposes +only. +The +.Fl f +flag forces state changes regardless of whether they are legal. +.It Ic start +Read configuration from all vinum drives. +.It Ic start Oo Fl S Ar size Oc Ar volume | plex | subdisk +Allow the system to access the objects. +If necessary, plexes will be synced and rebuilt. +If a subdisk was added to a running RAID-5 or striped plex, gvinum will +expand into this subdisk and grow the whole RAID-5 array. +This can be done without unmounting your filesystem. +The +.Fl S +flag is currently ignored. +.It Ic stop Oo Fl f Oc Op Ar volume | plex | subdisk +Terminate access to the objects, or stop +.Nm +if no parameters are specified. +.It Ic stripe Oo Fl fv Oc Oo Fl n Ar name Oc Ar drives +Create a striped volume from the specified drives. If no name is specified, +a unique name will be set by Ic gvinum. This organization requires at least two +drives. +.El +.Sh DESCRIPTION +The +.Nm +utility communicates with the kernel component of the GVinum logical volume +manager. +It is designed either for interactive use, when started without +command line arguments, or to execute a single command if the command is +supplied on the command line. +In interactive mode, +.Nm +maintains a command line history. +.Sh OPTIONS +The +.Nm +commands may be followed by an option. +.Bl -tag -width indent +.It Fl f +The +.Fl f +.Pq Dq force +option overrides safety checks. +It should be used with extreme caution. +This +option is required in order to use the +.Ic move +command. +.It Fl r +The +.Fl r +.Pq Dq recursive +option applies the command recursively to subordinate objects. +For example, in +conjunction with the +.Ic lv +command, the +.Fl r +option will also show information about the plexes and subdisks belonging to +the volume. +It is also used by the +.Ic rename +command to indicate that subordinate objects such as subdisks should be renamed +to match the object(s) specified and by the +.Ic rm +command to delete plexes belonging to a volume and so on. +.It Fl v +The +.Fl v +.Pq Dq verbose +option provides more detailed output. +.It Fl V +The +.Fl V +.Pq Dq "very verbose" +option provides even more detailed output than +.Fl v . +.El +.Sh ENVIRONMENT +.Bl -tag -width ".Ev EDITOR" +.It Ev EDITOR +The name of the editor to use for editing configuration files, by +default +.Xr vi 1 +is invoked. +.El +.Sh FILES +.Bl -tag -width ".Pa /dev/gvinum/plex" +.It Pa /dev/gvinum +directory with device nodes for +.Nm +objects +.El +.Sh EXAMPLES +To create a mirror on disks /dev/ad1 and /dev/ad2, create a filesystem, mount, +unmount and then stop Ic gvinum: +.Pp +.Dl "gvinum mirror /dev/ad1 /dev/ad2" +.Dl "newfs /dev/gvinum/gvinumvolume0" +.Dl "mount /dev/gvinum/gvinumvolume0 /mnt" +.Dl "..." +.Dl "unmount /mnt" +.Dl "gvinum stop" +.Pp +To create a striped mirror on disks /dev/ad1 /dev/ad2 /dev/ad3 and /dev/ad4 +named "data" and create a filesystem: +.Pp +.Dl "gvinum mirror -s -n data /dev/ad1 /dev/ad2 /dev/ad3 /dev/ad4" +.Dl "newfs /dev/gvinum/data" +.Pp +To create a raid5 array on disks /dev/ad1 /dev/ad2 and /dev/ad3, with stripesize +493k you can use the raid5 command: +.Pp +.Dl "gvinum raid5 -s 493k /dev/ad1 /dev/ad2 /dev/ad3" +.Pp +Then the volume will be created automatically. +Afterwards, you have to initialize the volume: +.Pp +.Dl "gvinum start myraid5vol" +.Pp +The initialization will start, and the states will be updated when it's +finished. +The list command will give you information about its progress. +.Pp +Imagine that one of the drives fails, and the output of 'printconfig' looks +something like this: +.Pp +.Dl "drive gvinumdrive1 device /dev/ad2" +.Dl "drive gvinumdrive2 device /dev/???" +.Dl "drive gvinumdrive0 device /dev/ad1" +.Dl "volume myraid5vol" +.Dl "plex name myraid5vol.p0 org raid5 986s vol myraid5vol" +.Dl "sd name myraid5vol.p0.s2 drive gvinumdrive2 len 32538s driveoffset 265s" +.Dl "plex myraid5vol.p0 plexoffset 1972s" +.Dl "sd name myraid5vol.p0.s1 drive gvinumdrive1 len 32538s driveoffset 265s" +.Dl "plex myraid5vol.p0 plexoffset 986s" +.Dl "sd name myraid5vol.p0.s0 drive gvinumdrive0 len 32538s driveoffset 265s" +.Dl "plex myraid5vol.p0 plexoffset 0s" +.Pp +Create a new drive with this configuration: +.Pp +.Dl "drive gdrive4 device /dev/ad4" +.Pp +Then move the stale subdisk to the new drive: +.Pp +.Dl "gvinum move gdrive4 myraid5vol.p0.s2" +.Pp +Then, initiate the rebuild: +.Pp +.Dl "gvinum start myraid5vol.p0" +.Pp +The plex will go up form degraded mode after the rebuild is finished. +The plex can still be used while the rebuild is in progress, although requests +might be delayed. +.Pp +Given the configuration as in the previous example, growing a RAID-5 or STRIPED +array is accomplished by using the grow command: +.Pp +.Dl "gvinum grow myraid5vol.p0 /dev/ad4" +.Pp +If everything went ok, the plex state should now be set to growable. +You can then start the growing with the +.Ic start +command: +.Pp +.Dl "gvinum start myraid5vol.p0" +.Pp +As with rebuilding, you can watch the progress using the +.Ic list +command. +.Pp +For a more advanced usage and detailed explanation of gvinum, the +handbook is recommended. +.Pp +.Sh SEE ALSO +.Xr geom 4 , +.Xr geom 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 5.3 . +The +.Nm vinum +utility, on which +.Nm +is based, was written by +.An "Greg Lehey" . +.Pp +The +.Nm +utility +was written by +.An "Lukas Ertl" . +The +.Ic move +and +.Ic rename +commands and +documentation were added by +.An "Chris Jones" +through the 2005 Google Summer +of Code program. +.Ic a partial rewrite of gvinum was done by "Lukas Ertl" and "Ulf Lilleengen" +through the 2007 Google Summer of Code program. +The documentation have been updated to reflect the new functionality. +.Sh AUTHORS +.An Lukas Ertl Aq le@FreeBSD.org +.An Chris Jones Aq soc-cjones@FreeBSD.org +.An Ulf Lilleengen Aq lulf@FreeBSD.org +.Sh BUGS +Currently, +.Nm +does not rename devices in +.Pa /dev/gvinum +until reloaded. +.Pp +The +.Fl S +initsize flag to +.Ic start +is ignored. +.Pp +Moving subdisks that are not part of a mirrored or RAID-5 volume will +destroy data. +It is perhaps a bug to permit this. +.Pp +Plexes in which subdisks have been moved do not automatically sync or +rebuild parity. +This may leave data unprotected and is perhaps unwise. +.Pp +Currently, +.Nm +does not yet fully implement all of the functions found in +.Xr vinum 4 . +Specifically, the following commands from +.Xr vinum 4 +are not supported: +.Bl -tag -width indent +.It Ic debug +Cause the volume manager to enter the kernel debugger. +.It Ic debug Ar flags +Set debugging flags. +.It Ic dumpconfig Op Ar drive ... +List the configuration information stored on the specified drives, or all +drives in the system if no drive names are specified. +.It Ic info Op Fl vV +List information about volume manager state. +.It Ic label Ar volume +Create a volume label. +.It Ic resetstats Oo Fl r Oc Op Ar volume | plex | subdisk +Reset statistics counters for the specified objects, or for all objects if none +are specified. +.It Ic setdaemon Op Ar value +Set daemon configuration. +.El diff --git a/sbin/gvinum/gvinum.c b/sbin/gvinum/gvinum.c new file mode 100644 index 000000000000..203976100c0e --- /dev/null +++ b/sbin/gvinum/gvinum.c @@ -0,0 +1,1432 @@ +/* + * 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. + * + * $FreeBSD$ + */ + +#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" + +void gvinum_attach(int, char **); +void gvinum_concat(int, char **); +void gvinum_create(int, char **); +void gvinum_detach(int, char **); +void gvinum_grow(int, char **); +void gvinum_help(void); +void gvinum_list(int, char **); +void gvinum_move(int, char **); +void gvinum_mirror(int, char **); +void gvinum_parityop(int, char **, int); +void gvinum_printconfig(int, char **); +void gvinum_raid5(int, char **); +void gvinum_rename(int, char **); +void gvinum_resetconfig(void); +void gvinum_rm(int, char **); +void gvinum_saveconfig(void); +void gvinum_setstate(int, char **); +void gvinum_start(int, char **); +void gvinum_stop(int, char **); +void gvinum_stripe(int, char **); +void parseline(int, char **); +void printconfig(FILE *, char *); + +char *create_drive(char *); +void create_volume(int, char **, char *); +char *find_name(const char *, int, int); +char *find_pattern(char *, char *); +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 line, tokens; + char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS]; + + /* Load the module if necessary. */ + if (kldfind(GVINUMMOD) < 0 && kldload(GVINUMMOD) < 0) + err(1, GVINUMMOD ": 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); + line++; /* count the lines */ + 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. */ +void +gvinum_attach(int argc, char **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); +} + +void +gvinum_create(int argc, char **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], *ed, *sdname; + 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[1], "r")) == NULL) { + warn("can't open '%s' for reading", argv[1]); + 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. */ +void +gvinum_concat(int argc, char **argv) +{ + + if (argc < 2) { + warnx("usage:\tconcat [-fv] [-n name] drives\n"); + return; + } + create_volume(argc, argv, "concat"); +} + + +/* Create a drive quick and dirty. */ +char * +create_drive(char *device) +{ + struct gv_drive *d; + struct gctl_req *req; + const char *errstr; + char *drivename, *dname; + int drives, i, flags, volumes, subdisks, plexes; + + 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); + gctl_free(req); + return (NULL); + } else { + gctl_free(req); + /* 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)) { + free(dname); + return (drivename); + } + free(dname); + dname = NULL; + usleep(100000); /* Sleep for 0.1s */ + } + } + gctl_free(req); + return (drivename); +} + +/* + * General routine for creating a volume. Mainly for use by concat, mirror, + * raid5 and stripe commands. + */ +void +create_volume(int argc, char **argv, 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>. */ +char * +find_pattern(char *line, 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 a prefix. */ +char * +find_name(const char *prefix, int type, int namelen) +{ + struct gctl_req *req; + char comment[1], buf[GV_CFG_LEN - 1], *name, *sname, *ptr; + const char *errstr; + int i, n, begin, len, conflict; + char line[1024]; + + comment[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_rw_param(req, "config", sizeof(buf), buf); + 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); +} + +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. */ +void +gvinum_detach(int argc, char **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); +} + +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\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" + ); + + return; +} + +void +gvinum_setstate(int argc, char **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); +} + +void +gvinum_list(int argc, char **argv) +{ + struct gctl_req *req; + int flags, i, j; + const char *errstr; + char buf[20], *cmd, config[GV_CFG_LEN + 1]; + + 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; + + } + + 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_rw_param(req, "config", sizeof(config), config); + 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); + return; +} + +/* Create a mirrored volume. */ +void +gvinum_mirror(int argc, char **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 [...]' */ +void +gvinum_move(int argc, char **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); + return; +} + +void +gvinum_printconfig(int argc, char **argv) +{ + printconfig(stdout, ""); +} + +void +gvinum_parityop(int argc, char **argv, int rebuild) +{ + struct gctl_req *req; + int flags, i; + const char *errstr; + char *op, *msg; + + if (rebuild) { + op = "rebuildparity"; + msg = "Rebuilding"; + } else { + op = "checkparity"; + msg = "Checking"; + } + + 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; + case '?': + 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. */ +void +gvinum_raid5(int argc, char **argv) +{ + + if (argc < 2) { + warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n"); + return; + } + create_volume(argc, argv, "raid5"); +} + + +void +gvinum_rename(int argc, char **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; + case '?': + 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); + return; +} + +void +gvinum_rm(int argc, char **argv) +{ + struct gctl_req *req; + int flags, i, j; + const char *errstr; + char buf[20], *cmd; + + cmd = argv[0]; + 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; + case '?': + 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); +} + +void +gvinum_resetconfig(void) +{ + struct gctl_req *req; + const char *errstr; + char reply[32]; + + 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"); +} + +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); +} + +void +gvinum_start(int argc, char **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; + case '?': + 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); +} + +void +gvinum_stop(int argc, char **argv) +{ + int err, fileid; + + fileid = kldfind(GVINUMMOD); + if (fileid == -1) { + warn("cannot find " GVINUMMOD); + 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 " GVINUMMOD " 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 " GVINUMMOD); + return; + } + + printf("done\n"); + exit(0); +} + +/* Create a striped volume. */ +void +gvinum_stripe(int argc, char **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. */ +void +gvinum_grow(int argc, char **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; + + 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); +} + +void +parseline(int argc, char **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(); + 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]); + + return; +} + +/* + * 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. + */ +void +printconfig(FILE *of, 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); + + 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_rw_param(req, "config", sizeof(buf), buf); + 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); +} diff --git a/sbin/gvinum/gvinum.h b/sbin/gvinum/gvinum.h new file mode 100644 index 000000000000..d1b45a07749b --- /dev/null +++ b/sbin/gvinum/gvinum.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1997, 1998 + * Nan Yang Computer Services Limited. All rights reserved. + * + * This software is distributed under the so-called ``Berkeley + * License'': + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Nan Yang Computer + * Services Limited. + * 4. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * This software is provided ``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 the company 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. + */ + +/* $FreeBSD$ */ + +#define GVINUMMOD "geom_vinum" |