diff --git a/sbin/vinum/Makefile b/sbin/vinum/Makefile new file mode 100644 index 000000000000..7d0a0c285c93 --- /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 diff --git a/sbin/vinum/commands.c b/sbin/vinum/commands.c new file mode 100644 index 000000000000..6554d3307a85 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vinumhdr.h" +#include "vext.h" +#include +#include +#include +#include + +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 [rename] []\n" + "\tattach [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 [rename]\n" + "\tdetach [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 "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 \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 \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 000000000000..40807480fff7 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 *) ®ion; + + 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, ®ion) < 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, ®ion) < 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 \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 000000000000..8ae420d784da --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vinumhdr.h" +#include "vext.h" +#include +#include +#include +#include + +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 + * 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/ */ + 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/ */ + 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 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/ */ + 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/ */ + 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/.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/.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/.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/ */ + 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/.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/.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/.plex/.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/ */ + 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 000000000000..5b1678f6c920 --- /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 000000000000..27cca434d861 --- /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.