freebsd-nq/sbin/vinum/commands.c
Robert Watson f315f7629f Don't attempt to make devices if we're using devfs. This
substantially cleans up the output when running the vinum
management tool, and also makes it work better.

Long sustained silence from:	grog
2003-11-17 15:56:00 +00:00

2357 lines
70 KiB
C

/* commands.c: vinum interface program, main commands */
/*-
* Copyright (c) 1997, 1998
* Nan Yang Computer Services Limited. All rights reserved.
*
* Written by Greg Lehey
*
* 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.52 2003/05/08 00:33:57 grog Exp $
* $FreeBSD$
*/
#include "vext.h"
#include <devstat.h>
static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen);
void
vinum_create(int argc, char *argv[], char *arg0[])
{
int error;
FILE *dfd; /* file descriptor for the config file */
char buffer[BUFSIZE]; /* read config file in here */
char commandline[BUFSIZE]; /* issue command from here */
struct _ioctl_reply *reply;
int ioctltype; /* for ioctl call */
char tempfile[PATH_MAX]; /* name of temp file for direct editing */
char *file; /* file to read */
FILE *tf; /* temp file */
if (argc == 0) { /* no args, */
char *editor; /* editor to start */
int status;
editor = getenv("EDITOR");
if (editor == NULL)
editor = _PATH_VI;
sprintf(tempfile, "/var/tmp/" VINUMMOD ".create.%d", getpid()); /* create a temp file */
tf = fopen(tempfile, "w"); /* open it */
if (tf == NULL) {
fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno));
return;
}
printconfig(tf, "# "); /* and put the current config it */
fclose(tf);
sprintf(commandline, "%s %s", editor, tempfile); /* create an edit command */
status = system(commandline); /* do it */
if (status != 0) {
fprintf(stderr, "Can't edit config: status %d\n", status);
return;
}
file = tempfile;
} else if (argc == 1)
file = argv[0];
else {
fprintf(stderr, "Expecting 1 parameter, not %d\n", argc);
return;
}
reply = (struct _ioctl_reply *) &buffer;
dfd = fopen(file, "r");
if (dfd == NULL) { /* no go */
fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno));
return;
}
if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* 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 (History)
fprintf(History, "%s", buffer);
if (configline == NULL) {
if (ferror(dfd))
perror("Can't read config file");
break;
}
file_line++; /* count the lines */
if (vflag)
printf("%4d: %s", file_line, buffer);
strcpy(commandline, buffer); /* make a copy */
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (!vflag) /* print this line anyway */
printf("%4d: %s", file_line, commandline);
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, &force)) /* can't get config? */
return;
}
}
fclose(dfd); /* done with the config file */
ioctltype = 0; /* saveconfig after update */
error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */
if (error != 0)
perror("Can't save Vinum config");
if (no_devfs)
make_devices();
listconfig();
checkupdates(); /* make sure we're updating */
}
/* 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;
int i;
reply = (struct _ioctl_reply *) &buffer;
buffer[0] = '\0'; /* make sure we don't pass anything */
if (argc > 0) { /* args specified, */
for (i = 0; i < argc; i++) { /* each drive name */
strcat(buffer, argv[i]);
strcat(buffer, " ");
}
}
ioctl(superdev, VINUM_READCONFIG, &buffer);
if (reply->error != 0) { /* error in config */
fprintf(stdout, "** %s: %s\n", reply->msg, strerror(reply->error));
if (error != 0)
perror("Can't save Vinum config");
} else {
if (error != 0)
perror("Can't save Vinum config");
if (no_devfs)
make_devices();
}
checkupdates(); /* make sure we're updating */
}
void
vinum_debug(int argc, char *argv[], char *arg0[])
{
struct debuginfo info;
if (vinum_conf.flags & VF_HASDEBUG) {
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);
} else /* no debug in kernel module */
fprintf(stderr, "Kernel module does not have debug support\n");
}
void
vinum_modify(int argc, char *argv[], char *arg0[])
{
fprintf(stderr, "Modify command is currently not implemented\n");
checkupdates(); /* make sure we're updating */
}
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, "usage: rm object [object...]\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 (vflag)
fprintf(stderr, "%s removed\n", argv[index]);
}
}
checkupdates(); /* make sure we're updating */
/* Arguably we should be cleverer about this. */
if (no_devfs)
make_devices();
}
}
void
vinum_resetconfig(int argc, char *argv[], char *arg0[])
{
char reply[32];
int error;
if (isatty(STDIN_FILENO)) {
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 {
if (no_devfs)
make_devices(); /* recreate the /dev/vinum hierarchy */
printf("\b Vinum configuration obliterated\n");
start_daemon(); /* then restart the daemon */
}
}
checkupdates(); /* make sure we're updating */
} else
fprintf(stderr, "Please enter this command from a terminal\n");
}
/* Initialize subdisks */
void
vinum_init(int argc, char *argv[], char *arg0[])
{
if (argc > 0) { /* initialize plexes */
int objindex;
int objno;
enum objecttype type; /* type returned */
if (History)
fflush(History); /* don't let all the kids do it. */
for (objindex = 0; objindex < argc; objindex++) {
objno = find_object(argv[objindex], &type); /* find the object */
if (objno < 0)
printf("Can't find %s\n", argv[objindex]);
else {
switch (type) {
case volume_object:
initvol(objno);
break;
case plex_object:
initplex(objno, argv[objindex]);
break;
case sd_object:
initsd(objno, dowait);
break;
default:
printf("Can't initialize %s: wrong object type\n", argv[objindex]);
break;
}
}
}
}
checkupdates(); /* make sure we're updating */
}
void
initvol(int volno)
{
printf("Initializing volumes is not implemented yet\n");
}
void
initplex(int plexno, char *name)
{
int sdno;
int plexfh = NULL; /* file handle for plex */
pid_t pid;
char filename[MAXPATHLEN]; /* create a file name here */
/* Variables for use by children */
int failed = 0; /* set if a child dies badly */
sprintf(filename, VINUM_DIR "/plex/%s", name);
if ((plexfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* got a plex, open it */
/*
* We don't actually write anything to the
* plex. 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;
}
if (dowait == 0) {
pid = fork(); /* into the background with you */
if (pid != 0) { /* I'm the parent, or we failed */
if (pid < 0) /* failure */
printf("Couldn't fork: %s", strerror(errno));
close(plexfh); /* we don't need this any more */
return;
}
}
/*
* If we get here, we're either the first-level
* child (if we're not waiting) or we're going
* to wait.
*/
for (sdno = 0; sdno < plex.subdisks; sdno++) { /* initialize each subdisk */
get_plex_sd_info(&sd, plexno, sdno);
initsd(sd.sdno, 0);
}
/* Now wait for them to complete */
while (1) {
int status;
pid = wait(&status);
if (((int) pid == -1)
&& (errno == ECHILD)) /* all gone */
break;
if (WEXITSTATUS(status) != 0) { /* oh, oh */
printf("child %d exited with status 0x%x\n", pid, WEXITSTATUS(status));
failed++;
}
}
if (failed == 0) {
syslog(LOG_INFO | LOG_KERN, "plex %s initialized", plex.name);
} else
syslog(LOG_ERR | LOG_KERN, "couldn't initialize plex %s, %d processes died",
plex.name,
failed);
if (dowait == 0) /* we're the waiting child, */
exit(0); /* we've done our dash */
}
/* Initialize a subdisk. */
void
initsd(int sdno, int dowait)
{
pid_t pid;
struct _ioctl_reply reply;
struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
char filename[MAXPATHLEN]; /* create a file name here */
/* Variables for use by children */
int sdfh; /* and for subdisk */
int initsize; /* actual size to write */
int64_t sdsize; /* size of subdisk */
if (dowait == 0) {
pid = fork(); /* into the background with you */
if (pid > 0) /* I'm the parent */
return;
else if (pid < 0) { /* failure */
printf("couldn't fork for subdisk %d: %s", sdno, strerror(errno));
return;
}
}
if (SSize != 0) { /* specified a size for init */
if (SSize < 512)
SSize <<= DEV_BSHIFT;
initsize = min(SSize, MAXPLEXINITSIZE);
} else
initsize = PLEXINITSIZE;
openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN);
get_sd_info(&sd, 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", filename);
if ((sdfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* no go */
syslog(LOG_ERR | LOG_KERN,
"can't open subdisk %s: %s",
filename,
strerror(errno));
exit(1);
}
/* Set the subdisk in initializing state */
message->index = sd.sdno; /* pass object number */
message->type = sd_object; /* and type of object */
message->state = object_initializing;
message->verify = vflag; /* verify what we write? */
message->force = 1; /* insist */
ioctl(superdev, VINUM_SETSTATE, message);
if ((SSize > 0) /* specified a size for init */
&&(SSize < 512))
SSize <<= DEV_BSHIFT;
if (reply.error) {
fprintf(stderr,
"Can't initialize %s: %s (%d)\n",
filename,
strerror(reply.error),
reply.error);
exit(1);
} else {
do {
if (interval) /* pause between copies */
usleep(interval * 1000);
message->index = sd.sdno; /* pass object number */
message->type = sd_object; /* and type of object */
message->state = object_up;
message->verify = vflag; /* verify what we write? */
message->blocksize = SSize;
ioctl(superdev, VINUM_SETSTATE, message);
}
while (reply.error == EAGAIN); /* until we're done */
if (reply.error) {
fprintf(stderr,
"Can't initialize %s: %s (%d)\n",
filename,
strerror(reply.error),
reply.error);
get_sd_info(&sd, sdno);
if (sd.state != sd_up)
/* Set the subdisk down */
message->index = sd.sdno; /* pass object number */
message->type = sd_object; /* and type of object */
message->state = object_down;
message->verify = vflag; /* verify what we write? */
message->force = 1; /* insist */
ioctl(superdev, VINUM_SETSTATE, message);
}
}
printf("subdisk %s initialized\n", filename);
if (!dowait)
exit(0);
}
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 */
/* XXX how should we do this right? */
vinum_read(0, NULL, NULL); /* that's what vinum_read does now */
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 {
int doit = 0; /* set to 1 if we pass our tests */
switch (type) {
case drive_object:
if (drive.state == drive_up) /* already up */
fprintf(stderr, "%s is already up\n", drive.label.name);
else
doit = 1;
break;
case sd_object:
if (sd.state == sd_up) /* already up */
fprintf(stderr, "%s is already up\n", sd.name);
else
doit = 1;
break;
case plex_object:
if (plex.state == plex_up) /* already up */
fprintf(stderr, "%s is already up\n", plex.name);
else {
int sdno;
/*
* First, see if we can bring it up
* just by asking. This might happen
* if somebody has used setupstate on
* the subdisks. If we don't do this,
* we'll return success, but the plex
* won't have changed state. Note
* that we don't check for errors
* here.
*/
message->index = plex.plexno; /* pass object number */
message->type = plex_object; /* it's a plex */
message->state = object_up;
message->force = 0; /* don't force it */
ioctl(superdev, VINUM_SETSTATE, message);
for (sdno = 0; sdno < plex.subdisks; sdno++) {
get_plex_sd_info(&sd, object, sdno);
if ((sd.state >= sd_empty)
&& (sd.state <= sd_reviving)) { /* candidate for start */
message->index = sd.sdno; /* pass object number */
message->type = sd_object; /* it's a subdisk */
message->state = object_up;
message->force = force; /* don't force it, use a larger hammer */
/*
* We don't do any checking here.
* The kernel module has a better
* understanding of these things,
* let it do it.
*/
if (SSize != 0) { /* specified a size for init */
if (SSize < 512)
SSize <<= DEV_BSHIFT;
message->blocksize = SSize;
} else
message->blocksize = DEFAULT_REVIVE_BLOCKSIZE;
ioctl(superdev, VINUM_SETSTATE, message);
if (reply.error != 0) {
if (reply.error == EAGAIN) /* we're reviving */
continue_revive(sd.sdno);
else
fprintf(stderr,
"Can't start %s: %s (%d)\n",
sd.name,
reply.msg[0] ? reply.msg : strerror(reply.error),
reply.error);
}
if (Verbose)
vinum_lsi(sd.sdno, 0);
}
}
}
break;
case volume_object:
if (vol.state == volume_up) /* already up */
fprintf(stderr, "%s is already up\n", vol.name);
else
doit = 1;
break;
}
if (doit) {
message->index = object; /* pass object number */
message->type = type; /* and type of object */
message->state = object_up;
message->force = force; /* don't force it, use a larger hammer */
/*
* We don't do any checking here.
* The kernel module has a better
* understanding of these things,
* let it do it.
*/
if (SSize != 0) { /* specified a size for init or revive */
if (SSize < 512)
SSize <<= DEV_BSHIFT;
message->blocksize = SSize;
} else
message->blocksize = 0;
ioctl(superdev, VINUM_SETSTATE, message);
if (reply.error != 0) {
if ((reply.error == EAGAIN) /* we're reviving */
&&(type == sd_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);
}
}
}
}
checkupdates(); /* make sure we're updating */
}
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;
if (checkupdates() && (!force)) /* not updating? */
return;
message->force = force; /* should we force the transition? */
if (argc == 0) { /* stop vinum */
int fileid = 0; /* ID of Vinum kld */
close(superdev); /* we can't stop if we have vinum open */
sleep(1); /* wait for the daemon to let go */
fileid = kldfind(VINUMMOD);
if ((fileid < 0) /* no go */
||(kldunload(fileid) < 0))
perror("Can't unload " VINUMMOD);
else {
fprintf(stderr, VINUMMOD " unloaded\n");
exit(0);
}
/* If we got here, the stop failed. Reopen the superdevice. */
superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* reopen vinum superdevice */
if (superdev < 0) {
perror("Can't reopen Vinum superdevice");
exit(1);
}
} 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
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) {
get_sd_info(&sd, sdno); /* get the info */
reset_drive_stats(sd.driveno); /* and clear the drive */
}
}
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_allocated; 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 = -1;
int plexno = -1;
char oldname[MAXNAME + 8];
char newname[MAXNAME + 8];
int rename = 0; /* set if we want to rename the object */
if ((argc < 2)
|| (argc > 4)) {
fprintf(stderr,
"usage: \tattach <subdisk> <plex> [rename] [<plexoffset>]\n"
"\tattach <plex> <volume> [rename]\n");
return;
}
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
msg.index = find_object(objname, &msg.type); /* find the object to attach */
msg.otherobject = find_object(supername, &supertype); /* and the object to attach to */
msg.force = force; /* did we specify the use of force? */
msg.recurse = recurse;
msg.offset = -1; /* and no offset */
for (i = 2; i < argc; i++) {
if (!strcmp(argv[i], "rename")) {
rename = 1;
msg.rename = 1; /* do renaming */
} else if (!isdigit(argv[i][0])) { /* not an offset */
fprintf(stderr, "Unknown attribute: %s\n", supername);
return;
} else
msg.offset = sizespec(argv[i]);
}
switch (msg.type) {
case sd_object:
find_object(argv[1], &supertype);
if (supertype != plex_object) { /* huh? */
fprintf(stderr, "%s can only be attached to a plex\n", objname);
return;
}
if ((plex.organization != plex_concat) /* not a cat plex, */
&&(!force)) {
fprintf(stderr, "Can't attach subdisks to a %s plex\n", plex_org(plex.organization));
return;
}
sdno = msg.index; /* note the subdisk number for later */
break;
case plex_object:
find_object(argv[1], &supertype);
if (supertype != volume_object) { /* huh? */
fprintf(stderr, "%s can only be attached to a volume\n", objname);
return;
}
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(sdno); /* 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);
sprintf(oldname, "%s", sd.name);
vinum_rename_2(oldname, 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);
sprintf(oldname, "%s", plex.name);
vinum_rename_2(oldname, newname); /* this may recurse */
break;
}
}
checkupdates(); /* make sure we're updating */
}
/* Detach a subdisk from a plex, or a plex from a volume.
* detach subdisk plex [rename]
* detach plex volume [rename]
*/
void
vinum_detach(int argc, char *argv[], char *argv0[])
{
struct vinum_ioctl_msg msg;
struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg;
if ((argc < 1)
|| (argc > 2)) {
fprintf(stderr,
"usage: \tdetach <subdisk> [rename]\n"
"\tdetach <plex> [rename]\n");
return;
}
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
msg.index = find_object(argv[0], &msg.type); /* find the object to detach */
msg.force = force; /* did we specify the use of force? */
msg.rename = 0; /* don't specify new name */
msg.recurse = recurse; /* but recurse if we have to */
/* XXX are we going to keep this?
* Don't document it yet, since the
* kernel side of things doesn't
* implement it */
if (argc == 2) {
if (!strcmp(argv[1], "rename"))
msg.rename = 1; /* do renaming */
else {
fprintf(stderr, "Unknown attribute: %s\n", argv[1]);
return;
}
}
if ((msg.type != sd_object)
&& (msg.type != plex_object)) {
fprintf(stderr, "Can only detach subdisks and plexes\n");
return;
}
ioctl(superdev, VINUM_DETACH, &msg);
if (reply->error != 0)
fprintf(stderr,
"Can't detach %s: %s (%d)\n",
argv[0],
reply->msg[0] ? reply->msg : strerror(reply->error),
reply->error);
checkupdates(); /* make sure we're updating */
}
static void
dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen)
{
struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
if (strlen(name) > maxlen) {
fprintf(stderr, "%s is too long\n", name);
return;
}
strcpy(msg->newname, name);
ioctl(superdev, VINUM_RENAME, msg);
if (reply->error != 0)
fprintf(stderr,
"Can't rename %s to %s: %s (%d)\n",
oldname,
name,
reply->msg[0] ? reply->msg : strerror(reply->error),
reply->error);
}
/* Rename an object:
* rename <object> "newname"
*/
void
vinum_rename_2(char *oldname, char *newname)
{
struct vinum_rename_msg msg;
int volno;
int plexno;
msg.index = find_object(oldname, &msg.type); /* find the object to rename */
msg.recurse = recurse;
/* Ugh. Determine how long the name may be */
switch (msg.type) {
case drive_object:
dorename(&msg, oldname, newname, MAXDRIVENAME);
break;
case sd_object:
dorename(&msg, oldname, newname, MAXSDNAME);
break;
case plex_object:
plexno = msg.index;
dorename(&msg, oldname, newname, MAXPLEXNAME);
if (recurse) {
int sdno;
get_plex_info(&plex, plexno); /* find out who we are */
msg.type = sd_object;
for (sdno = 0; sdno < plex.subdisks; sdno++) {
char sdname[MAXPLEXNAME + 8];
get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */
sprintf(sdname, "%s.s%d", newname, sdno);
msg.index = sd.sdno; /* number of the subdisk */
dorename(&msg, sd.name, sdname, MAXSDNAME);
}
}
break;
case volume_object:
volno = msg.index;
dorename(&msg, oldname, newname, MAXVOLNAME);
if (recurse) {
int sdno;
int plexno;
get_volume_info(&vol, volno); /* find out who we are */
for (plexno = 0; plexno < vol.plexes; plexno++) {
char plexname[MAXVOLNAME + 8];
msg.type = plex_object;
sprintf(plexname, "%s.p%d", newname, plexno);
msg.index = vol.plex[plexno]; /* number of the plex */
dorename(&msg, plex.name, plexname, MAXPLEXNAME);
get_plex_info(&plex, vol.plex[plexno]); /* find out who we are */
msg.type = sd_object;
for (sdno = 0; sdno < plex.subdisks; sdno++) {
char sdname[MAXPLEXNAME + 8];
get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */
sprintf(sdname, "%s.s%d", plexname, sdno);
msg.index = sd.sdno; /* number of the subdisk */
dorename(&msg, sd.name, sdname, MAXSDNAME);
}
}
}
break;
default:
fprintf(stderr, "%s is not a Vinum object\n", oldname);
return;
}
}
void
vinum_rename(int argc, char *argv[], char *argv0[])
{
if (argc != 2) {
fprintf(stderr, "usage: \trename <object> <new name>\n");
return;
}
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Can't get vinum config");
return;
}
vinum_rename_2(argv[0], argv[1]);
checkupdates(); /* make sure we're updating */
}
/*
* Move objects:
*
* mv <dest> <src> ...
*/
void
vinum_mv(int argc, char *argv[], char *argv0[])
{
int i; /* loop index */
int srcobj;
int destobj;
enum objecttype srct;
enum objecttype destt;
int sdno;
struct _ioctl_reply reply;
struct vinum_ioctl_msg *msg = (struct vinum_ioctl_msg *) &reply;
if (argc < 2) {
fprintf(stderr, "usage: \tmove <dest> <src> ...\n");
return;
}
/* Get current config */
if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
perror("Cannot get vinum config\n");
return;
}
/* Get our destination */
destobj = find_object(argv[0], &destt);
if (destobj == -1) {
fprintf(stderr, "Can't find %s\n", argv[0]);
return;
}
/* Verify that the target is a drive */
if (destt != drive_object) {
fprintf(stderr, "%s is not a drive\n", argv[0]);
return;
}
for (i = 1; i < argc; i++) { /* for all the sources */
srcobj = find_object(argv[i], &srct);
if (srcobj == -1) {
fprintf(stderr, "Can't find %s\n", argv[i]);
continue;
}
msg->index = destobj;
switch (srct) { /* Handle the source object */
case drive_object: /* Move all subdisks on the drive to dst. */
get_drive_info(&drive, srcobj); /* get info on drive */
for (sdno = 0; sdno < vinum_conf.subdisks_allocated; ++sdno) {
get_sd_info(&sd, sdno);
if (sd.driveno == srcobj) {
msg->index = destobj;
msg->otherobject = sd.sdno;
if (ioctl(superdev, VINUM_MOVE, msg) < 0)
fprintf(stderr,
"Can't move %s (part of %s) to %s: %s (%d)\n",
sd.name,
drive.label.name,
argv[0],
strerror(reply.error),
reply.error);
}
}
break;
case sd_object:
msg->otherobject = srcobj;
if (ioctl(superdev, VINUM_MOVE, msg) < 0)
fprintf(stderr,
"Can't move %s to %s: %s (%d)\n",
sd.name,
argv[0],
strerror(reply.error),
reply.error);
break;
case plex_object:
get_plex_info(&plex, srcobj);
for (sdno = 0; sdno < plex.subdisks; ++sdno) {
get_plex_sd_info(&sd, plex.plexno, sdno);
msg->index = destobj;
msg->otherobject = sd.sdno;
if (ioctl(superdev, VINUM_MOVE, msg) < 0)
fprintf(stderr,
"Can't move %s (part of %s) to %s: %s (%d)\n",
sd.name,
plex.name,
argv[0],
strerror(reply.error),
reply.error);
}
break;
case volume_object:
case invalid_object:
default:
fprintf(stderr, "Can't move %s (inappropriate object).\n", argv[i]);
break;
}
if (reply.error)
fprintf(stderr,
"Can't move %s to %s: %s (%d)\n",
argv[i],
argv[0],
strerror(reply.error),
reply.error);
}
checkupdates(); /* make sure we're updating */
}
/*
* Replace objects. Not implemented, may never be.
*/
void
vinum_replace(int argc, char *argv[], char *argv0[])
{
fprintf(stderr, "'replace' not implemented yet. Use 'move' instead\n");
}
/* Primitive help function */
void
vinum_help(int argc, char *argv[], char *argv0[])
{
char commands[] =
{
"COMMANDS\n"
"attach plex volume [rename]\n"
"attach subdisk plex [offset] [rename]\n"
" Attach a plex to a volume, or a subdisk to a plex.\n"
"checkparity plex [-f] [-v]\n"
" Check the parity blocks of a RAID-4 or RAID-5 plex.\n"
"concat [-f] [-n name] [-v] drives\n"
" Create a concatenated volume from the specified drives.\n"
"create [-f] description-file\n"
" Create a volume as described in description-file.\n"
"debug Cause the volume manager to enter the kernel debugger.\n"
"debug flags\n"
" Set debugging flags.\n"
"detach [-f] [plex | subdisk]\n"
" Detach a plex or subdisk from the volume or plex to which it is\n"
" attached.\n"
"dumpconfig [drive ...]\n"
" List the configuration information stored on the specified\n"
" drives, or all drives in the system if no drive names are speci-\n"
" fied.\n"
"info [-v] [-V]\n"
" List information about volume manager state.\n"
"init [-S size] [-w] plex | subdisk\n"
" Initialize the contents of a subdisk or all the subdisks of a\n"
" plex to all zeros.\n"
"label volume\n"
" Create a volume label.\n"
"l | list [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n"
" List information about specified objects.\n"
"ld [-r] [-s] [-v] [-V] [volume]\n"
" List information about drives.\n"
"ls [-r] [-s] [-v] [-V] [subdisk]\n"
" List information about subdisks.\n"
"lp [-r] [-s] [-v] [-V] [plex]\n"
" List information about plexes.\n"
"lv [-r] [-s] [-v] [-V] [volume]\n"
" List information about volumes.\n"
"makedev\n"
" Remake the device nodes in /dev/vinum.\n"
"mirror [-f] [-n name] [-s] [-v] drives\n"
" Create a mirrored volume from the specified drives.\n"
"move | mv -f drive object ...\n"
" Move the object(s) to the specified drive.\n"
"printconfig [file]\n"
" Write a copy of the current configuration to file.\n"
"quit Exit the vinum program when running in interactive mode. Nor-\n"
" mally this would be done by entering the EOF character.\n"
"read disk ...\n"
" Read the vinum configuration from the specified disks.\n"
"rename [-r] [drive | subdisk | plex | volume] newname\n"
" Change the name of the specified object.\n"
"rebuildparity plex [-f] [-v] [-V]\n"
" Rebuild the parity blocks of a RAID-4 or RAID-5 plex.\n"
"resetconfig\n"
" Reset the complete vinum configuration.\n"
"resetstats [-r] [volume | plex | subdisk]\n"
" Reset statistics counters for the specified objects, or for all\n"
" objects if none are specified.\n"
"rm [-f] [-r] volume | plex | subdisk\n"
" Remove an object.\n"
"saveconfig\n"
" Save vinum configuration to disk after configuration failures.\n"
"setdaemon [value]\n"
" Set daemon configuration.\n"
"setstate state [volume | plex | subdisk | drive]\n"
" Set state without influencing other objects, for diagnostic pur-\n"
" poses only.\n"
"start Read configuration from all vinum drives.\n"
"start [-i interval] [-S size] [-w] volume | plex | subdisk\n"
" Allow the system to access the objects.\n"
"stop [-f] [volume | plex | subdisk]\n"
" Terminate access to the objects, or stop vinum if no parameters\n"
" are specified.\n"
"stripe [-f] [-n name] [-v] drives\n"
" Create a striped volume from the specified drives.\n"
};
puts(commands);
}
/* Set daemon options.
* XXX quick and dirty: use a bitmap, which requires
* knowing which bit does what. FIXME */
void
vinum_setdaemon(int argc, char *argv[], char *argv0[])
{
int options;
switch (argc) {
case 0:
if (ioctl(superdev, VINUM_GETDAEMON, &options) < 0)
fprintf(stderr, "Can't get daemon options: %s (%d)\n", strerror(errno), errno);
else
printf("Options mask: %d\n", options);
break;
case 1:
options = atoi(argv[0]);
if (ioctl(superdev, VINUM_SETDAEMON, &options) < 0)
fprintf(stderr, "Can't set daemon options: %s (%d)\n", strerror(errno), errno);
break;
default:
fprintf(stderr, "usage: \tsetdaemon [<bitmask>]\n");
}
checkupdates(); /* make sure we're updating */
}
/* Save config info */
void
vinum_saveconfig(int argc, char *argv[], char *argv0[])
{
int ioctltype;
if (argc != 0) {
printf("usage: saveconfig\n");
return;
}
ioctltype = 1; /* user saveconfig */
if (ioctl(superdev, VINUM_SAVECONFIG, &ioctltype) < 0)
fprintf(stderr, "Can't save configuration: %s (%d)\n", strerror(errno), errno);
checkupdates(); /* make sure we're updating */
}
/*
* Create a volume name for the quick and dirty
* commands. It will be of the form "vinum#",
* where # is a small positive number.
*/
void
genvolname()
{
int v; /* volume number */
static char volumename[MAXVOLNAME]; /* name to create */
enum objecttype type;
objectname = volumename; /* point to it */
for (v = 0;; v++) {
sprintf(objectname, "vinum%d", v); /* create the name */
if (find_object(objectname, &type) == -1) /* does it exist? */
return; /* no, it's ours */
}
}
/*
* Create a drive for the quick and dirty
* commands. The name will be of the form
* vinumdrive#, where # is a small positive
* number. Return the name of the drive.
*/
struct _drive *
create_drive(char *devicename)
{
int d; /* volume number */
static char drivename[MAXDRIVENAME]; /* name to create */
enum objecttype type;
struct _ioctl_reply *reply;
/*
* We're never likely to get anything
* like 10000 drives. The only reason for
* this limit is to stop the thing
* looping if we have a bug somewhere.
*/
for (d = 0; d < 100000; d++) { /* look for a free drive number */
sprintf(drivename, "vinumdrive%d", d); /* create the name */
if (find_object(drivename, &type) == -1) { /* does it exist? */
char command[MAXDRIVENAME * 2];
sprintf(command, "drive %s device %s", drivename, devicename); /* create a create command */
if (vflag)
printf("drive %s device %s\n", drivename, devicename); /* create a create command */
ioctl(superdev, VINUM_CREATE, command);
reply = (struct _ioctl_reply *) &command;
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create drive %s, device %s: %s\n",
drivename,
devicename,
reply->msg);
else
fprintf(stderr,
"Can't create drive %s, device %s: %s (%d)\n",
drivename,
devicename,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
find_object(drivename, &type);
return &drive; /* return the name of the drive */
}
}
fprintf(stderr, "Can't generate a drive name\n");
/* NOTREACHED */
return NULL;
}
/*
* Create a volume with a single concatenated plex from
* as much space as we can get on the specified drives.
* If the drives aren't Vinum drives, make them so.
*/
void
vinum_concat(int argc, char *argv[], char *argv0[])
{
int o; /* object number */
char buffer[BUFSIZE];
struct _drive *drive; /* drive we're currently looking at */
struct _ioctl_reply *reply;
int ioctltype;
int error;
enum objecttype type;
reply = (struct _ioctl_reply *) &buffer;
if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */
printf("Can't configure: %s (%d)\n", strerror(errno), errno);
return;
}
if (!objectname) /* we need a name for our object */
genvolname();
sprintf(buffer, "volume %s", objectname);
if (vflag)
printf("volume %s\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create volume %s: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create volume %s: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
sprintf(buffer, "plex name %s.p0 org concat", objectname);
if (vflag)
printf(" plex name %s.p0 org concat\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create plex %s.p0: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create plex %s.p0: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
for (o = 0; o < argc; o++) {
if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
drive = create_drive(argv[o]); /* create it */
sprintf(buffer, "sd name %s.p0.s%d drive %s size 0", objectname, o, drive->label.name);
if (vflag)
printf(" sd name %s.p0.s%d drive %s size 0\n", objectname, o, drive->label.name);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create subdisk %s.p0.s%d: %s\n",
objectname,
o,
reply->msg);
else
fprintf(stderr,
"Can't create subdisk %s.p0.s%d: %s (%d)\n",
objectname,
o,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
}
/* done, save the config */
ioctltype = 0; /* saveconfig after update */
error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */
if (error != 0)
perror("Can't save Vinum config");
find_object(objectname, &type); /* find the index of the volume */
if (no_devfs)
make_vol_dev(vol.volno, 1); /* and create the devices */
if (vflag) {
vflag--; /* XXX don't give too much detail */
find_object(objectname, &type); /* point to the volume */
vinum_lvi(vol.volno, 1); /* and print info about it */
}
}
/*
* Create a volume with a single striped plex from
* as much space as we can get on the specified drives.
* If the drives aren't Vinum drives, make them so.
*/
void
vinum_stripe(int argc, char *argv[], char *argv0[])
{
int o; /* object number */
char buffer[BUFSIZE];
struct _drive *drive; /* drive we're currently looking at */
struct _ioctl_reply *reply;
int ioctltype;
int error;
enum objecttype type;
off_t maxsize;
int fe; /* freelist entry index */
struct drive_freelist freelist;
struct ferq { /* request to pass to ioctl */
int driveno;
int fe;
} *ferq = (struct ferq *) &freelist;
u_int64_t bigchunk; /* biggest chunk in freelist */
maxsize = QUAD_MAX;
reply = (struct _ioctl_reply *) &buffer;
/*
* First, check our drives.
*/
if (argc < 2) {
fprintf(stderr, "You need at least two drives to create a striped plex\n");
return;
}
if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */
printf("Can't configure: %s (%d)\n", strerror(errno), errno);
return;
}
if (!objectname) /* we need a name for our object */
genvolname();
for (o = 0; o < argc; o++) {
if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
drive = create_drive(argv[o]); /* create it */
/* Now find the largest chunk available on the drive */
bigchunk = 0; /* ain't found nothin' yet */
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);
}
bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */
}
maxsize = min(maxsize, bigchunk); /* this is as much as we can do */
}
/* Now create the volume */
sprintf(buffer, "volume %s", objectname);
if (vflag)
printf("volume %s\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create volume %s: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create volume %s: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
sprintf(buffer, "plex name %s.p0 org striped 279k", objectname);
if (vflag)
printf(" plex name %s.p0 org striped 279k\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create plex %s.p0: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create plex %s.p0: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
for (o = 0; o < argc; o++) {
drive = find_drive_by_devname(argv[o]); /* we know it exists... */
sprintf(buffer,
"sd name %s.p0.s%d drive %s size %lldb",
objectname,
o,
drive->label.name,
(long long) maxsize);
if (vflag)
printf(" sd name %s.p0.s%d drive %s size %lldb\n",
objectname,
o,
drive->label.name,
(long long) maxsize);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create subdisk %s.p0.s%d: %s\n",
objectname,
o,
reply->msg);
else
fprintf(stderr,
"Can't create subdisk %s.p0.s%d: %s (%d)\n",
objectname,
o,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
}
/* done, save the config */
ioctltype = 0; /* saveconfig after update */
error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */
if (error != 0)
perror("Can't save Vinum config");
find_object(objectname, &type); /* find the index of the volume */
if (no_devfs)
make_vol_dev(vol.volno, 1); /* and create the devices */
if (vflag) {
vflag--; /* XXX don't give too much detail */
find_object(objectname, &type); /* point to the volume */
vinum_lvi(vol.volno, 1); /* and print info about it */
}
}
/*
* Create a volume with a single RAID-4 plex from
* as much space as we can get on the specified drives.
* If the drives aren't Vinum drives, make them so.
*/
void
vinum_raid4(int argc, char *argv[], char *argv0[])
{
int o; /* object number */
char buffer[BUFSIZE];
struct _drive *drive; /* drive we're currently looking at */
struct _ioctl_reply *reply;
int ioctltype;
int error;
enum objecttype type;
off_t maxsize;
int fe; /* freelist entry index */
struct drive_freelist freelist;
struct ferq { /* request to pass to ioctl */
int driveno;
int fe;
} *ferq = (struct ferq *) &freelist;
u_int64_t bigchunk; /* biggest chunk in freelist */
maxsize = QUAD_MAX;
reply = (struct _ioctl_reply *) &buffer;
/*
* First, check our drives.
*/
if (argc < 3) {
fprintf(stderr, "You need at least three drives to create a RAID-4 plex\n");
return;
}
if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */
printf("Can't configure: %s (%d)\n", strerror(errno), errno);
return;
}
if (!objectname) /* we need a name for our object */
genvolname();
for (o = 0; o < argc; o++) {
if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
drive = create_drive(argv[o]); /* create it */
/* Now find the largest chunk available on the drive */
bigchunk = 0; /* ain't found nothin' yet */
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);
}
bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */
}
maxsize = min(maxsize, bigchunk); /* this is as much as we can do */
}
/* Now create the volume */
sprintf(buffer, "volume %s", objectname);
if (vflag)
printf("volume %s\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create volume %s: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create volume %s: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
sprintf(buffer, "plex name %s.p0 org raid4 279k", objectname);
if (vflag)
printf(" plex name %s.p0 org raid4 279k\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create plex %s.p0: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create plex %s.p0: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
for (o = 0; o < argc; o++) {
drive = find_drive_by_devname(argv[o]); /* we know it exists... */
sprintf(buffer,
"sd name %s.p0.s%d drive %s size %lldb",
objectname,
o,
drive->label.name,
(long long) maxsize);
if (vflag)
printf(" sd name %s.p0.s%d drive %s size %lldb\n",
objectname,
o,
drive->label.name,
(long long) maxsize);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create subdisk %s.p0.s%d: %s\n",
objectname,
o,
reply->msg);
else
fprintf(stderr,
"Can't create subdisk %s.p0.s%d: %s (%d)\n",
objectname,
o,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
}
/* done, save the config */
ioctltype = 0; /* saveconfig after update */
error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */
if (error != 0)
perror("Can't save Vinum config");
find_object(objectname, &type); /* find the index of the volume */
if (no_devfs)
make_vol_dev(vol.volno, 1); /* and create the devices */
if (vflag) {
vflag--; /* XXX don't give too much detail */
find_object(objectname, &type); /* point to the volume */
vinum_lvi(vol.volno, 1); /* and print info about it */
}
}
/*
* Create a volume with a single RAID-4 plex from
* as much space as we can get on the specified drives.
* If the drives aren't Vinum drives, make them so.
*/
void
vinum_raid5(int argc, char *argv[], char *argv0[])
{
int o; /* object number */
char buffer[BUFSIZE];
struct _drive *drive; /* drive we're currently looking at */
struct _ioctl_reply *reply;
int ioctltype;
int error;
enum objecttype type;
off_t maxsize;
int fe; /* freelist entry index */
struct drive_freelist freelist;
struct ferq { /* request to pass to ioctl */
int driveno;
int fe;
} *ferq = (struct ferq *) &freelist;
u_int64_t bigchunk; /* biggest chunk in freelist */
maxsize = QUAD_MAX;
reply = (struct _ioctl_reply *) &buffer;
/*
* First, check our drives.
*/
if (argc < 3) {
fprintf(stderr, "You need at least three drives to create a RAID-5 plex\n");
return;
}
if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */
printf("Can't configure: %s (%d)\n", strerror(errno), errno);
return;
}
if (!objectname) /* we need a name for our object */
genvolname();
for (o = 0; o < argc; o++) {
if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
drive = create_drive(argv[o]); /* create it */
/* Now find the largest chunk available on the drive */
bigchunk = 0; /* ain't found nothin' yet */
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);
}
bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */
}
maxsize = min(maxsize, bigchunk); /* this is as much as we can do */
}
/* Now create the volume */
sprintf(buffer, "volume %s", objectname);
if (vflag)
printf("volume %s\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create volume %s: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create volume %s: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
sprintf(buffer, "plex name %s.p0 org raid5 279k", objectname);
if (vflag)
printf(" plex name %s.p0 org raid5 279k\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create plex %s.p0: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create plex %s.p0: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
for (o = 0; o < argc; o++) {
drive = find_drive_by_devname(argv[o]); /* we know it exists... */
sprintf(buffer,
"sd name %s.p0.s%d drive %s size %lldb",
objectname,
o,
drive->label.name,
(long long) maxsize);
if (vflag)
printf(" sd name %s.p0.s%d drive %s size %lldb\n",
objectname,
o,
drive->label.name,
(long long) maxsize);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create subdisk %s.p0.s%d: %s\n",
objectname,
o,
reply->msg);
else
fprintf(stderr,
"Can't create subdisk %s.p0.s%d: %s (%d)\n",
objectname,
o,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
}
/* done, save the config */
ioctltype = 0; /* saveconfig after update */
error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */
if (error != 0)
perror("Can't save Vinum config");
find_object(objectname, &type); /* find the index of the volume */
if (no_devfs)
make_vol_dev(vol.volno, 1); /* and create the devices */
if (vflag) {
vflag--; /* XXX don't give too much detail */
find_object(objectname, &type); /* point to the volume */
vinum_lvi(vol.volno, 1); /* and print info about it */
}
}
/*
* Create a volume with a two plexes from as much space
* as we can get on the specified drives. If the
* drives aren't Vinum drives, make them so.
*
* The number of drives must be even, and at least 4
* for a striped plex. Specify striped plexes with the
* -s flag; otherwise they will be concatenated. It's
* possible that the two plexes may differ in length.
*/
void
vinum_mirror(int argc, char *argv[], char *argv0[])
{
int o; /* object number */
int p; /* plex number */
char buffer[BUFSIZE];
struct _drive *drive; /* drive we're currently looking at */
struct _ioctl_reply *reply;
int ioctltype;
int error;
enum objecttype type;
off_t maxsize[2]; /* maximum subdisk size for striped plexes */
int fe; /* freelist entry index */
struct drive_freelist freelist;
struct ferq { /* request to pass to ioctl */
int driveno;
int fe;
} *ferq = (struct ferq *) &freelist;
u_int64_t bigchunk; /* biggest chunk in freelist */
if (sflag) /* striped, */
maxsize[0] = maxsize[1] = QUAD_MAX; /* we need to calculate sd size */
else
maxsize[0] = maxsize[1] = 0; /* let the kernel routines do it */
reply = (struct _ioctl_reply *) &buffer;
/*
* First, check our drives.
*/
if ((argc < 2)
|| (argc & 1)) {
fprintf(stderr, "You need an even number of drives to create a mirrored volume\n");
return;
}
if (sflag && (argc < 4)) {
fprintf(stderr, "You need at least 4 drives to create a mirrored, striped volume\n");
return;
}
if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */
printf("Can't configure: %s (%d)\n", strerror(errno), errno);
return;
}
if (!objectname) /* we need a name for our object */
genvolname();
for (o = 0; o < argc; o++) {
if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
drive = create_drive(argv[o]); /* create it */
if (sflag) { /* striping, */
/* Find the largest chunk available on the drive */
bigchunk = 0; /* ain't found nothin' yet */
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);
}
bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */
}
maxsize[o & 1] = min(maxsize[o & 1], bigchunk); /* get the maximum size of a subdisk */
}
}
/* Now create the volume */
sprintf(buffer, "volume %s setupstate", objectname);
if (vflag)
printf("volume %s setupstate\n", objectname);
ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create volume %s: %s\n",
objectname,
reply->msg);
else
fprintf(stderr,
"Can't create volume %s: %s (%d)\n",
objectname,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
for (p = 0; p < 2; p++) { /* create each plex */
if (sflag) {
sprintf(buffer, "plex name %s.p%d org striped 279k", objectname, p);
if (vflag)
printf(" plex name %s.p%d org striped 279k\n", objectname, p);
} else { /* concat */
sprintf(buffer, "plex name %s.p%d org concat", objectname, p);
if (vflag)
printf(" plex name %s.p%d org concat\n", objectname, p);
}
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create plex %s.p%d: %s\n",
objectname,
p,
reply->msg);
else
fprintf(stderr,
"Can't create plex %s.p%d: %s (%d)\n",
objectname,
p,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
/* Now look at the subdisks */
for (o = p; o < argc; o += 2) { /* every second one */
drive = find_drive_by_devname(argv[o]); /* we know it exists... */
sprintf(buffer,
"sd name %s.p%d.s%d drive %s size %lldb",
objectname,
p,
o >> 1,
drive->label.name,
(long long) maxsize[p]);
if (vflag)
printf(" sd name %s.p%d.s%d drive %s size %lldb\n",
objectname,
p,
o >> 1,
drive->label.name,
(long long) maxsize[p]);
ioctl(superdev, VINUM_CREATE, buffer);
if (reply->error != 0) { /* error in config */
if (reply->msg[0])
fprintf(stderr,
"Can't create subdisk %s.p%d.s%d: %s\n",
objectname,
p,
o >> 1,
reply->msg);
else
fprintf(stderr,
"Can't create subdisk %s.p%d.s%d: %s (%d)\n",
objectname,
p,
o >> 1,
strerror(reply->error),
reply->error);
longjmp(command_fail, -1); /* give up */
}
}
}
/* done, save the config */
ioctltype = 0; /* saveconfig after update */
error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */
if (error != 0)
perror("Can't save Vinum config");
find_object(objectname, &type); /* find the index of the volume */
if (no_devfs)
make_vol_dev(vol.volno, 1); /* and create the devices */
if (vflag) {
vflag--; /* XXX don't give too much detail */
sflag = 0; /* no stats, please */
find_object(objectname, &type); /* point to the volume */
vinum_lvi(vol.volno, 1); /* and print info about it */
}
}
void
vinum_readpol(int argc, char *argv[], char *argv0[])
{
int object;
struct _ioctl_reply reply;
struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
enum objecttype type;
struct _plex plex;
struct _volume vol;
int plexno;
if (argc != 2) {
fprintf(stderr, "usage: readpol <volume> <plex> | round\n");
return;
}
object = find_object(argv[0], &type); /* look for it */
if (type != volume_object) {
fprintf(stderr, "%s is not a volume\n", argv[0]);
return;
}
get_volume_info(&vol, object);
if (strcmp(argv[1], "round")) { /* not 'round' */
object = find_object(argv[1], &type); /* look for it */
if (type != plex_object) {
fprintf(stderr, "%s is not a plex\n", argv[1]);
return;
}
get_plex_info(&plex, object);
plexno = plex.plexno;
} else /* round */
plexno = -1;
/* Set the value */
message->index = vol.volno;
message->otherobject = plexno;
ioctl(superdev, VINUM_READPOL, message);
if (reply.error)
fprintf(stderr,
"Can't set read policy: %s (%d)\n",
reply.msg[0] ? reply.msg : strerror(reply.error),
reply.error);
if (vflag)
vinum_lpi(plexno, recurse);
}
/*
* Brute force set state function. Don't look at
* any dependencies, just do it.
*/
void
vinum_setstate(int argc, char *argv[], char *argv0[])
{
int object;
struct _ioctl_reply reply;
struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
int index;
enum objecttype type;
int state;
for (index = 1; 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 {
int doit = 0; /* set to 1 if we pass our tests */
switch (type) {
case drive_object:
state = DriveState(argv[0]); /* get the state */
if (drive.state == state) /* already in that state */
fprintf(stderr, "%s is already %s\n", drive.label.name, argv[0]);
else
doit = 1;
break;
case sd_object:
state = SdState(argv[0]); /* get the state */
if (sd.state == state) /* already in that state */
fprintf(stderr, "%s is already %s\n", sd.name, argv[0]);
else
doit = 1;
break;
case plex_object:
state = PlexState(argv[0]); /* get the state */
if (plex.state == state) /* already in that state */
fprintf(stderr, "%s is already %s\n", plex.name, argv[0]);
else
doit = 1;
break;
case volume_object:
state = VolState(argv[0]); /* get the state */
if (vol.state == state) /* already in that state */
fprintf(stderr, "%s is already %s\n", vol.name, argv[0]);
else
doit = 1;
break;
default:
state = 0; /* to keep the compiler happy */
}
if (state == -1)
fprintf(stderr, "Invalid state for object: %s\n", argv[0]);
else if (doit) {
message->index = object; /* pass object number */
message->type = type; /* and type of object */
message->state = state;
message->force = force; /* don't force it, use a larger hammer */
ioctl(superdev, VINUM_SETSTATE_FORCE, message);
if (reply.error != 0)
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_checkparity(int argc, char *argv[], char *argv0[])
{
Verbose = vflag; /* accept -v for verbose */
if (argc == 0) /* no parameters? */
fprintf(stderr, "usage: checkparity object [object...]\n");
else
parityops(argc, argv, checkparity);
}
void
vinum_rebuildparity(int argc, char *argv[], char *argv0[])
{
if (argc == 0) /* no parameters? */
fprintf(stderr, "usage: rebuildparity object [object...]\n");
else
parityops(argc, argv, vflag ? rebuildandcheckparity : rebuildparity);
}
/*
* Common code for rebuildparity and checkparity.
* We bend the meanings of some flags here:
*
* -v: Report incorrect parity on rebuild.
* -V: Show running count of position being checked.
* -f: Start from beginning of the plex.
*/
void
parityops(int argc, char *argv[], enum parityop op)
{
int object;
struct _plex plex;
struct _ioctl_reply reply;
struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
int index;
enum objecttype type;
char *msg;
off_t block;
if (op == checkparity)
msg = "Checking";
else
msg = "Rebuilding";
for (index = 0; index < argc; index++) {
object = find_object(argv[index], &type); /* look for it */
if (type != plex_object)
fprintf(stderr, "%s is not a plex\n", argv[index]);
else {
get_plex_info(&plex, object);
if (!isparity((&plex)))
fprintf(stderr, "%s is not a RAID-4 or RAID-5 plex\n", argv[index]);
else {
do {
message->index = object; /* pass object number */
message->type = type; /* and type of object */
message->op = op; /* what to do */
if (force)
message->offset = 0; /* start at the beginning */
else
message->offset = plex.checkblock; /* continue where we left off */
force = 0; /* don't reset after the first time */
ioctl(superdev, VINUM_PARITYOP, message);
get_plex_info(&plex, object);
if (Verbose) {
block = (plex.checkblock << DEV_BSHIFT) * (plex.subdisks - 1);
if (block != 0)
printf("\r%s at %s (%d%%) ",
msg,
roughlength(block, 1),
((int) (block * 100 / plex.length) >> DEV_BSHIFT));
if ((reply.error == EAGAIN)
&& (reply.msg[0])) /* got a comment back */
fputs(reply.msg, stderr); /* show it */
fflush(stdout);
}
}
while (reply.error == EAGAIN);
if (reply.error != 0) {
if (reply.msg[0])
fputs(reply.msg, stderr);
else
fprintf(stderr,
"%s failed: %s\n",
msg,
strerror(reply.error));
} else if (Verbose) {
if (op == checkparity)
fprintf(stderr, "%s has correct parity\n", argv[index]);
else
fprintf(stderr, "Rebuilt parity on %s\n", argv[index]);
}
}
}
}
}
/* Local Variables: */
/* fill-column: 50 */
/* End: */