summaryrefslogtreecommitdiffstats
path: root/sbin/vinum
diff options
context:
space:
mode:
authorgrog <grog@FreeBSD.org>1998-09-16 05:57:36 +0000
committergrog <grog@FreeBSD.org>1998-09-16 05:57:36 +0000
commit22d3d93a1eba3a9ce72cf9298bb84d5529a2e0ed (patch)
tree6254db166b0e835e60a70488b228dea10268b04b /sbin/vinum
parent633c70539f9a75caebb97293bd237b0611df1427 (diff)
downloadFreeBSD-src-22d3d93a1eba3a9ce72cf9298bb84d5529a2e0ed.zip
FreeBSD-src-22d3d93a1eba3a9ce72cf9298bb84d5529a2e0ed.tar.gz
Import base vinum userland sources
Diffstat (limited to 'sbin/vinum')
-rw-r--r--sbin/vinum/Makefile23
-rw-r--r--sbin/vinum/commands.c1030
-rw-r--r--sbin/vinum/list.c743
-rw-r--r--sbin/vinum/v.c561
-rw-r--r--sbin/vinum/vext.h119
-rw-r--r--sbin/vinum/vinum.8899
6 files changed, 3375 insertions, 0 deletions
diff --git a/sbin/vinum/Makefile b/sbin/vinum/Makefile
new file mode 100644
index 0000000..7d0a0c2
--- /dev/null
+++ b/sbin/vinum/Makefile
@@ -0,0 +1,23 @@
+PROG= vinum
+SRCS= v.c list.c parser.c util.c vext.h commands.c
+MAN8= vinum.8
+
+CFLAGS= -I${.CURDIR}/../../lkm/vinum -g -Wall -DDEBUG -DRAID5
+
+LDADD+= -lutil -lreadline -ltermcap
+# DPADD+= ${LIBKVM}
+BINGRP= kmem
+BINMODE= 2555
+
+parser.c:
+ rm -f $@
+ ln -s ${.CURDIR}/../../lkm/vinum/$@ .
+
+util.c: statetexts.h
+ rm -f $@
+ ln -s ${.CURDIR}/../../lkm/vinum/$@ .
+
+statetexts.h:
+ (cd ${.CURDIR}/../../lkm/vinum; make $@)
+
+.include <bsd.prog.mk>
diff --git a/sbin/vinum/commands.c b/sbin/vinum/commands.c
new file mode 100644
index 0000000..6554d33
--- /dev/null
+++ b/sbin/vinum/commands.c
@@ -0,0 +1,1030 @@
+/* commands.c: vinum interface program, main commands */
+/*-
+ * 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.
+ *
+ */
+
+/* $Id: commands.c,v 1.1 1998/08/19 08:06:57 grog Exp grog $ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include "vinumhdr.h"
+#include "vext.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <readline/history.h>
+#include <readline/readline.h>
+
+static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen);
+
+void
+vinum_create(int argc, char *argv[], char *arg0[])
+{
+ int error;
+ FILE *dfd; /* file descriptor for the config file */
+ char buffer[BUFSIZE]; /* read config file in here */
+ struct _ioctl_reply *reply;
+
+ if (argc != 1) { /* wrong arg count */
+ fprintf(stderr, "Expecting 1 parameter, not %d\n", argc);
+ return;
+ }
+ reply = (struct _ioctl_reply *) &buffer;
+ dfd = fopen(argv[0], "r");
+ if (dfd == NULL) { /* no go */
+ fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno));
+ return;
+ }
+ if (ioctl(superdev, VINUM_STARTCONFIG, NULL)) { /* can't get config? */
+ printf("Can't configure: %s (%d)\n", strerror(errno), errno);
+ return;
+ }
+ file_line = 0; /* start with line 1 */
+ /* Parse the configuration, and add it to the global configuration */
+ for (;;) { /* love this style(9) */
+ char *configline;
+
+ configline = fgets(buffer, BUFSIZE, dfd);
+
+ if (configline == NULL) {
+ if (ferror(dfd))
+ perror("Can't read config file");
+ break;
+ }
+ file_line++; /* count the lines */
+ if (verbose)
+ printf("%4d: %s", file_line, buffer); /* XXX */
+ ioctl(superdev, VINUM_CREATE, &buffer);
+ if (reply->error != 0) { /* error in config */
+ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error));
+ /* XXX at the moment, we reset the config
+ * lock on error, so try to get it again.
+ * If we fail, don't cry again */
+ if (ioctl(superdev, VINUM_STARTCONFIG, NULL)) /* can't get config? */
+ return;
+ }
+ }
+ fclose(dfd); /* done with the config file */
+ error = ioctl(superdev, VINUM_SAVECONFIG, NULL); /* save the config to disk */
+ if (error != 0)
+ perror("Can't save Vinum config");
+ make_devices();
+ listconfig();
+}
+
+/* Read vinum config from a disk */
+void
+vinum_read(int argc, char *argv[], char *arg0[])
+{
+ int error;
+ char buffer[BUFSIZE]; /* read config file in here */
+ struct _ioctl_reply *reply;
+ reply = (struct _ioctl_reply *) &buffer;
+
+ if (argc != 1) { /* wrong arg count */
+ fprintf(stderr, "Expecting 1 parameter, not %d\n", argc);
+ return;
+ }
+ strcpy(buffer, "read ");
+ strcat(buffer, argv[0]);
+ if (ioctl(superdev, VINUM_STARTCONFIG, NULL)) { /* can't get config? */
+ printf("Can't configure: %s (%d)\n", strerror(errno), errno);
+ return;
+ }
+ ioctl(superdev, VINUM_CREATE, &buffer);
+ if (reply->error != 0) { /* error in config */
+ fprintf(stdout, "** %s: %s\n", reply->msg, strerror(reply->error));
+ error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */
+ if (error != 0)
+ perror("Can't save Vinum config");
+ } else {
+ error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */
+ if (error != 0)
+ perror("Can't save Vinum config");
+ make_devices();
+ }
+}
+
+void
+vinum_volume(int argc, char *argv[], char *arg0[])
+{
+ int i;
+ char *line;
+ struct _ioctl_reply *reply;
+
+ line = arg0[0];
+ for (i = 0; i < argc; i++)
+ line[strlen(line)] = ' '; /* remove the blocks */
+ ioctl(superdev, VINUM_CREATE, line);
+ reply = (struct _ioctl_reply *) line;
+ if (reply->error != 0) /* error in config */
+ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error));
+}
+
+void
+vinum_plex(int argc, char *argv[], char *arg0[])
+{
+ int i;
+ char *line;
+ struct _ioctl_reply *reply;
+
+ line = arg0[0];
+ for (i = 0; i < argc; i++)
+ line[strlen(line)] = ' '; /* remove the blocks */
+ ioctl(superdev, VINUM_CREATE, line);
+ reply = (struct _ioctl_reply *) line;
+ if (reply->error != 0) /* error in config */
+ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error));
+}
+
+void
+vinum_sd(int argc, char *argv[], char *arg0[])
+{
+ int i;
+ char *line;
+ struct _ioctl_reply *reply;
+
+ line = arg0[0];
+ for (i = 0; i < argc; i++)
+ line[strlen(line)] = ' '; /* remove the blocks */
+ ioctl(superdev, VINUM_CREATE, line);
+ reply = (struct _ioctl_reply *) line;
+ if (reply->error != 0) /* error in config */
+ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error));
+}
+
+void
+vinum_drive(int argc, char *argv[], char *arg0[])
+{
+ int i;
+ char *line;
+ struct _ioctl_reply *reply;
+
+ line = arg0[0];
+ for (i = 0; i < argc; i++)
+ line[strlen(line)] = ' '; /* remove the blocks */
+ ioctl(superdev, VINUM_CREATE, line);
+ reply = (struct _ioctl_reply *) line;
+ if (reply->error != 0) /* error in config */
+ fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error));
+}
+
+#ifdef DEBUG
+void
+vinum_debug(int argc, char *argv[], char *arg0[])
+{
+ struct debuginfo info;
+
+ if (argc > 0) {
+ info.param = atoi(argv[0]);
+ info.changeit = 1;
+ } else {
+ info.changeit = 0;
+ sleep(2); /* give a chance to leave the window */
+ }
+ ioctl(superdev, VINUM_DEBUG, (caddr_t) & info);
+}
+#endif
+
+void
+vinum_modify(int argc, char *argv[], char *arg0[])
+{
+ fprintf(stderr, "Modify command is currently not implemented\n");
+}
+
+void
+vinum_set(int argc, char *argv[], char *arg0[])
+{
+ fprintf(stderr, "set is not implemented yet\n");
+}
+
+void
+vinum_rm(int argc, char *argv[], char *arg0[])
+{
+ int object;
+ struct _ioctl_reply reply;
+ struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
+
+ if (argc == 0) /* start everything */
+ fprintf(stderr, "what do you want to remove?\n");
+ else { /* start specified objects */
+ int index;
+ enum objecttype type;
+
+ for (index = 0; index < argc; index++) {
+ object = find_object(argv[index], &type); /* look for it */
+ if (type == invalid_object)
+ fprintf(stderr, "Can't find object: %s\n", argv[index]);
+ else {
+ message->index = object; /* pass object number */
+ message->type = type; /* and type of object */
+ message->force = force; /* do we want to force the operation? */
+ message->recurse = recurse; /* do we want to remove subordinates? */
+ ioctl(superdev, VINUM_REMOVE, message);
+ if (reply.error != 0) {
+ fprintf(stderr,
+ "Can't remove %s: %s (%d)\n",
+ argv[index],
+ reply.msg[0] ? reply.msg : strerror(reply.error),
+ reply.error);
+ } else if (verbose)
+ fprintf(stderr, "%s removed\n", argv[index]);
+ }
+ }
+ }
+}
+
+void
+vinum_resetconfig(int argc, char *argv[], char *arg0[])
+{
+ char reply[32];
+ int error;
+
+ printf(" WARNING! This command will completely wipe out your vinum 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")) /* changed his mind */
+ printf("\n No change\n");
+ else {
+ error = ioctl(superdev, VINUM_RESETCONFIG, NULL); /* trash config on disk */
+ if (error) {
+ if (errno == EBUSY)
+ fprintf(stderr, "Can't reset configuration: objects are in use\n");
+ else
+ perror("Can't find vinum config");
+ } else {
+ printf("\b Vinum configuration obliterated\n");
+ system("rm -rf " VINUM_DIR "/" "*"); /* remove the old /dev/vinum */
+ syslog(LOG_NOTICE | LOG_KERN, "configuration obliterated");
+ }
+ }
+}
+
+void
+vinum_init(int argc, char *argv[], char *arg0[])
+{
+ if (argc > 0) { /* initialize plexes */
+ int plexindex;
+ int sdno;
+ int plexno;
+ int plexfh = NULL; /* file handle for plex */
+ pid_t pid;
+ enum objecttype type; /* type returned */
+ struct _ioctl_reply reply;
+ struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
+ char filename[MAXPATHLEN]; /* create a file name here */
+
+ for (plexindex = 0; plexindex < argc; plexindex++) {
+ plexno = find_object(argv[plexindex], &type); /* find the object */
+ if (plexno < 0)
+ printf("Can't find %s\n", argv[plexindex]);
+ else if (type != plex_object) {
+ /* XXX Consider doing this for all plexes in
+ * a volume, etc. */
+ printf("%s is not a plex\n", argv[plexindex]);
+ break;
+ } else if (plex.state == plex_unallocated) /* not a real plex, */
+ printf("%s is not allocated, can't initialize\n", plex.name);
+ else {
+ sprintf(filename, VINUM_DIR "/plex/%s", argv[plexindex]);
+ if ((plexfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* got a plex, open it */
+ /* We don't actually write anything to the plex,
+ * since the system will try to format it. We open
+ * it to ensure that nobody else tries to open it
+ * while we initialize its subdisks */
+ fprintf(stderr, "can't open plex %s: %s\n", filename, strerror(errno));
+ return;
+ }
+ }
+ message->index = plexno; /* pass object number */
+ message->type = plex_object; /* and type of object */
+ message->state = object_initializing;
+ message->force = 1; /* insist */
+ ioctl(superdev, VINUM_SETSTATE, message);
+ if (reply.error) {
+ syslog(LOG_ERR | LOG_KERN,
+ "can't initialize %s: %s",
+ plex.name,
+ reply.msg[0] ? reply.msg : strerror(reply.error));
+ } else {
+ pid = fork();
+ if (pid == 0) { /* we're the child */
+ int failed = 0; /* set if a child dies badly */
+ int sdfh; /* and for subdisk */
+ char zeros[PLEXINITSIZE];
+ int count; /* write count */
+ long long offset; /* offset in subdisk */
+ long long sdsize; /* size of subdisk */
+
+ bzero(zeros, sizeof(zeros));
+ openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN);
+ for (sdno = 0; sdno < plex.subdisks; sdno++) { /* initialize each subdisk */
+ /* We already have the plex data in global
+ * plex from the call to find_object */
+ pid = fork(); /* into the background with you */
+ if (pid == 0) { /* I'm the child */
+ get_plex_sd_info(&sd, plexno, sdno);
+ sdsize = sd.sectors * DEV_BSIZE; /* size of subdisk in bytes */
+ sprintf(filename, VINUM_DIR "/sd/%s", sd.name);
+ setproctitle("initializing %s", filename); /* show what we're doing */
+ syslog(LOG_INFO | LOG_KERN, "initializing subdisk %s\n", filename);
+ if ((sdfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* no go */
+ syslog(LOG_ERR | LOG_KERN,
+ "can't open subdisk %s: %s\n",
+ filename,
+ strerror(errno));
+ exit(1);
+ }
+ for (offset = 0; offset < sdsize; offset += count) {
+ count = write(sdfh, zeros, PLEXINITSIZE); /* write a block */
+ if (count < 0) {
+ syslog(LOG_ERR | LOG_KERN,
+ "can't write subdisk %s: %s\n",
+ filename,
+ strerror(errno));
+ exit(1);
+ }
+ /* XXX Grrrr why doesn't this thing recognize EOF? */
+ else if (count == 0)
+ break;
+ }
+ syslog(LOG_INFO | LOG_KERN, "subdisk %s initialized\n", filename);
+ exit(0);
+ } else if (pid < 0) /* failure */
+ printf("couldn't fork for subdisk %d: %s", sdno, strerror(errno));
+ }
+ /* Now wait for them to complete */
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ int status;
+ pid = wait(&status);
+ if (WEXITSTATUS(status) != 0) { /* oh, oh */
+ printf("child %d exited with status 0x%x\n", pid, WEXITSTATUS(status));
+ failed++;
+ }
+ }
+ if (failed == 0) {
+ for (sdno = 0; sdno < plex.subdisks; sdno++) { /* bring the subdisks up */
+ get_plex_sd_info(&sd, plexno, sdno); /* get the SD info again */
+ message->index = sd.sdno; /* pass object number */
+ message->type = sd_object; /* and type of object */
+ message->state = object_up;
+ message->force = 1; /* insist */
+ ioctl(superdev, VINUM_SETSTATE, message);
+ }
+ syslog(LOG_INFO | LOG_KERN, "plex %s initialized\n", plex.name);
+ } else
+ syslog(LOG_ERR | LOG_KERN, "couldn't initialize plex %s, %d processes died\n",
+ plex.name,
+ failed);
+ exit(0);
+ } else
+ close(plexfh); /* we don't need this any more */
+ }
+ }
+ }
+}
+
+void
+vinum_start(int argc, char *argv[], char *arg0[])
+{
+ int object;
+ struct _ioctl_reply reply;
+ struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
+
+ if (argc == 0) /* start everything */
+ fprintf(stderr, "start must have an argument\n");
+ else { /* start specified objects */
+ int index;
+ enum objecttype type;
+
+ for (index = 0; index < argc; index++) {
+ object = find_object(argv[index], &type); /* look for it */
+ if (type == invalid_object)
+ fprintf(stderr, "Can't find object: %s\n", argv[index]);
+ else {
+ message->index = object; /* pass object number */
+ message->type = type; /* and type of object */
+ message->state = object_up;
+ /* XXX Kludge until we get the kernelland
+ * config stuff rewritten:
+ * Take all subdisks down, then up. */
+ if (message->type == plex_object) { /* it's a plex */
+ struct plex plex;
+ struct sd sd;
+ int sdno;
+ struct _ioctl_reply sreply;
+ struct vinum_ioctl_msg *smessage = (struct vinum_ioctl_msg *) &sreply;
+
+ get_plex_info(&plex, message->index);
+ if (plex.state != plex_up) {
+ /* And when they were down, they were down */
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ get_plex_sd_info(&sd, plex.plexno, sdno);
+ smessage->type = sd_object;
+ smessage->state = object_down;
+ smessage->force = 1;
+ smessage->index = sd.sdno;
+ ioctl(superdev, VINUM_SETSTATE, smessage);
+ if (sreply.error != 0) {
+ fprintf(stderr,
+ "Can't stop %s: %s (%d)\n",
+ sd.name,
+ sreply.msg[0] ? sreply.msg : strerror(sreply.error),
+ sreply.error);
+ }
+ }
+
+ /* And when they were up, they were up */
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ get_plex_sd_info(&sd, plex.plexno, sdno);
+ smessage->type = sd_object;
+ smessage->state = object_up;
+ smessage->force = 1;
+ smessage->index = sd.sdno;
+ ioctl(superdev, VINUM_SETSTATE, smessage);
+ if (sreply.error != 0) {
+ fprintf(stderr,
+ "Can't stop %s: %s (%d)\n",
+ sd.name,
+ sreply.msg[0] ? sreply.msg : strerror(sreply.error),
+ sreply.error);
+ }
+ }
+ }
+ }
+ /* XXX End kludge until we get the kernelland
+ * config stuff rewritten */
+ ioctl(superdev, VINUM_SETSTATE, message);
+ if (reply.error != 0) {
+ if ((reply.error == EAGAIN) /* we're reviving */
+ &&(type == plex_object))
+ continue_revive(object);
+ else
+ fprintf(stderr,
+ "Can't start %s: %s (%d)\n",
+ argv[index],
+ reply.msg[0] ? reply.msg : strerror(reply.error),
+ reply.error);
+ }
+ if (Verbose)
+ vinum_li(object, type);
+ }
+ }
+ }
+}
+
+void
+vinum_stop(int argc, char *argv[], char *arg0[])
+{
+ int object;
+ struct _ioctl_reply reply;
+ struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
+
+ message->force = force; /* should we force the transition? */
+ if (argc == 0) /* stop everything */
+ fprintf(stderr, "stop must have an argument\n");
+ else { /* stop specified objects */
+ int i;
+ enum objecttype type;
+
+ for (i = 0; i < argc; i++) {
+ object = find_object(argv[i], &type); /* look for it */
+ if (type == invalid_object)
+ fprintf(stderr, "Can't find object: %s\n", argv[i]);
+ else {
+ message->index = object; /* pass object number */
+ message->type = type; /* and type of object */
+ message->state = object_down;
+ ioctl(superdev, VINUM_SETSTATE, message);
+ if (reply.error != 0)
+ fprintf(stderr,
+ "Can't stop %s: %s (%d)\n",
+ argv[i],
+ reply.msg[0] ? reply.msg : strerror(reply.error),
+ reply.error);
+ if (Verbose)
+ vinum_li(object, type);
+ }
+ }
+ }
+}
+
+void
+vinum_label(int argc, char *argv[], char *arg0[])
+{
+ int object;
+ struct _ioctl_reply reply;
+ int *message = (int *) &reply;
+
+ if (argc == 0) /* start everything */
+ fprintf(stderr, "label: please specify one or more volume names\n");
+ else { /* start specified objects */
+ int i;
+ enum objecttype type;
+
+ for (i = 0; i < argc; i++) {
+ object = find_object(argv[i], &type); /* look for it */
+ if (type == invalid_object)
+ fprintf(stderr, "Can't find object: %s\n", argv[i]);
+ else if (type != volume_object) /* it exists, but it isn't a volume */
+ fprintf(stderr, "%s is not a volume\n", argv[i]);
+ else {
+ message[0] = object; /* pass object number */
+ ioctl(superdev, VINUM_LABEL, message);
+ if (reply.error != 0)
+ fprintf(stderr,
+ "Can't label %s: %s (%d)\n",
+ argv[i],
+ reply.msg[0] ? reply.msg : strerror(reply.error),
+ reply.error);
+ if (Verbose)
+ vinum_li(object, type);
+ }
+ }
+ }
+}
+
+void
+reset_volume_stats(int volno, int recurse)
+{
+ struct vinum_ioctl_msg msg;
+ struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg;
+
+ msg.index = volno;
+ msg.type = volume_object;
+ /* XXX get these numbers right if we ever
+ * actually return errors */
+ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) {
+ fprintf(stderr, "Can't reset stats for volume %d: %s\n", volno, reply->msg);
+ longjmp(command_fail, -1);
+ } else if (recurse) {
+ struct volume vol;
+ int plexno;
+
+ get_volume_info(&vol, volno);
+ for (plexno = 0; plexno < vol.plexes; plexno++)
+ reset_plex_stats(vol.plex[plexno], recurse);
+ }
+}
+
+void
+reset_plex_stats(int plexno, int recurse)
+{
+ struct vinum_ioctl_msg msg;
+ struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg;
+
+ msg.index = plexno;
+ msg.type = plex_object;
+ /* XXX get these numbers right if we ever
+ * actually return errors */
+ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) {
+ fprintf(stderr, "Can't reset stats for plex %d: %s\n", plexno, reply->msg);
+ longjmp(command_fail, -1);
+ } else if (recurse) {
+ struct plex plex;
+ struct sd sd;
+ int sdno;
+
+ get_plex_info(&plex, plexno);
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ get_plex_sd_info(&sd, plex.plexno, sdno);
+ reset_sd_stats(sd.sdno, recurse);
+ }
+ }
+}
+
+void
+reset_sd_stats(int sdno, int recurse)
+{
+ struct vinum_ioctl_msg msg;
+ struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg;
+
+ msg.index = sdno;
+ msg.type = sd_object;
+ /* XXX get these numbers right if we ever
+ * actually return errors */
+ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) {
+ fprintf(stderr, "Can't reset stats for subdisk %d: %s\n", sdno, reply->msg);
+ longjmp(command_fail, -1);
+ } else if (recurse)
+ reset_drive_stats(sd.driveno);
+}
+
+void
+reset_drive_stats(int driveno)
+{
+ struct vinum_ioctl_msg msg;
+ struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg;
+
+ msg.index = driveno;
+ msg.type = drive_object;
+ /* XXX get these numbers right if we ever
+ * actually return errors */
+ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) {
+ fprintf(stderr, "Can't reset stats for drive %d: %s\n", driveno, reply->msg);
+ longjmp(command_fail, -1);
+ }
+}
+
+void
+vinum_resetstats(int argc, char *argv[], char *argv0[])
+{
+ int i;
+ int objno;
+ enum objecttype type;
+
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ if (argc == 0) {
+ for (objno = 0; objno < vinum_conf.volumes_used; objno++)
+ reset_volume_stats(objno, 1); /* clear everything recursively */
+ } else {
+ for (i = 0; i < argc; i++) {
+ objno = find_object(argv[i], &type);
+ if (objno >= 0) { /* not invalid */
+ switch (type) {
+ case drive_object:
+ reset_drive_stats(objno);
+ break;
+
+ case sd_object:
+ reset_sd_stats(objno, recurse);
+ break;
+
+ case plex_object:
+ reset_plex_stats(objno, recurse);
+ break;
+
+ case volume_object:
+ reset_volume_stats(objno, recurse);
+ break;
+
+ case invalid_object: /* can't get this */
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* Attach a subdisk to a plex, or a plex to a volume.
+ * attach subdisk plex [offset] [rename]
+ * attach plex volume [rename]
+ */
+void
+vinum_attach(int argc, char *argv[], char *argv0[])
+{
+ int i;
+ enum objecttype supertype;
+ struct vinum_ioctl_msg msg;
+ struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg;
+ const char *objname = argv[0];
+ const char *supername = argv[1];
+ int sdno;
+ int plexno = -1;
+ char newname[MAXNAME + 8];
+ int rename = 0; /* set if we want to rename the object */
+
+ if ((argc < 2)
+ || (argc > 4)) {
+ fprintf(stderr,
+ "Usage: \tattach <subdisk> <plex> [rename] [<plexoffset>]\n"
+ "\tattach <plex> <volume> [rename]\n");
+ return;
+ }
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ msg.index = find_object(objname, &msg.type); /* find the object to attach */
+ msg.otherobject = find_object(supername, &supertype); /* and the object to attach to */
+ msg.force = force; /* did we specify the use of force? */
+ msg.recurse = recurse;
+ msg.offset = -1; /* and no offset */
+
+ for (i = 2; i < argc; i++) {
+ if (!strcmp(argv[i], "rename")) {
+ rename = 1;
+ msg.rename = 1; /* do renaming */
+ } else if (!isdigit(argv[i][0])) { /* not an offset */
+ fprintf(stderr, "Unknown attribute: %s\n", supername);
+ return;
+ } else
+ msg.offset = sizespec(argv[i]);
+ }
+
+ switch (msg.type) {
+ case sd_object:
+ if (supertype != plex_object) { /* huh? */
+ fprintf(stderr, "%s can only be attached to a plex\n", objname);
+ return;
+ }
+ get_plex_info(&plex, supertype);
+ if (plex.organization != plex_concat) { /* not a cat plex, */
+ fprintf(stderr, "Can't attach subdisks to a %s plex\n", plex_org(plex.organization));
+ return;
+ }
+ break;
+
+ case plex_object:
+ if (supertype != volume_object) { /* huh? */
+ fprintf(stderr, "%s can only be attached to a volume\n", objname);
+ return;
+ }
+ plexno = msg.index; /* note the plex number, we'll need it again */
+ break;
+
+ case volume_object:
+ case drive_object:
+ fprintf(stderr, "Can only attach subdisks and plexes\n");
+ return;
+
+ default:
+ fprintf(stderr, "%s is not a Vinum object\n", objname);
+ return;
+ }
+
+ ioctl(superdev, VINUM_ATTACH, &msg);
+ if (reply->error != 0) {
+ if (reply->error == EAGAIN) /* reviving */
+ continue_revive(plexno); /* continue the revive */
+ else
+ fprintf(stderr,
+ "Can't attach %s to %s: %s (%d)\n",
+ objname,
+ supername,
+ reply->msg[0] ? reply->msg : strerror(reply->error),
+ reply->error);
+ }
+ if (rename) {
+ struct sd;
+ struct plex;
+ struct volume;
+
+ /* we've overwritten msg with the
+ * ioctl reply, start again */
+ msg.index = find_object(objname, &msg.type); /* find the object to rename */
+ switch (msg.type) {
+ case sd_object:
+ get_sd_info(&sd, msg.index);
+ get_plex_info(&plex, sd.plexno);
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ if (plex.sdnos[sdno] == msg.index) /* found our subdisk */
+ break;
+ }
+ sprintf(newname, "%s.s%d", plex.name, sdno);
+ vinum_rename_2(sd.name, newname);
+ break;
+
+ case plex_object:
+ get_plex_info(&plex, msg.index);
+ get_volume_info(&vol, plex.volno);
+ for (plexno = 0; plexno < vol.plexes; plexno++) {
+ if (vol.plex[plexno] == msg.index) /* found our subdisk */
+ break;
+ }
+ sprintf(newname, "%s.p%d", vol.name, plexno);
+ vinum_rename_2(plex.name, newname); /* this may recurse */
+ break;
+
+ default: /* can't get here */
+ }
+ }
+}
+
+/* Detach a subdisk from a plex, or a plex from a volume.
+ * detach subdisk plex [rename]
+ * detach plex volume [rename]
+ */
+void
+vinum_detach(int argc, char *argv[], char *argv0[])
+{
+ struct vinum_ioctl_msg msg;
+ struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg;
+
+ if ((argc < 1)
+ || (argc > 2)) {
+ fprintf(stderr,
+ "Usage: \tdetach <subdisk> [rename]\n"
+ "\tdetach <plex> [rename]\n");
+ return;
+ }
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ msg.index = find_object(argv[0], &msg.type); /* find the object to detach */
+ msg.force = force; /* did we specify the use of force? */
+ msg.rename = 0; /* don't specify new name */
+ msg.recurse = recurse; /* but recurse if we have to */
+
+ /* XXX are we going to keep this?
+ * Don't document it yet, since the
+ * kernel side of things doesn't
+ * implement it */
+ if (argc == 2) {
+ if (!strcmp(argv[1], "rename"))
+ msg.rename = 1; /* do renaming */
+ else {
+ fprintf(stderr, "Unknown attribute: %s\n", argv[1]);
+ return;
+ }
+ }
+ if ((msg.type != sd_object)
+ && (msg.type != plex_object)) {
+ fprintf(stderr, "Can only detach subdisks and plexes\n");
+ return;
+ }
+ ioctl(superdev, VINUM_DETACH, &msg);
+ if (reply->error != 0)
+ fprintf(stderr,
+ "Can't detach %s: %s (%d)\n",
+ argv[0],
+ reply->msg[0] ? reply->msg : strerror(reply->error),
+ reply->error);
+}
+
+static void
+dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen)
+{
+ struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
+
+ if (strlen(name) > maxlen) {
+ fprintf(stderr, "%s is too long\n", name);
+ return;
+ }
+ strcpy(msg->newname, name);
+ ioctl(superdev, VINUM_RENAME, msg);
+ if (reply->error != 0)
+ fprintf(stderr,
+ "Can't rename %s to %s: %s (%d)\n",
+ oldname,
+ name,
+ reply->msg[0] ? reply->msg : strerror(reply->error),
+ reply->error);
+}
+
+/* Rename an object:
+ * rename <object> "newname"
+ */
+void
+vinum_rename_2(char *oldname, char *newname)
+{
+ struct vinum_rename_msg msg;
+ int volno;
+ int plexno;
+
+ msg.index = find_object(oldname, &msg.type); /* find the object to rename */
+ msg.recurse = recurse;
+
+ /* Ugh. Determine how long the name may be */
+ switch (msg.type) {
+ case drive_object:
+ dorename(&msg, oldname, newname, MAXDRIVENAME);
+ break;
+
+ case sd_object:
+ dorename(&msg, oldname, newname, MAXSDNAME);
+ break;
+
+ case plex_object:
+ plexno = msg.index;
+ dorename(&msg, oldname, newname, MAXPLEXNAME);
+ if (recurse) {
+ int sdno;
+
+ get_plex_info(&plex, plexno); /* find out who we are */
+ msg.type = sd_object;
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ char sdname[MAXPLEXNAME + 8];
+
+ get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */
+ sprintf(sdname, "%s.s%d", newname, sdno);
+ msg.index = sd.sdno; /* number of the subdisk */
+ dorename(&msg, sd.name, sdname, MAXSDNAME);
+ }
+ }
+ break;
+
+ case volume_object:
+ volno = msg.index;
+ dorename(&msg, oldname, newname, MAXVOLNAME);
+ if (recurse) {
+ int sdno;
+ int plexno;
+
+ get_volume_info(&vol, volno); /* find out who we are */
+ for (plexno = 0; plexno < vol.plexes; plexno++) {
+ char plexname[MAXVOLNAME + 8];
+
+ msg.type = plex_object;
+ sprintf(plexname, "%s.p%d", newname, plexno);
+ msg.index = vol.plex[plexno]; /* number of the plex */
+ dorename(&msg, plex.name, plexname, MAXPLEXNAME);
+ get_plex_info(&plex, vol.plex[plexno]); /* find out who we are */
+ msg.type = sd_object;
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ char sdname[MAXPLEXNAME + 8];
+
+ get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */
+ sprintf(sdname, "%s.s%d", plexname, sdno);
+ msg.index = sd.sdno; /* number of the subdisk */
+ dorename(&msg, sd.name, sdname, MAXSDNAME);
+ }
+ }
+ }
+ break;
+
+ default:
+ fprintf(stderr, "%s is not a Vinum object\n", oldname);
+ return;
+ }
+}
+
+void
+vinum_rename(int argc, char *argv[], char *argv0[])
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: \trename <object> <new name>\n");
+ return;
+ }
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ vinum_rename_2(argv[0], argv[1]);
+}
+
+#ifdef COMPLETE
+/* Replace an object. Syntax and semantics TBD */
+void
+vinum_replace(int argc, char *argv[], char *argv0[])
+{
+ int maxlen;
+ struct vinum_rename_msg msg;
+ struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: \trename <object> <new name>\n");
+ return;
+ }
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ fprintf(stderr, "Not implemented yet\n");
+}
+#endif
+/* Replace an object. Syntax and semantics TBD */
+void
+vinum_replace(int argc, char *argv[], char *argv0[])
+{
+ fprintf(stderr, "replace function not implemented yet\n");
+}
diff --git a/sbin/vinum/list.c b/sbin/vinum/list.c
new file mode 100644
index 0000000..4080748
--- /dev/null
+++ b/sbin/vinum/list.c
@@ -0,0 +1,743 @@
+/* list.c: vinum interface program, list routines
+ */
+/*-
+ * 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.
+ *
+ * $Id: list.c,v 1.12 1998/08/10 05:15:06 grog Exp grog $
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include "vinumhdr.h"
+#include "vext.h"
+
+/* Take a size in sectors and return a pointer to a
+ * string which represents the size best.
+ * If lj is != 0, return left justified, otherwise
+ * in a fixed 10 character field suitable for
+ * columnar printing.
+ *
+ * Note this uses a static string: it's only intended to
+ * be used immediately for printing */
+char *
+roughlength(long long bytes, int lj)
+{
+ static char description[16];
+
+ if (bytes > (long long) MEGABYTE * 10000) /* gigabytes */
+ sprintf(description, lj ? "%d GB" : "%10d GB", bytes / GIGABYTE);
+ else if (bytes > KILOBYTE * 10000) /* megabytes */
+ sprintf(description, lj ? "%d MB" : "%10d MB", bytes / MEGABYTE);
+ else if (bytes > 10000) /* kilobytes */
+ sprintf(description, lj ? "%d kB" : "%10d kB", bytes / KILOBYTE);
+ else /* bytes */
+ sprintf(description, lj ? "%d B" : "%10d B", bytes);
+ return description;
+}
+
+void
+vinum_list(int argc, char *argv[], char *argv0[])
+{
+ int object;
+ int i;
+ enum objecttype type;
+
+ if (argc == 0)
+ listconfig(); /* list everything */
+ else
+ for (i = 0; i < argc; i++) {
+ object = find_object(argv[i], &type); /* look for it */
+ if (vinum_li(object, type))
+ fprintf(stderr, "Can't find object: %s\n", argv[i]);
+ }
+}
+
+/* List an object */
+int
+vinum_li(int object, enum objecttype type)
+{
+ switch (type) {
+ case drive_object:
+ vinum_ldi(object, recurse);
+ break;
+
+ case sd_object:
+ vinum_lsi(object, recurse);
+ break;
+
+ case plex_object:
+ vinum_lpi(object, recurse);
+ break;
+
+ case volume_object:
+ vinum_lvi(object, recurse);
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+void
+vinum_ldi(int driveno, int recurse)
+{
+ get_drive_info(&drive, driveno);
+ if (drive.state != drive_unallocated) {
+ if (verbose) {
+ printf("Drive %s:\tDevice %s\n",
+ drive.label.name,
+ drive.devicename);
+ printf("\t\tCreated on %s at %s",
+ drive.label.sysname,
+ ctime(&drive.label.date_of_birth.tv_sec));
+ printf("\t\tConfig last updated %s", /* care: \n at end */
+ ctime(&drive.label.last_update.tv_sec));
+ printf("\t\tSize: %16qd bytes (%qd MB)\n\t\tUsed: %16qd bytes (%qd MB)\n"
+ "\t\tAvailable: %11qd bytes (%d MB)\n",
+ drive.label.drive_size, /* bytes used */
+ (drive.label.drive_size / MEGABYTE),
+ drive.label.drive_size - drive.sectors_available * DEV_BSIZE,
+ (drive.label.drive_size - drive.sectors_available * DEV_BSIZE) / MEGABYTE,
+ drive.sectors_available * DEV_BSIZE,
+ (int) (drive.sectors_available * DEV_BSIZE / MEGABYTE));
+ printf("\t\tState: %s\n", drive_state(drive.state));
+ if (drive.lasterror != 0)
+ printf("\t\tLast error: %s\n", strerror(drive.lasterror));
+ else
+ printf("\t\tLast error: none\n");
+ if (Verbose) { /* print the free list */
+ int fe; /* freelist entry */
+ struct drive_freelist freelist;
+ struct ferq { /* request to pass to ioctl */
+ int driveno;
+ int fe;
+ } *ferq = (struct ferq *) &freelist;
+
+ printf("\t\tFree list contains %d entries:\n\t\t Offset\t Size\n",
+ drive.freelist_entries);
+ for (fe = 0; fe < drive.freelist_entries; fe++) {
+ ferq->driveno = drive.driveno;
+ ferq->fe = fe;
+ if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) {
+ fprintf(stderr,
+ "Can't get free list element %d: %s\n",
+ fe,
+ strerror(errno));
+ longjmp(command_fail, -1);
+ }
+ printf("\t\t%9qd\t%9ld\n", freelist.offset, freelist.sectors);
+ }
+ }
+ } else
+ printf("D %-21s State: %s\tDevice %s\n",
+ drive.label.name,
+ drive_state(drive.state),
+ drive.devicename);
+ if (stats) {
+ printf("\t\tReads: \t%16qd\n\t\tBytes read:\t%16qd (%s)\n",
+ drive.reads,
+ drive.bytes_read,
+ roughlength(drive.bytes_read, 1));
+ if (drive.reads != 0)
+ printf("\t\tAverage read:\t%16qd bytes\n", drive.bytes_read / drive.reads);
+ printf("\t\tWrites: \t%16qd\n\t\tBytes written:\t%16qd (%s)\n",
+ drive.writes,
+ drive.bytes_written,
+ roughlength(drive.bytes_written, 1));
+ if (drive.writes != 0)
+ printf("\t\tAverage write:\t%16qd bytes\n",
+ drive.bytes_written / drive.writes);
+ }
+ }
+}
+
+void
+vinum_ld(int argc, char *argv[], char *argv0[])
+{
+ int i;
+ int driveno;
+ enum objecttype type;
+
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ if (argc == 0) {
+ for (driveno = 0; driveno < vinum_conf.drives_used; driveno++)
+ vinum_ldi(driveno, recurse);
+ } else {
+ for (i = 0; i < argc; i++) {
+ driveno = find_object(argv[i], &type);
+ if (type == drive_object)
+ vinum_ldi(driveno, recurse);
+ else
+ fprintf(stderr, "%s is not a drive\n", argv[i]);
+ }
+ }
+}
+
+void
+vinum_lvi(int volno, int recurse)
+{
+ get_volume_info(&vol, volno);
+ if (vol.state != volume_unallocated) {
+ if (verbose) {
+ printf("Volume %s:\tSize: %qd bytes (%qd MB)\n"
+ "\t\tState: %s\n\t\tOpen by PID: %d\n\t\tFlags: %s%s\n",
+ vol.name,
+ ((long long) vol.size) * DEV_BSIZE,
+ ((long long) vol.size) * DEV_BSIZE / MEGABYTE,
+ volume_state(vol.state),
+ vol.pid,
+ (vol.flags & VF_WRITETHROUGH ? "writethrough " : ""),
+ (vol.flags & VF_RAW ? "raw" : ""));
+ printf("\t\t%d plexes\n\t\tRead policy: ", vol.plexes);
+ if (vol.preferred_plex < 0) /* round robin */
+ printf("round robin\n");
+ else {
+ get_plex_info(&plex, vol.plex[vol.preferred_plex]);
+ printf("plex %d (%s)\n", vol.preferred_plex, plex.name);
+ }
+ } else /* brief */
+ printf("V %-21s State: %s\tPlexes: %7d\tSize: %s\n",
+ vol.name,
+ volume_state(vol.state),
+ vol.plexes,
+ roughlength(vol.size << DEV_BSHIFT, 0));
+ if (stats) {
+ printf("\t\tReads: \t%16qd\n\t\tRecovered:\t%16qd\n\t\tBytes read:\t%16qd (%s)\n",
+ vol.reads,
+ vol.recovered_reads,
+ vol.bytes_read,
+ roughlength(vol.bytes_read, 1));
+ if (vol.reads != 0)
+ printf("\t\tAverage read:\t%16qd bytes\n", vol.bytes_read / vol.reads);
+ printf("\t\tWrites: \t%16qd\n\t\tBytes written:\t%16qd (%s)\n",
+ vol.writes,
+ vol.bytes_written,
+ roughlength(vol.bytes_written, 1));
+ if (vol.writes != 0)
+ printf("\t\tAverage write:\t%16qd bytes\n",
+ vol.bytes_written / vol.writes);
+ printf("\t\tActive requests:\t%8d\n", vol.active);
+ }
+ if (vol.plexes > 0) {
+ int plexno;
+ if (Verbose) { /* brief list */
+ for (plexno = 0; plexno < vol.plexes; plexno++) {
+ get_plex_info(&plex, vol.plex[plexno]);
+ /* Just a brief summary here */
+ printf("\t\tPlex %2d:\t%s\t(%s), %s\n",
+ plexno,
+ plex.name,
+ plex_org(plex.organization),
+ roughlength(plex.length << DEV_BSHIFT, 0));
+ }
+ }
+ if (recurse) {
+ for (plexno = 0; plexno < vol.plexes; plexno++)
+ vinum_lpi(vol.plex[plexno], 0); /* first show the plexes */
+ for (plexno = 0; plexno < vol.plexes; plexno++) { /* then the subdisks */
+ get_plex_info(&plex, vol.plex[plexno]);
+ if (plex.subdisks > 0) {
+ int sdno;
+
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ get_plex_sd_info(&sd, vol.plex[plexno], sdno);
+ vinum_lsi(sd.sdno, 0);
+ }
+ }
+ }
+ if (verbose == 0) /* not verbose, but recursive */
+ printf("\n"); /* leave a line at the end of each hierarchy */
+ }
+ }
+ }
+}
+
+void
+vinum_lv(int argc, char *argv[], char *argv0[])
+{
+ int i;
+ int volno;
+ enum objecttype type;
+
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ if (argc == 0)
+ for (volno = 0; volno < vinum_conf.volumes_used; volno++)
+ vinum_lvi(volno, recurse);
+ else {
+ for (i = 0; i < argc; i++) {
+ volno = find_object(argv[i], &type);
+ if (type == volume_object)
+ vinum_lvi(volno, recurse);
+ else
+ fprintf(stderr, "%s is not a volume\n", argv[i]);
+ }
+ }
+}
+
+void
+vinum_lpi(int plexno, int recurse)
+{
+ get_plex_info(&plex, plexno);
+ if (plex.state != plex_unallocated) {
+ if (verbose) {
+ printf("Plex %s:\tSize:\t%9qd bytes (%qd MB)\n\t\tSubdisks: %8d\n",
+ plex.name,
+ (long long) plex.length * DEV_BSIZE,
+ (long long) plex.length * DEV_BSIZE / MEGABYTE,
+ plex.subdisks);
+ printf("\t\tState: %s\n\t\tOrganization: %s",
+ plex_state(plex.state),
+ plex_org(plex.organization));
+ if ((plex.organization == plex_striped)
+ || (plex.organization == plex_raid5))
+ printf("\tStripe size: %s\n", roughlength(plex.stripesize * DEV_BSIZE, 1));
+ else
+ printf("\n");
+ if (plex.volno >= 0) {
+ get_volume_info(&vol, plex.volno);
+ printf("\t\tPart of volume %s\n", vol.name);
+ }
+ if (plex.state == plex_reviving) {
+ printf("\t\tRevive pointer:\t\t%s\n",
+ roughlength(plex.revived << DEV_BSHIFT, 0));
+ printf("\t\tRevive blocksize:\t%s\n"
+ "\t\tRevive interval:\t%10d seconds\n",
+ roughlength(plex.revive_blocksize << DEV_BSHIFT, 0),
+ plex.revive_interval);
+ }
+ if (Verbose) { /* show the unmapped and defective parts */
+ int re; /* freelist entry */
+ struct plexregion region;
+ struct rerq { /* request to pass to ioctl */
+ int plexno; /* plex for the request */
+ int re; /* region */
+ } *rerq = (struct rerq *) &region;
+
+ if (plex.unmapped_regions) {
+ printf("\t\tPlex contains %d unmapped regions:\n\t\t Offset\t Size\n",
+ plex.unmapped_regions);
+ for (re = 0; re < plex.unmapped_regions; re++) {
+ rerq->plexno = plex.plexno;
+ rerq->re = re;
+ if (ioctl(superdev, VINUM_GETUNMAPPED, &region) < 0) {
+ fprintf(stderr,
+ "Can't get unmapped region %d: %s\n",
+ re,
+ strerror(errno));
+ longjmp(command_fail, -1);
+ }
+ printf("\t\t%9qd\t%9qd\n", region.offset, region.length);
+ }
+ }
+ if (plex.defective_regions) {
+ printf("\t\tPlex contains %d defective regions:\n\t\t Offset\t Size\n",
+ plex.defective_regions);
+ for (re = 0; re < plex.defective_regions; re++) {
+ rerq->plexno = plex.plexno;
+ rerq->re = re;
+ if (ioctl(superdev, VINUM_GETDEFECTIVE, &region) < 0) {
+ fprintf(stderr,
+ "Can't get defective region %d: %s\n",
+ re,
+ strerror(errno));
+ longjmp(command_fail, -1);
+ }
+ printf("\t\t%9qd\t%9qd\n", region.offset, region.length);
+ }
+ }
+ }
+ } else {
+ char *org = ""; /* organization */
+
+ switch (plex.organization) {
+ case plex_disorg: /* disorganized */
+ org = "??";
+ break;
+ case plex_concat: /* concatenated plex */
+ org = "C";
+ break;
+ case plex_striped: /* striped plex */
+ org = "S";
+ break;
+ case plex_raid5: /* RAID5 plex */
+ org = "R5";
+ break;
+ }
+ printf("P %-18s %2s State: %s\tSubdisks: %5d\tSize: %s\n",
+ plex.name,
+ org,
+ plex_state(plex.state),
+ plex.subdisks,
+ roughlength(plex.length << DEV_BSHIFT, 0));
+ }
+ if (stats) {
+ printf("\t\tReads: \t%16qd\n\t\tBytes read:\t%16qd (%s)\n",
+ plex.reads,
+ plex.bytes_read,
+ roughlength(plex.bytes_read, 1));
+ if (plex.reads != 0)
+ printf("\t\tAverage read:\t%16qd bytes\n", plex.bytes_read / plex.reads);
+ printf("\t\tWrites: \t%16qd\n\t\tBytes written:\t%16qd (%s)\n",
+ plex.writes,
+ plex.bytes_written,
+ roughlength(plex.bytes_written, 1));
+ if (plex.writes != 0)
+ printf("\t\tAverage write:\t%16qd bytes\n",
+ plex.bytes_written / plex.writes);
+ if ((plex.organization == plex_striped)
+ || (plex.organization == plex_raid5))
+ printf("\t\tMultiblock:\t%16qd\n"
+ "\t\tMultistripe:\t%16qd\n",
+ plex.multiblock,
+ plex.multistripe);
+ }
+ if (plex.subdisks > 0) {
+ int sdno;
+
+ if (Verbose) {
+ printf("\n");
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ get_plex_sd_info(&sd, plexno, sdno);
+ printf("\t\tSubdisk %d:\t%s\n\t\t state: %s\tsize %11qd (%qd MB)\n",
+ sdno,
+ sd.name,
+ sd_state(sd.state),
+ (long long) sd.sectors * DEV_BSIZE,
+ (long long) sd.sectors * DEV_BSIZE / MEGABYTE);
+ if (plex.organization == plex_concat)
+ printf("\t\t\toffset %9ld (0x%lx)\n",
+ (long) sd.plexoffset,
+ (long) sd.plexoffset);
+ }
+ }
+ if (recurse)
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ get_plex_sd_info(&sd, plexno, sdno);
+ vinum_lsi(sd.sdno, 0);
+ }
+ }
+ }
+}
+
+void
+vinum_lp(int argc, char *argv[], char *argv0[])
+{
+ int i;
+ int plexno;
+ enum objecttype type;
+
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ if (argc == 0) {
+ for (plexno = 0; plexno < vinum_conf.plexes_used; plexno++)
+ vinum_lpi(plexno, recurse);
+ } else {
+ for (i = 0; i < argc; i++) {
+ plexno = find_object(argv[i], &type);
+ if (type == plex_object)
+ vinum_lpi(plexno, recurse);
+ else
+ fprintf(stderr, "%s is not a plex\n", argv[i]);
+ }
+ }
+}
+
+void
+vinum_lsi(int sdno, int recurse)
+{
+ get_sd_info(&sd, sdno);
+ if (sd.state != sd_unallocated) {
+ if (verbose) {
+ printf("Subdisk %s:\n\t\tSize: %16qd bytes (%qd MB)\n\t\tState: %s\n",
+ sd.name,
+ (long long) sd.sectors * DEV_BSIZE,
+ (long long) sd.sectors / (MEGABYTE / DEV_BSIZE),
+ sd_state(sd.state));
+ if (sd.plexno >= 0) {
+ get_plex_info(&plex, sd.plexno);
+ printf("\t\tPlex %s", plex.name);
+ if (plex.organization == plex_concat)
+ printf(" at offset %qd\n", (long long) sd.plexoffset * DEV_BSIZE);
+ else
+ printf("\n");
+ }
+ } else {
+ printf("S %-21s State: %s\tPO: %s ",
+ sd.name,
+ sd_state(sd.state),
+ &(roughlength(sd.plexoffset << DEV_BSHIFT, 0))[2]); /* what a kludge! */
+ printf("Size: %s\n",
+ roughlength(sd.sectors << DEV_BSHIFT, 0));
+ }
+ if (stats) {
+ printf("\t\tReads: \t%16qd\n\t\tBytes read:\t%16qd (%s)\n",
+ sd.reads,
+ sd.bytes_read,
+ roughlength(sd.bytes_read, 1));
+ if (sd.reads != 0)
+ printf("\t\tAverage read:\t%16qd bytes\n", sd.bytes_read / sd.reads);
+ printf("\t\tWrites: \t%16qd\n\t\tBytes written:\t%16qd (%s)\n",
+ sd.writes,
+ sd.bytes_written,
+ roughlength(sd.bytes_written, 1));
+ if (sd.writes != 0)
+ printf("\t\tAverage write:\t%16qd bytes\n",
+ sd.bytes_written / sd.writes);
+ }
+ if (Verbose) {
+ get_drive_info(&drive, sd.driveno);
+ printf("\t\tDrive %15s\n\t\t\tDevice %-15s\n",
+ drive.label.name,
+ drive.devicename);
+ if (sd.driveoffset < 0)
+ printf("\t\t\tDrive offset\t *none*\n");
+ else
+ printf("\t\t\tDrive offset\t%9ld\n", (long) sd.driveoffset * DEV_BSIZE);
+ }
+ if (recurse)
+ vinum_ldi(sd.driveno, recurse);
+ if (verbose)
+ printf("\n"); /* make it more readable */
+ }
+}
+
+void
+vinum_ls(int argc, char *argv[], char *argv0[])
+{
+ int i;
+ int sdno;
+
+ /* Structures to read kernel data into */
+ struct _vinum_conf vinum_conf;
+ enum objecttype type;
+
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ if (argc == 0) {
+ for (sdno = 0; sdno < vinum_conf.subdisks_used; sdno++)
+ vinum_lsi(sdno, recurse);
+ } else { /* specific subdisks */
+ for (i = 0; i < argc; i++) {
+ sdno = find_object(argv[i], &type);
+ if (type == sd_object)
+ vinum_lsi(sdno, recurse);
+ else
+ fprintf(stderr, "%s is not a subdisk\n", argv[i]);
+ }
+ }
+}
+
+
+/* List the complete configuration.
+
+ * XXX Change this to specific lists */
+void
+listconfig()
+{
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ printf("Configuration summary\n\n");
+ printf("Drives:\t\t%d (%d configured)\n", vinum_conf.drives_used, vinum_conf.drives_allocated);
+ printf("Volumes:\t%d (%d configured)\n", vinum_conf.volumes_used, vinum_conf.volumes_allocated);
+ printf("Plexes:\t\t%d (%d configured)\n", vinum_conf.plexes_used, vinum_conf.plexes_allocated);
+ printf("Subdisks:\t%d (%d configured)\n\n", vinum_conf.subdisks_used, vinum_conf.subdisks_allocated);
+ vinum_ld(0, NULL, NULL);
+ printf("\n");
+ vinum_lv(0, NULL, NULL);
+ printf("\n");
+ vinum_lp(0, NULL, NULL);
+ printf("\n");
+ vinum_ls(0, NULL, NULL);
+}
+
+void
+vinum_info(int argc, char *argv[], char *argv0[])
+{
+ struct meminfo meminfo;
+ struct mc malloced;
+ int i;
+
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ printf("Flags: 0x%x\t%d opens\n", vinum_conf.flags, vinum_conf.opencount);
+ if (ioctl(superdev, VINUM_MEMINFO, &meminfo) < 0) {
+ perror("Can't get information");
+ return;
+ }
+ printf("Total of %d blocks malloced, total memory: %d\nMaximum allocs: %8d, malloc table at 0x%08x\n",
+ meminfo.mallocs,
+ meminfo.total_malloced,
+ meminfo.highwater,
+ (int) meminfo.malloced);
+
+ if (Verbose)
+ for (i = 0; i < meminfo.mallocs; i++) {
+ malloced.seq = i;
+ if (ioctl(superdev, VINUM_MALLOCINFO, &malloced) < 0) {
+ perror("Can't get information");
+ return;
+ }
+ if (!(i & 63))
+ printf("Block\tSequence\t size\t address\t line\t\tfile\n\n");
+ printf("%6d\t%6d\t\t%6d\t0x%08x\t%6d\t\t%s\n",
+ i,
+ malloced.seq,
+ malloced.size,
+ (int) malloced.address,
+ malloced.line,
+ (char *) &malloced.file);
+ }
+}
+
+/* Print config file to a file. This is a userland version
+ * of kernel format_config */
+void
+vinum_printconfig(int argc, char *argv[], char *argv0[])
+{
+ FILE *of;
+ struct utsname uname_s;
+ time_t now;
+ int i;
+ int j;
+ struct volume vol;
+ struct plex plex;
+ struct sd sd;
+ struct drive drive;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: \tprintconfig <outfile>\n");
+ return;
+ }
+ of = fopen(argv[0], "w");
+ if (of == NULL) {
+ fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno));
+ return;
+ }
+ uname(&uname_s); /* get our system name */
+ time(&now); /* and the current time */
+ fprintf(of,
+ "# Vinum configuration of %s, saved at %s",
+ uname_s.nodename,
+ ctime(&now)); /* say who did it */
+
+ for (i = 0; i < vinum_conf.drives_used; i++) {
+ get_drive_info(&drive, i);
+ if (drive.state != drive_unallocated) {
+ fprintf(of,
+ "drive %s device %s\n",
+ drive.label.name,
+ drive.devicename);
+ }
+ }
+
+ for (i = 0; i < vinum_conf.volumes_used; i++) {
+ get_volume_info(&vol, i);
+ if (vol.state != volume_unallocated) {
+ if (vol.preferred_plex >= 0) /* preferences, */
+ fprintf(of,
+ "volume %s readpol prefer %s",
+ vol.name,
+ vinum_conf.plex[vol.preferred_plex].name);
+ else /* default round-robin */
+ fprintf(of, "volume %s", vol.name);
+ }
+ }
+
+ /* Then the plex configuration */
+ for (i = 0; i < vinum_conf.plexes_used; i++) {
+ get_volume_info(&vol, i);
+ if (plex.state != plex_unallocated) {
+ fprintf(of, "plex name %s state %s org %s ",
+ plex.name,
+ plex_state(plex.state),
+ plex_org(plex.organization));
+ if ((plex.organization == plex_striped)
+ ) {
+ fprintf(of, "%db ", (int) plex.stripesize);
+ }
+ if (plex.volno >= 0) { /* we have a volume */
+ get_volume_info(&vol, plex.volno);
+ fprintf(of, "vol %s ", vol.name);
+ }
+ for (j = 0; j < plex.subdisks; j++) {
+ get_plex_sd_info(&sd, i, j);
+ fprintf(of, " sd %s", sd.name);
+ }
+ fprintf(of, "\n");
+ }
+ }
+
+ /* And finally the subdisk configuration */
+ for (i = 0; i < vinum_conf.subdisks_used; i++) {
+ get_sd_info(&sd, i);
+ if (sd.state != sd_unallocated) {
+ get_drive_info(&drive, sd.driveno);
+ get_plex_info(&plex, sd.plexno);
+ fprintf(of,
+ "sd name %s drive %s plex %s len %qdb driveoffset %qdb plexoffset %qdb\n",
+ sd.name,
+ drive.label.name,
+ plex.name,
+ sd.sectors,
+ sd.driveoffset,
+ sd.plexoffset);
+ }
+ }
+}
diff --git a/sbin/vinum/v.c b/sbin/vinum/v.c
new file mode 100644
index 0000000..8ae420d
--- /dev/null
+++ b/sbin/vinum/v.c
@@ -0,0 +1,561 @@
+/* vinum.c: vinum interface program */
+/*-
+ * 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.
+ *
+ */
+
+/* $Id: v.c,v 1.22 1998/08/11 07:44:54 grog Exp grog $ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include "vinumhdr.h"
+#include "vext.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <readline/history.h>
+#include <readline/readline.h>
+
+FILE *cf; /* config file handle */
+
+char buffer[BUFSIZE]; /* buffer to read in to */
+
+int line = 0; /* stdin line number for error messages */
+int file_line = 0; /* and line in input file (yes, this is tacky) */
+int inerror; /* set to 1 to exit after end of config file */
+
+/* flags */
+
+#if DEBUG
+int debug = 0; /* debug flag, usage varies */
+#endif
+int force = 0; /* set to 1 to force some dangerous ops */
+int verbose = 0; /* set verbose operation */
+int Verbose = 0; /* set very verbose operation */
+int recurse = 0; /* set recursion */
+int stats = 0; /* show statistics */
+
+/* Structures to read kernel data into */
+struct _vinum_conf vinum_conf; /* configuration information */
+
+struct volume vol;
+struct plex plex;
+struct sd sd;
+struct drive drive;
+
+jmp_buf command_fail; /* return on a failed command */
+int superdev; /* vinum super device */
+
+#define ofs(x) ((void *) (& ((struct confdata *) 0)->x)) /* offset of x in struct confdata */
+
+/* create description-file
+ Create a volume as described in description-file
+ modify description-file
+ Modify the objects as described in description-file
+ list [-r] [volume | plex | subdisk]
+ List information about specified objects
+ set [-f] state volume | plex | subdisk | disk
+ Set the state of the object to state
+ rm [-f] [-r] volume | plex | subdisk
+ Remove an object
+ start [volume | plex | subdisk]
+ Allow the system to access the objects
+ stop [-f] [volume | plex | subdisk]
+ Terminate access the objects
+ */
+
+char *token[MAXARGS]; /* pointers to individual tokens */
+int tokens; /* number of tokens */
+
+int
+main(int argc, char *argv[])
+{
+ superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open it */
+
+ if (superdev < 0) { /* no go */
+ if (errno == ENOENT) /* we don't have our node, */
+ make_devices(); /* create them first */
+ if (superdev < 0) {
+ perror("Can't open " VINUM_SUPERDEV_NAME);
+ return 1;
+ }
+ }
+ if (argc > 1) { /* we have a command on the line */
+ if (setjmp(command_fail) != 0) /* long jumped out */
+ return -1;
+ parseline(argc - 1, &argv[1]); /* do it */
+ } else {
+ for (;;) { /* ugh */
+ char *c;
+
+ setjmp(command_fail); /* come back here on catastrophic failure */
+
+ c = readline("vinum -> "); /* get an input */
+ if (c == NULL) { /* EOF or error */
+ if (ferror(stdin)) {
+ fprintf(stderr, "Can't read input: %s (%d)\n", strerror(errno), errno);
+ return 1;
+ } else { /* EOF */
+ printf("\n");
+ return 0;
+ }
+ } else if (*c) { /* got something there */
+ add_history(c); /* save it in the history */
+ strcpy(buffer, c); /* put it where we can munge it */
+ free(c);
+ line++; /* count the lines */
+ tokens = tokenize(buffer, token);
+ /* got something potentially worth parsing */
+ if (tokens)
+ parseline(tokens, token); /* and do what he says */
+ }
+ }
+ }
+ return 0; /* normal completion */
+}
+
+#define FUNKEY(x) { kw_##x, &vinum_##x } /* create pair "kw_foo", vinum_foo */
+
+struct funkey {
+ enum keyword kw;
+ void (*fun) (int argc, char *argv[], char *arg0[]);
+} funkeys[] = {
+
+ FUNKEY(create),
+ FUNKEY(read),
+#ifdef DEBUG
+ FUNKEY(debug),
+#endif
+ FUNKEY(volume),
+ FUNKEY(plex),
+ FUNKEY(sd),
+ FUNKEY(drive),
+ FUNKEY(modify),
+ FUNKEY(list),
+ FUNKEY(ld),
+ FUNKEY(ls),
+ FUNKEY(lp),
+ FUNKEY(lv),
+ FUNKEY(info),
+ FUNKEY(set),
+ FUNKEY(init),
+ FUNKEY(label),
+ FUNKEY(resetconfig),
+ FUNKEY(rm),
+ FUNKEY(attach),
+ FUNKEY(detach),
+ FUNKEY(rename),
+ FUNKEY(replace),
+ FUNKEY(printconfig),
+ FUNKEY(start),
+ FUNKEY(stop),
+ FUNKEY(resetstats)
+};
+
+/* Take args arguments at argv and attempt to perform the operation specified */
+void
+parseline(int args, char *argv[])
+{
+ int i;
+ int j;
+ enum keyword command; /* command to execute */
+
+ if ((args == 0) /* empty line */
+ ||(*argv[0] == '#')) /* or a comment, */
+ return;
+ if (args == MAXARGS) { /* too many arguments, */
+ fprintf(stderr, "Too many arguments to %s, this can't be right\n", argv[0]);
+ return;
+ }
+ command = get_keyword(argv[0], &keyword_set);
+ force = 0; /* initialize flags */
+ verbose = 0; /* initialize flags */
+ Verbose = 0; /* initialize flags */
+ recurse = 0; /* initialize flags */
+ stats = 0; /* initialize flags */
+ /* First handle generic options */
+ for (i = 1; (i < args) && (argv[i][0] == '-'); i++) { /* while we have flags */
+ for (j = 1; j < strlen(argv[i]); j++)
+ switch (argv[i][j]) {
+#if DEBUG
+ case 'd': /* -d: debug */
+ debug = 1;
+ break;
+#endif
+
+ case 'f': /* -f: force */
+ force = 1;
+ break;
+
+ case 'v': /* -v: verbose */
+ verbose++;
+ break;
+
+ case 'V': /* -V: Very verbose */
+ verbose++;
+ Verbose++;
+ break;
+
+ case 'r': /* -r: recurse */
+ recurse = 1;
+ break;
+
+ case 's': /* -s: show statistics */
+ stats = 1;
+ break;
+
+ default:
+ fprintf(stderr, "Invalid flag: %s\n", argv[i]);
+ }
+ }
+
+ /* Pass what we have left to the command to handle it */
+ for (j = 0; j < (sizeof(funkeys) / sizeof(struct funkey)); j++) {
+ if (funkeys[j].kw == command) { /* found the command */
+ funkeys[j].fun(args - i, &argv[i], &argv[0]);
+ return;
+ }
+ }
+ fprintf(stderr, "Unknown command: %s\n", argv[0]);
+}
+
+void
+get_drive_info(struct drive *drive, int index)
+{
+ *(int *) drive = index; /* put in drive to hand to driver */
+ if (ioctl(superdev, VINUM_DRIVECONFIG, drive) < 0) {
+ fprintf(stderr,
+ "Can't get config for drive %d: %s\n",
+ index,
+ strerror(errno));
+ longjmp(command_fail, -1);
+ }
+}
+
+void
+get_sd_info(struct sd *sd, int index)
+{
+ *(int *) sd = index; /* put in sd to hand to driver */
+ if (ioctl(superdev, VINUM_SDCONFIG, sd) < 0) {
+ fprintf(stderr,
+ "Can't get config for subdisk %d: %s\n",
+ index,
+ strerror(errno));
+ longjmp(command_fail, -1);
+ }
+}
+
+/* Get the contents of the sd entry for subdisk <sdno>
+ * of the specified plex. */
+void
+get_plex_sd_info(struct sd *sd, int plexno, int sdno)
+{
+ ((int *) sd)[0] = plexno;
+ ((int *) sd)[1] = sdno; /* pass parameters */
+ if (ioctl(superdev, VINUM_PLEXSDCONFIG, sd) < 0) {
+ fprintf(stderr,
+ "Can't get config for subdisk %d (part of plex %d): %s\n",
+ sdno,
+ plexno,
+ strerror(errno));
+ longjmp(command_fail, -1);
+ }
+}
+
+void
+get_plex_info(struct plex *plex, int index)
+{
+ *(int *) plex = index; /* put in plex to hand to driver */
+ if (ioctl(superdev, VINUM_PLEXCONFIG, plex) < 0) {
+ fprintf(stderr,
+ "Can't get config for plex %d: %s\n",
+ index,
+ strerror(errno));
+ longjmp(command_fail, -1);
+ }
+}
+
+void
+get_volume_info(struct volume *volume, int index)
+{
+ *(int *) volume = index; /* put in volume to hand to driver */
+ if (ioctl(superdev, VINUM_VOLCONFIG, volume) < 0) {
+ fprintf(stderr,
+ "Can't get config for volume %d: %s\n",
+ index,
+ strerror(errno));
+ longjmp(command_fail, -1);
+ }
+}
+
+/* Create the device nodes for vinum objects */
+void
+make_devices(void)
+{
+ int volno;
+ int plexno;
+ int sdno;
+ int driveno;
+
+ char filename[PATH_MAX]; /* for forming file names */
+
+ if (superdev >= 0) /* super device open */
+ close(superdev);
+
+ system("rm -rf " VINUM_DIR " " VINUM_RDIR); /* remove the old directories */
+ system("mkdir -p " VINUM_DIR "/drive " /* and make them again */
+ VINUM_DIR "/plex "
+ VINUM_DIR "/sd "
+ VINUM_DIR "/vol "
+ VINUM_DIR "/rvol "
+ VINUM_RDIR);
+
+ if (mknod(VINUM_SUPERDEV_NAME,
+ S_IRWXU | S_IFBLK, /* block device, user only */
+ VINUM_SUPERDEV) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", VINUM_SUPERDEV_NAME, strerror(errno));
+
+ superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open the super device */
+
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ return;
+ }
+ /* First, create directories for the volumes */
+ for (volno = 0; volno < vinum_conf.volumes_used; volno++) {
+ dev_t voldev;
+ dev_t rvoldev;
+
+ get_volume_info(&vol, volno);
+ if (vol.state != volume_unallocated) { /* we could have holes in our lists */
+ voldev = VINUMBDEV(volno, 0, 0, VINUM_VOLUME_TYPE); /* create a block device number */
+ rvoldev = VINUMCDEV(volno, 0, 0, VINUM_VOLUME_TYPE); /* and a character device */
+
+ /* Create /dev/vinum/<myvol> */
+ sprintf(filename, VINUM_DIR "/%s", vol.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, voldev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* Create /dev/rvinum/<myvol> */
+ sprintf(filename, VINUM_RDIR "/%s", vol.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFCHR, rvoldev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* Create /dev/vinum/r<myvol> XXX until we fix fsck and friends */
+ sprintf(filename, VINUM_DIR "/r%s", vol.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFCHR, rvoldev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* Create /dev/vinum/vol/<myvol> */
+ sprintf(filename, VINUM_DIR "/vol/%s", vol.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, voldev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* Create /dev/vinum/rvol/<myvol> */
+ sprintf(filename, VINUM_DIR "/rvol/%s", vol.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFCHR, rvoldev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* Create /dev/vinum/vol/<myvol>.plex/ */
+ sprintf(filename, VINUM_DIR "/vol/%s.plex", vol.name);
+ if (mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* Now create device entries for the plexes in
+ * /dev/vinum/<vol>.plex/ and /dev/vinum/plex */
+ for (plexno = 0; plexno < vol.plexes; plexno++) {
+ dev_t plexdev;
+
+ get_plex_info(&plex, vol.plex[plexno]);
+ if (plex.state != plex_unallocated) {
+ plexdev = VINUMBDEV(volno, plexno, 0, VINUM_PLEX_TYPE);
+
+ /* Create device /dev/vinum/vol/<vol>.plex/<plex> */
+ sprintf(filename, VINUM_DIR "/vol/%s.plex/%s", vol.name, plex.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, plexdev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* And /dev/vinum/plex/<plex> */
+ sprintf(filename, VINUM_DIR "/plex/%s", plex.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, plexdev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* Create directory /dev/vinum/vol/<vol>.plex/<plex>.sd */
+ sprintf(filename, VINUM_DIR "/vol/%s.plex/%s.sd", vol.name, plex.name);
+ if (mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* Create the contents of /dev/vinum/<vol>.plex/<plex>.sd */
+ for (sdno = 0; sdno < plex.subdisks; sdno++) {
+ dev_t sddev;
+
+ get_plex_sd_info(&sd, vol.plex[plexno], sdno);
+ if (sd.state != sd_unallocated) {
+ sddev = VINUMBDEV(volno, plexno, sdno, VINUM_SD_TYPE);
+
+ /* Create /dev/vinum/vol/<vol>.plex/<plex>.sd/<sd> */
+ sprintf(filename, VINUM_DIR "/vol/%s.plex/%s.sd/%s", vol.name, plex.name, sd.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, sddev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+
+ /* And /dev/vinum/sd/<sd> */
+ sprintf(filename, VINUM_DIR "/sd/%s", sd.name);
+ if (mknod(filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IFBLK, sddev) < 0)
+ fprintf(stderr, "Can't create %s: %s\n", filename, strerror(errno));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Drives. Do this later (both logical and physical names) XXX */
+ for (driveno = 0; driveno < vinum_conf.drives_used; driveno++) {
+ get_drive_info(&drive, driveno);
+ if (drive.state != drive_unallocated) {
+ sprintf(filename, "ln -s %s " VINUM_DIR "/drive/%s", drive.devicename, drive.label.name);
+ system(filename);
+ }
+ }
+}
+
+/* Find the object "name". Return object type at type,
+ * and the index as the return value.
+ * If not found, return -1 and invalid_object.
+ */
+int
+find_object(const char *name, enum objecttype *type)
+{
+ int object;
+
+ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
+ perror("Can't get vinum config");
+ *type = invalid_object;
+ return -1;
+ }
+ /* Search the drive table */
+ for (object = 0; object < vinum_conf.drives_used; object++) {
+ get_drive_info(&drive, object);
+ if (strcmp(name, drive.label.name) == 0) {
+ *type = drive_object;
+ return object;
+ }
+ }
+
+ /* Search the subdisk table */
+ for (object = 0; object < vinum_conf.subdisks_used; object++) {
+ get_sd_info(&sd, object);
+ if (strcmp(name, sd.name) == 0) {
+ *type = sd_object;
+ return object;
+ }
+ }
+
+ /* Search the plex table */
+ for (object = 0; object < vinum_conf.plexes_used; object++) {
+ get_plex_info(&plex, object);
+ if (strcmp(name, plex.name) == 0) {
+ *type = plex_object;
+ return object;
+ }
+ }
+
+ /* Search the volume table */
+ for (object = 0; object < vinum_conf.volumes_used; object++) {
+ get_volume_info(&vol, object);
+ if (strcmp(name, vol.name) == 0) {
+ *type = volume_object;
+ return object;
+ }
+ }
+
+ /* Didn't find the name: invalid */
+ *type = invalid_object;
+ return -1;
+}
+
+/* Continue reviving a plex in the background */
+void
+continue_revive(int plexno)
+{
+ struct plex plex;
+ pid_t pid;
+ get_plex_info(&plex, plexno);
+
+#if DEBUG
+ if (debug)
+ pid = 0; /* wander through into the "child" process */
+ else
+ pid = fork(); /* do this in the background */
+#endif
+ if (pid == 0) { /* we're the child */
+ struct _ioctl_reply reply;
+ struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
+
+ openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN);
+ syslog(LOG_INFO | LOG_KERN, "reviving plex %s", plex.name);
+
+ for (reply.error = EAGAIN; reply.error == EAGAIN;) {
+ message->index = plexno; /* pass plex number */
+ message->type = plex_object; /* and type of object */
+ message->state = object_up;
+ ioctl(superdev, VINUM_SETSTATE, message);
+ }
+ if (reply.error) {
+ syslog(LOG_ERR | LOG_KERN,
+ "can't revive plex %s: %s",
+ plex.name,
+ reply.msg[0] ? reply.msg : strerror(reply.error));
+ exit(1);
+ } else {
+ get_plex_info(&plex, plexno); /* update the info */
+ syslog(LOG_INFO | LOG_KERN, "plex %s is %s", plex.name, plex_state(plex.state));
+ exit(0);
+ }
+ } else if (pid < 0) /* couldn't fork? */
+ fprintf(stderr, "Can't continue reviving %s: %s\n", plex.name, strerror(errno));
+ else
+ printf("Reviving %s in the background\n", plex.name);
+}
diff --git a/sbin/vinum/vext.h b/sbin/vinum/vext.h
new file mode 100644
index 0000000..5b1678f
--- /dev/null
+++ b/sbin/vinum/vext.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 1997 Nan Yang Computer Services Limited
+ * All rights reserved.
+ *
+ * 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 for the NetBSD Project
+ * by Jason R. Thorpe.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/* $Id: vext.h,v 1.9 1998/08/11 03:06:02 grog Exp grog $ */
+
+#define MAXARGS 64 /* maximum number of args on a line */
+#define PLEXINITSIZE MAXPHYS /* block size to write when initializing */
+
+enum {
+ KILOBYTE = 1024,
+ MEGABYTE = 1048576,
+ GIGABYTE = 1073741824
+};
+
+/* Prototype declarations */
+void parseline(int c, char *args[]); /* parse a line with c parameters at args */
+void checkentry(int index);
+int haveargs(int); /* check arg, error message if not valid */
+void vinum_create(int argc, char *argv[], char *arg0[]);
+void vinum_read(int argc, char *argv[], char *arg0[]);
+void vinum_modify(int argc, char *argv[], char *arg0[]);
+void vinum_volume(int argc, char *argv[], char *arg0[]);
+void vinum_plex(int argc, char *argv[], char *arg0[]);
+void vinum_sd(int argc, char *argv[], char *arg0[]);
+void vinum_drive(int argc, char *argv[], char *arg0[]);
+void vinum_list(int argc, char *argv[], char *arg0[]);
+void vinum_info(int argc, char *argv[], char *arg0[]);
+void vinum_set(int argc, char *argv[], char *arg0[]);
+void vinum_rm(int argc, char *argv[], char *arg0[]);
+void vinum_init(int argc, char *argv[], char *arg0[]);
+void vinum_resetconfig(int argc, char *argv[], char *arg0[]);
+void vinum_start(int argc, char *argv[], char *arg0[]);
+void continue_revive(int plexno);
+void vinum_stop(int argc, char *argv[], char *arg0[]);
+void reset_volume_stats(int volno, int recurse);
+void reset_plex_stats(int plexno, int recurse);
+void reset_sd_stats(int sdno, int recurse);
+void reset_drive_stats(int driveno);
+void vinum_resetstats(int argc, char *argv[], char *arg0[]);
+void vinum_attach(int argc, char *argv[], char *argv0[]);
+void vinum_detach(int argc, char *argv[], char *argv0[]);
+void vinum_rename(int argc, char *argv[], char *argv0[]);
+void vinum_rename_2(char *, char *);
+void vinum_replace(int argc, char *argv[], char *argv0[]);
+void vinum_printconfig(int argc, char *argv[], char *argv0[]);
+void vinum_label(int argc, char *argv[], char *arg0[]);
+void vinum_ld(int argc, char *argv[], char *arg0[]);
+void vinum_ls(int argc, char *argv[], char *arg0[]);
+void vinum_lp(int argc, char *argv[], char *arg0[]);
+void vinum_lv(int argc, char *argv[], char *arg0[]);
+#ifdef DEBUG
+void vinum_debug(int argc, char *argv[], char *arg0[]);
+#endif
+void make_devices(void);
+void get_drive_info(struct drive *drive, int index);
+void get_sd_info(struct sd *sd, int index);
+void get_plex_sd_info(struct sd *sd, int plexno, int sdno);
+void get_plex_info(struct plex *plex, int index);
+void get_volume_info(struct volume *volume, int index);
+int find_object(const char *name, enum objecttype *type);
+char *lltoa(long long l, char *s);
+void vinum_ldi(int, int);
+void vinum_lvi(int, int);
+void vinum_lpi(int, int);
+void vinum_lsi(int, int);
+int vinum_li(int object, enum objecttype type);
+char *roughlength(long long bytes, int);
+u_int64_t sizespec(char *spec);
+
+extern int force; /* set to 1 to force some dangerous ops */
+extern int verbose; /* set verbose operation */
+extern int Verbose; /* very verbose operation */
+extern int recurse; /* set recursion */
+extern int stats; /* show statistics */
+
+/* Structures to read kernel data into */
+extern struct _vinum_conf vinum_conf; /* configuration information */
+
+extern struct volume vol;
+extern struct plex plex;
+extern struct sd sd;
+extern struct drive drive;
+
+extern jmp_buf command_fail; /* return on a failed command */
+extern int superdev; /* vinum super device */
+
+extern int line; /* stdin line number for error messages */
+extern int file_line; /* and line in input file (yes, this is tacky) */
+
+extern char buffer[]; /* buffer to read in to */
diff --git a/sbin/vinum/vinum.8 b/sbin/vinum/vinum.8
new file mode 100644
index 0000000..27cca43
--- /dev/null
+++ b/sbin/vinum/vinum.8
@@ -0,0 +1,899 @@
+.\" Hey, Emacs, edit this file in -*- nroff-fill -*- mode
+.\"
+.Dd 11 July 1998
+.Dt vinum 8
+.Os FreeBSD
+.Sh NAME
+.Nm vinum
+.Nd Logical Volume Manager control program
+.Sh SYNOPSIS
+.Nm
+.Op command
+.Sh COMMANDS
+.Cd create
+.Ar description-file
+.in +1i
+Create a volume as described in
+.Ar description-file
+.in
+.\" XXX remove this
+.Cd attach Ar plex Ar volume
+.Op Nm rename
+.Cd attach Ar subdisk Ar plex Ar [offset]
+.Op Nm rename
+.in +1i
+Attach a plex to a volume, or a subdisk to a plex.
+.in
+.\" XXX remove this
+.Cd debug
+.in +1i
+Cause the volume manager to enter the kernel debugger.
+.in
+.Cd detach
+.Op Ar plex | subdisk
+.in +1
+Detach a plex or subdisk from the volume or plex to which it is attached.
+.in
+.Cd info
+.Op Fl v
+.in +1i
+List information about volume manager state.
+.in
+.Cd init
+.Op Fl v
+.in +1i
+.\" XXX
+Initialize a plex by writing zeroes to all its subdisks.
+.in
+.Cd l
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op volume | plex | subdisk
+.in +1i
+List information about specified objects
+.in
+.Cd list
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op volume | plex | subdisk
+.in +1i
+List information about specified objects
+.in
+.Cd ld
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op volume
+.in +1i
+List information about drives
+.in
+.Cd ls
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op subdisk
+.in +1i
+List information about subdisks
+.in
+.Cd lp
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op plex
+.in +1i
+List information about plexes
+.in
+.Cd lv
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op volume
+.in +1i
+List information about volumes
+.in
+.Cd label
+.Ar volume
+.in +1i
+Create a volume label
+.in
+.Cd read
+.Ar disk-partition
+.in +1i
+Read the
+.Nm
+configuration from the specified disk partition.
+.in
+.Cd rename Op Fl r
+.Ar [ drive | subdisk | plex | volume ]
+.Ar newname
+.in +1i
+Change the name of the specified object.
+.in
+.Cd replace
+.Ar [ subdisk | plex ]
+.Ar newobject
+.in +1i
+Replace the object with an identical other object. XXX not implemented yet.
+.in
+.Cd resetconfig
+.in +1i
+Reset the complete
+.Nm
+configuration.
+.in
+.Cd resetstats
+.Op Fl r
+.Op volume | plex | subdisk
+.in +1i
+Reset statistisc counters for the specified objects, or for all objects if none
+are specified.
+.in
+.Cd rm
+.Op Fl f
+.Op Fl r
+.Ar volume | plex | subdisk
+.in +1i
+Remove an object
+.in
+.ig
+XXX
+.Cd set
+.Op Fl f
+.Ar state
+.Ar volume | plex | subdisk | disk
+.in +1i
+Set the state of the object to \fIstate\fP\|
+.in
+..
+.Cd start
+.Op volume | plex | subdisk
+.in +1i
+Allow the system to access the objects
+.in
+.Cd stop
+.Op Fl f
+.Op volume | plex | subdisk
+.in +1i
+Terminate access the objects
+.in
+.Sh DESCRIPTION
+.Nm
+is a utility program to communicate with the \fBVinum\fP\| logical volume
+manager. See
+.Xr vinum 4
+for more information about the volume manager.
+.Xr vinum 8
+is designed either for interactive use, when started without a command, or to
+execute a single command if the command is supplied as arguments to
+.Nm vinum.
+.Ss OPTIONS
+.Bl -hang
+.It Cd -v
+The
+.Nm -v
+option can be used with any command to request more detailed information. In
+some cases, such as the
+.Cd stop
+command, it does not have any effect.
+.Pp
+.It Cd -V
+The
+.Nm -V
+option can be used with any command to request more detailed information than
+the
+.Nm -v
+option provides. As with the
+.Nm -v
+option, in some cases it does not have any effect.
+.Pp
+Other options are specific to the command. When specified directly on the
+command line, they may be specified either before or after the command name.
+For example, the following two commands are equivalent:
+.Pp
+.Bd -unfilled -offset indent
+vinum -v stop -f sd0
+vinum -v -f stop sd0
+.Ed
+.It Cd -f
+The
+.Nm -f
+option overrides safety checks. Use with extreme care. This option is for
+emergency use only. For example, the command
+.Bd -unfilled -offset indent
+rm -f myvolume
+.Ed
+.Pp
+removes
+.Nm myvolume
+even if it is open. Any subsequent access to the volume will probably cause a
+panic.
+.It Cd -r
+The
+.Nm -r
+(``recursive'') option is used by the list commands to display information not
+only about the specified objects, but also about subordinate objects. For
+example, in conjnction with the
+.Nm lv
+command, the
+.Nm -r
+option will also show information about the plexes and subdisks belonging to the
+volume.
+.It Cd -s
+The
+.Nm -s
+option is used by the list commands to display statistical information.
+.El
+.Pp
+.Ss COMMANDS IN DETAIL
+.Pp
+.Nm
+commands perform the following functions:
+.Bl -hang
+.It Nm attach Ar plex Ar volume
+.Op Nm rename
+.sp -1v
+.It Nm attach Ar subdisk Ar plex Ar [offset]
+.Op Nm rename
+.sp
+.Nm
+.Ar attach
+inserts the specified plex or subdisk in a volume or plex. In the case of a
+subdisk, an offset in the plex may be specified. If it is not, the subdisk will
+be attached at the first possible location. After attaching a plex to a
+non-empty volume,
+.Nm
+reintegrates the plex.
+.Pp
+If the keyword
+.Nm rename
+is specified,
+.Nm
+renames the object (and in the case of a plex, any subordinate subdisks) to fit
+in with the default
+.Nm
+naming convention.
+.It Nm create Ar description-file
+.sp
+.Nm
+.Ar create
+is used to create any object. In view of the relatively complicated
+relationship and the potential dangers involved in creating a
+.Nm
+object, there is no interactive interface to this function. See the section
+CONFIGURATION FILE below for more information.
+.It Nm debug
+.Pp
+.Nm
+.Ar debug
+is used to enter the remote kernel debugger. It is only activated if
+.Nm
+is built with the DEBUG option. This option will stop the execution of the
+operating system until the kernel debugger is exited. If there is no remote
+connection for a kernel debugger, it will be necessary to reset the system and
+reboot in order to leave the debugger.
+.It Nm detach Op Fl f
+.Ar plex
+.sp -1v
+.It Nm detach Op Fl f
+.Ar subdisk
+.sp
+.Nm
+.Ar detach
+removes the specified plex or subdisk from the volume or plex to which it is
+attached. If removing the object would impair the data integrity of the volume,
+the operation will fail unless the
+.Fl f
+option is specified. If the object is named after the object above it (for
+example, subdisk vol1.plex7.sd0 attached to plex vol1.plex7), the name will be
+changed by prepending the text ``ex-'' (for example, ex-vol1.plex7.sd0). If
+necessary, the name will be truncated in the process.
+.It Nm info
+.Pp
+.Nm
+.Ar info
+displays information about
+.Nm
+memory usage. This is intended primarily for debugging. With the
+.Fl v
+option, it will give detailed information about the memory areas in use.
+.\" XXX
+.It Nm init Ar plex
+.Pp
+.Nm
+.Ar init
+initializes a plex by writing zeroes to all its subdisks. This is the only way
+to ensure consistent data in a plex. You must perform this initialization
+before using a RAID-5 plex. It is also recommended for other new plexes.
+.Pp
+.Nm
+initializes all subdisks of a plex in parallel. Since this operation can take a
+long time, it is performed in the background.
+.Nm
+prints a console message when the initialization is complete.
+.It Nm list
+.Op Fl r
+.Op Fl V
+.Op volume | plex | subdisk
+.sp -1
+.It Nm l
+.Op Fl r
+.Op Fl V
+.Op volume | plex | subdisk
+.sp -1
+.It Nm ld
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op volume
+.sp -1
+.It Nm ls
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op subdisk
+.sp -1
+.It Nm lp
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op plex
+.sp -1
+.It Nm lv
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Fl V
+.Op volume
+.Pp
+.Ar list
+is used to show information about the specified object. If the argument is
+omitted, information is shown about all objects known to
+.Nm vinum .
+The
+.Ar l
+command is a synonym for
+.Ar list .
+.Pp
+The
+.Fl r
+option relates to volumes and plexes: if specified, it recursively lists
+information for the subdisks and (for a volume) plexes subordinate to the
+objects. The commands
+.Ar lv ,
+.Ar lp ,
+.Ar ls
+and
+.Ar ld
+commands list only volumes, plexes, subdisks and drives respectively. This is
+particularly useful when used without parameters.
+.Pp
+The
+.Fl s
+option causes
+.Nm
+to output device statistics, the
+.Op Fl v
+(verbose) option causes some additional information to be output, and the
+.Op Fl V
+causes considerable additional information to be output.
+.It Nm label
+.Ar volume
+.Pp
+The
+.Nm label
+command writes a
+.Ar ufs
+style volume label on a volume. It is a simple alternative to an appropriate
+call to
+.Ar disklabel .
+This is needed because some
+.Ar ufs
+commands still read the disk to find the label instead of using the correct
+.Ar ioctl
+call to access it.
+.Nm
+maintains a volume label separately from the volume data, so this command is not
+needed for
+.Ar newfs .
+.Pp
+.It Nm read
+.Ar disk-partition
+.Pp
+The
+.Nm read
+command reads a previously created
+.Nm
+configuration from the specified disk partition.
+.Nm
+maintains an up-to-date copy of all configuration information on each of the
+disk slices. You can specify any of the partitions in a configuration as the
+parameter to this command.
+.It Nm rename
+.Op Fl r
+.Ar [ drive | subdisk | plex | volume ]
+.Ar newname
+.Pp
+Change the name of the specified object. If the
+.Fl r
+option is specified, subordinate objects will be named by the default rules:
+plex names will be formed by appending .p\f(BInumber\fP to the volume name, and
+subdisk names will be formed by appending .s\f(BInumber\fP to the plex name.
+.It Nm replace
+.Ar [ subdisk | plex ]
+.Ar newobject
+.Pp
+Replace the object with an identical other object. XXX not implemented yet.
+.It Nm resetconfig
+.Pp
+The
+.Nm resetconfig
+command completely obliterates the
+.Nm
+configuration on a system. Use this command only when you want to completely
+delete the configuration.
+.Nm
+will ask for confirmation: you must type in the words NO FUTURE exactly
+as shown:
+.Bd -unfilled -offset indent
+# \f(CBvinum resetconfig\f(CW
+
+WARNING! This command will completely wipe out your vinum
+configuration. All data will be lost. If you really want
+to do this, enter the text
+
+NO FUTURE
+Enter text -> \f(BINO FUTURE\fP
+Vinum configuration obliterated
+.Ed
+.ft R
+.It Nm resetstats
+.Op Fl r
+.Op volume | plex | subdisk
+.Pp
+.Nm
+maintains a number of statistical counters for each object. See the header file
+.Fi vinumvar.h
+for more information.
+.\" XXX put it in here when it's finalized
+Use the
+.Nm resetstats
+command to reset these counters. In conjunction with the
+.Fl r
+option,
+.Nm
+also resets the counters of subordinate objects.
+.It Nm rm
+.Op Fl f
+.Op Fl r
+.Ar volume | plex | subdisk
+.Pp
+.Nm rm
+removes an object from the
+.Nm
+configuration. Once an object has been removed, there is no way to recover it.
+Normally
+.Nm
+performs a large amount of consistency checking before removing an object. The
+.Fl f
+option tells
+.Nm
+to omit this checking and remove the object anyway. Use this option with great
+care: it can result in total loss of data on a volume.
+.Pp
+Normally,
+.Nm
+refuses to remove a volume or plex if it has subordinate plexes or subdisks
+respectively. You can tell
+.Nm
+to remove the object anyway by using the
+.Fl f
+flag, or you can cause
+.Nm
+to remove the subordinate objects as well by using the
+.Fl r
+(recursive) flag. If you remove a volume with the
+.Fl r
+flag, it will remove both the plexes and the subdisks which belong to the
+plexes.
+.ig
+.It Nm set
+.Op Fl f
+.Ar state
+.Ar volume | plex | subdisk | disk
+.Nm set
+sets the state of the specified object to one of the valid states (see OBJECT
+STATES below). Normally
+.Nm
+performs a large amount of consistency checking before making the change. The
+.Fl f
+option tells
+.Nm
+to omit this checking and perform the change anyway. Use this option with great
+care: it can result in total loss of data on a volume.
+.\"XXX
+.Nm This command has not yet been implemented.
+..
+.It Nm start
+.Op volume | plex | subdisk
+.Pp
+.Nm start
+starts the
+.Nm
+subsystem or one of its components. To start a plex in a multi-plex volume, the
+data must be copied from another plex in the volume. This frequently takes a
+long time and is done in the background.
+.ig
+XXX
+When invoked without arguments, it checks
+all disks connected to the system for BSD partitions (type 165) and scans the
+slices for a
+.Nm
+slice, which it calls a \fIdrive\fR\|. The
+.Nm
+drive contains a header with all information about the data stored on the drive,
+including the names of the other drives which are required in order to represent
+plexes and volumes.
+.\" XXX
+.Nm The scan function has not yet been implemented.
+..
+.It Nm stop
+.Op Fl f
+.Op volume | plex | subdisk
+.Pp
+.Nm stop
+disables access to the specified objects and any subordinate objects. It does
+not remove the objects from the configuration. They can be accessed again after
+a
+.Nm start
+command.
+.Pp
+By default,
+.Nm
+does not remove active objects. For example, you cannot remove a plex which is
+attached to an active volume, and you cannot remove a volume which is open. The
+.Fl f
+option tells
+.Nm
+to omit this checking and remove the object anyway. Use this option with great
+care and understanding: used incorrectly, it can result in serious data
+corruption.
+.El
+.Ss CONFIGURATION FILE
+.Nm
+requires that all parameters to the
+.Nm create
+and
+.Nm modify
+commands must be in a configuration file. Entries in the configuration file
+define volumes, plexes and subdisks, and may be in free format, except that each
+entry must be on a single line.
+.Pp
+Some configuration file parameters specify a size (lengths, stripe sizes).
+These lengths can be specified as bytes, as sectors of 512 bytes (by appending
+the letter \f(CWb\fR), as kilobytes (by appending the letter \f(CWk\fR), as
+megabytes (by appending the letter \f(CWm\fR) or as gigabytes (by appending the
+letter \f(CWg\fR). These quantities represent the values 2**10, 2**20 and 2**30
+respectively. For example, the value \f(CW16777216\fR bytes can also be written
+as \f(CW16m\fR, \f(CW16384k\fR or \f(CW32768b\fR.
+.Pp
+The configuration file can contain the following entries:
+.Pp
+.Bl -hang
+.It Nm volume
+.Ar name
+.Op options
+.Pp
+Define a volume with name
+.Ar name .
+.Pp
+Options are:
+.Pp
+.TS H
+tab(#) ;
+l lw50 .
+Option#Meaning
+.TH N
+T{
+.Nm plex
+.Ar plexname
+T}#T{
+Add the specified plex to the volume. If
+.Ar plexname
+is specified as
+.Ar * ,
+.Nm
+will look for the definition of the plex as the next possible entry in the
+configuration file after the definition of the volume.
+T}
+.sp
+T{
+.Nm readpol
+.Ar policy
+T}#T{
+Define a
+.Ar read policy
+for the volume.
+.Ar policy
+may be either
+.Nm round
+or
+.Nm prefer Ar plexname .
+.Nm
+satisfies a read request from only one of the plexes. A
+.Ar round
+read policy specifies that each read should be performed from a different plex
+in \fIround-robin\fR\| fashion. A
+.Ar prefer
+read policy reads from the specified plex every time.
+T}
+.sp
+T{
+.Nm setupstate
+T}#T{
+When creating a multi-plex volume, assume that the contents of all the plexes
+are consistent. This is normally not the case, and correctly you should use the
+.Nm init
+command to first bring them to a consistent state. In the case of striped and
+concatenated plexes, however, it does not normally cause problems to leave them
+inconsistent: when using a volume for a file system or a swap partition, the
+previous contents of the disks are not of interest, so they may be ignored.
+If you want to take this risk, use this keyword.
+.Pp
+Note that you \fImust\fP\| use the
+.Nm init
+command with RAID-5 plexes: otherwise extreme data corruption will result if one
+subdisk fails.
+T}
+.fi
+.TE
+.It Nm plex
+.Op options
+.Pp
+Define a plex. Unlike a volume, a plex does not need a name.
+.Pp
+.TS H
+tab(#) ;
+l lw50 .
+Option#Meaning
+.TH N
+T{
+.Nm name
+.Ar plexname
+T}#T{
+Specify the name of the plex. Note that you must use the keyword
+.Ar name
+when naming a plex or subdisk.
+T}
+.sp
+T{
+.Nm org
+.Ar organization
+.Op stripesize
+T}#T{
+Specify the organization of the plex.
+.Ar organization
+can be one of
+.Ar concat ,
+.Ar striped
+or
+.Ar raid5 .
+For
+.Ar striped
+and
+.Ar raid5
+plexes, the parameter
+.Ar stripesize
+must be specified, while for
+.Ar concat
+it must be omitted. For type
+.Ar striped ,
+it specifies the width of each stripe. For type
+.Ar raid5 ,
+it specifies the size of a group. A group is a portion of a plex which
+stores the parity bits all in the same subdisk. It must be a factor of the plex size (in
+other words, the result of dividing the plex size by the stripe size must be an
+integer), and it must be a multiple of a disk sector (512 bytes).
+T}
+.Pp
+#T{
+A striped plex must have at least two subdisks (otherwise it is a concatenated
+plex), and each must be the same size. A RAID-5 plex must have at least three
+subdisks, and each must be the same size. In practice, a RAID-5 plex should
+have at least 5 subdisks.
+T}
+.Pp
+T{
+.Nm volume
+.Ar volname
+T}#T{
+Add the plex to the specified volume. If no
+.Nm volume
+keyword is specified, the plex will be added to the last volume mentioned in the
+configuration file.
+T}
+.sp
+T{
+.Nm sd
+.Ar sdname
+.Ar offset
+T}#T{
+Add the specified subdisk to the plex at offset
+.Ar offset .
+T}
+.br
+.fi
+.TE
+.It Nm subdisk
+.Op options
+.Pp
+Define a subdisk.
+.Pp
+.TS H
+tab(#) ;
+l lw50 .
+Option#Meaning
+.nf
+.sp
+T{
+.Nm name
+.Ar name
+T}#T{
+Specify the name of a subdisk. It is not necessary to specify a name for a
+subdisk\(emsee OBJECT NAMING above. Note that you must specify the keyword
+.Ar name
+if you wish to name a subdisk.
+T}
+.sp
+T{
+.Nm plexoffset
+.Ar offset
+T}#T{
+Specify the starting offset of the subdisk in the plex. If not specified,
+.Nm
+allocates the space immediately after the previous subdisk, if any, or otherwise
+at the beginning of the plex.
+T}
+.sp
+T{
+.Nm driveoffset
+.Ar offset
+T}#T{
+Specify the starting offset of the subdisk in the drive. If not specified,
+.Nm
+allocates the first contiguous
+.Ar length
+bytes of free space on the drive.
+T}
+.sp
+T{
+.Nm length
+.Ar length
+T}#T{
+Specify the length of the subdisk. This keyword must be specified. There is no
+default.
+.Nm length
+may be shortened to
+.Nm len .
+T}
+.sp
+T{
+.Nm plex
+.Ar plex
+T}#T{
+Specify the plex to which the subdisk belongs. By default, the subdisk belongs
+to the last plex specified.
+T}
+.sp
+T{
+.Nm drive
+.Ar drive
+T}#T{
+Specify the drive on which the subdisk resides. By default, the subdisk resides
+on the last drive specified.
+T}
+.br
+.fi
+.TE
+.El
+.Sh EXAMPLE CONFIGURATION FILE
+.nf
+# Sample vinum configuration file
+#
+# Our drives
+drive drive1 device /dev/sd1h
+drive drive2 device /dev/sd2h
+drive drive3 device /dev/sd3h
+drive drive4 device /dev/sd4h
+drive drive5 device /dev/sd5h
+drive drive6 device /dev/sd6h
+# A volume with one striped plex
+volume tinyvol
+ plex org striped 32b
+ sd length 64m drive drive2
+ sd length 64m drive drive4
+volume stripe
+ plex org striped 32b
+ sd length 512m drive drive2
+ sd length 512m drive drive4
+# Two plexes
+volume concat
+ plex org concat
+ sd length 100m drive drive2
+ sd length 50m drive drive4
+ plex org concat
+ sd length 100m drive drive4
+# A volume with one striped plex and one concatenated plex
+volume strcon
+ plex org striped 32b
+ sd length 100m drive drive2
+ sd length 100m drive drive4
+ plex org concat
+ sd length 100m drive drive2
+ sd length 50m drive drive4
+# a volume with a RAID-5 and a striped plex
+# note that the RAID-5 volume is longer by
+# the length of one subdisk
+volume vol5
+ plex org striped 64k
+ sd length 1000m drive drive2
+ sd length 1000m drive drive4
+ plex org raid5 32k
+ sd length 500m drive drive1
+ sd length 500m drive drive2
+ sd length 500m drive drive3
+ sd length 500m drive drive4
+ sd length 500m drive drive5
+.fi
+.Ss DRIVE LAYOUT CONSIDERATIONS
+.Nm
+drives are currently BSD disk partitions. They must be of type
+.Ar unused
+in order to avoid overwriting file systems. In later versions of
+.Nm
+this requirement will change to type
+.Ar vinum .
+.Nm
+uses the first 265 sectors on each partition for configuration information, so
+the maximum size of a subdisk is 265 sectors smaller than the drive.
+.Sh BUGS
+.Nm
+is currently in alpha test. Many bugs can be expected. The configuration
+mechanism is not yet fully functional.
+.Sh FILES
+.Ar /dev/vinum
+- directory with device nodes for
+.Nm
+objects.
+.br
+.Ar /dev/vinum/control
+- control device for
+.Nm vinum
+.br
+.Ar /dev/vinum/plex
+- directory containing device nodes for
+.Nm
+plexes.
+.br
+.Ar /dev/vinum/sd
+- directory containing device nodes for
+.Nm
+subdisks.
+.Sh SEE ALSO
+.Xr vinum 4
+.Sh AUTHOR
+Greg Lehey
+.Sh HISTORY
+The
+.Nm
+command first appeared in FreeBSD 2.2.6.
OpenPOWER on IntegriCloud