79f5327ef7
shadowing warnings for variables with the same name used both in global and local context. Really this needs either to rename all the global variables something different (prefix them with gl_ or something) or even better get rid of them altogether.
2388 lines
71 KiB
C
2388 lines
71 KiB
C
/* $NetBSD: commands.c,v 1.4 2005/06/26 22:36:55 christos Exp $ */
|
|
|
|
/* 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.4 2005/06/26 22:36:55 christos Exp $
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include "vext.h"
|
|
|
|
__RCSID("$NetBSD: commands.c,v 1.4 2005/06/26 22:36:55 christos Exp $");
|
|
|
|
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 xbuffer[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, */
|
|
const char *editor; /* editor to start */
|
|
int status;
|
|
|
|
editor = getenv("EDITOR");
|
|
if (editor == NULL)
|
|
editor = "/usr/bin/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(xbuffer, BUFSIZE, dfd);
|
|
if (History)
|
|
fprintf(History, "%s", xbuffer);
|
|
|
|
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, xbuffer);
|
|
strcpy(commandline, xbuffer); /* make a copy */
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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");
|
|
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 xbuffer[BUFSIZE]; /* read config file in here */
|
|
struct _ioctl_reply *reply;
|
|
int i;
|
|
|
|
reply = (struct _ioctl_reply *) xbuffer;
|
|
if (argc < 1) { /* wrong arg count */
|
|
fprintf(stderr, "usage: read drive [drive ...]\n");
|
|
return;
|
|
}
|
|
strcpy(xbuffer, "read ");
|
|
for (i = 0; i < argc; i++) { /* each drive name */
|
|
strcat(xbuffer, argv[i]);
|
|
strcat(xbuffer, " ");
|
|
}
|
|
|
|
if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */
|
|
fprintf(stderr, "Can't configure: %s (%d)\n", strerror(errno), errno);
|
|
return;
|
|
}
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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();
|
|
}
|
|
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 idx;
|
|
enum objecttype type;
|
|
|
|
for (idx = 0; idx < argc; idx++) {
|
|
object = find_object(argv[idx], &type); /* look for it */
|
|
if (type == invalid_object)
|
|
fprintf(stderr, "Can't find object: %s\n", argv[idx]);
|
|
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[idx],
|
|
reply.msg[0] ? reply.msg : strerror(reply.error),
|
|
reply.error);
|
|
} else if (vflag)
|
|
fprintf(stderr, "%s removed\n", argv[idx]);
|
|
}
|
|
}
|
|
checkupdates(); /* make sure we're updating */
|
|
/* Arguably we should be cleverer about this. */
|
|
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 {
|
|
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 objidx;
|
|
int objno;
|
|
enum objecttype type; /* type returned */
|
|
|
|
if (History)
|
|
fflush(History); /* don't let all the kids do it. */
|
|
for (objidx = 0; objidx < argc; objidx++) {
|
|
objno = find_object(argv[objidx], &type); /* find the object */
|
|
if (objno < 0)
|
|
printf("Can't find %s\n", argv[objidx]);
|
|
else {
|
|
switch (type) {
|
|
case volume_object:
|
|
initvol(objno);
|
|
break;
|
|
|
|
case plex_object:
|
|
initplex(objno, argv[objidx]);
|
|
break;
|
|
|
|
case sd_object:
|
|
initsd(objno, dowait);
|
|
break;
|
|
|
|
default:
|
|
printf("Can't initialize %s: wrong object type\n", argv[objidx]);
|
|
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 = 0; /* 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) {
|
|
#if 0
|
|
message->index = plexno; /* pass object number */
|
|
message->type = plex_object; /* and type of object */
|
|
message->state = object_up;
|
|
message->force = 1; /* insist */
|
|
ioctl(superdev, VINUM_SETSTATE, message);
|
|
#endif
|
|
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 dowaitx)
|
|
{
|
|
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 (dowaitx == 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 (!dowaitx)
|
|
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 FIXME */
|
|
fprintf(stderr, "Sorry, you must specify the object names at the moment\n");
|
|
else { /* start specified objects */
|
|
int idx;
|
|
enum objecttype type;
|
|
|
|
for (idx = 0; idx < argc; idx++) {
|
|
object = find_object(argv[idx], &type); /* look for it */
|
|
if (type == invalid_object)
|
|
fprintf(stderr, "Can't find object: %s\n", argv[idx]);
|
|
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;
|
|
case invalid_object:
|
|
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[idx],
|
|
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 */
|
|
/* XXX FIXME */
|
|
fprintf(stderr, "Sorry, you can't stop this version of Vinum\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);
|
|
}
|
|
}
|
|
}
|
|
checkupdates(); /* not updating? */
|
|
}
|
|
|
|
void
|
|
reset_volume_stats(int volno, int rec)
|
|
{
|
|
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 (rec) {
|
|
struct _volume vl;
|
|
int plexno;
|
|
|
|
get_volume_info(&vl, volno);
|
|
for (plexno = 0; plexno < vol.plexes; plexno++)
|
|
reset_plex_stats(vol.plex[plexno], rec);
|
|
}
|
|
}
|
|
|
|
void
|
|
reset_plex_stats(int plexno, int rec)
|
|
{
|
|
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 (rec) {
|
|
struct _plex plx;
|
|
struct _sd sdx;
|
|
int sdno;
|
|
|
|
get_plex_info(&plx, plexno);
|
|
for (sdno = 0; sdno < plx.subdisks; sdno++) {
|
|
get_plex_sd_info(&sdx, plx.plexno, sdno);
|
|
reset_sd_stats(sdx.sdno, rec);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
reset_sd_stats(int sdno, int rec)
|
|
{
|
|
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 (rec) {
|
|
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 ren = 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")) {
|
|
ren = 1;
|
|
msg.rename = 1; /* do renaming */
|
|
} else if (!isdigit((unsigned char)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 (ren) {
|
|
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;
|
|
case drive_object:
|
|
case invalid_object:
|
|
case volume_object:
|
|
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 plexnox;
|
|
|
|
get_volume_info(&vol, volno); /* find out who we are */
|
|
for (plexnox = 0; plexnox < vol.plexes; plexnox++) {
|
|
char plexname[MAXVOLNAME + 8];
|
|
|
|
msg.type = plex_object;
|
|
sprintf(plexname, "%s.p%d", newname, plexnox);
|
|
msg.index = vol.plex[plexnox]; /* number of the plex */
|
|
dorename(&msg, plex.name, plexname, MAXPLEXNAME);
|
|
get_plex_info(&plex, vol.plex[plexnox]); /* 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 idx */
|
|
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 xbuffer[BUFSIZE];
|
|
struct _drive *drv; /* drive we're currently looking at */
|
|
struct _ioctl_reply *reply;
|
|
int ioctltype;
|
|
int error;
|
|
enum objecttype type;
|
|
|
|
reply = (struct _ioctl_reply *) xbuffer;
|
|
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(xbuffer, "volume %s", objectname);
|
|
if (vflag)
|
|
printf("volume %s\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer); /* 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(xbuffer, "plex name %s.p0 org concat", objectname);
|
|
if (vflag)
|
|
printf(" plex name %s.p0 org concat\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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 ((drv = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
|
|
drv = create_drive(argv[o]); /* create it */
|
|
sprintf(xbuffer, "sd name %s.p0.s%d drive %s size 0", objectname, o, drv->label.name);
|
|
if (vflag)
|
|
printf(" sd name %s.p0.s%d drive %s size 0\n", objectname, o, drv->label.name);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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 idx of the volume */
|
|
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 xbuffer[BUFSIZE];
|
|
struct _drive *drv; /* drive we're currently looking at */
|
|
struct _ioctl_reply *reply;
|
|
int ioctltype;
|
|
int error;
|
|
enum objecttype type;
|
|
off_t maxsize;
|
|
int fe; /* freelist entry idx */
|
|
struct drive_freelist freelist;
|
|
struct ferq { /* request to pass to ioctl */
|
|
int driveno;
|
|
int fe;
|
|
} *ferq = (void *) &freelist;
|
|
u_int64_t bigchunk; /* biggest chunk in freelist */
|
|
|
|
maxsize = QUAD_MAX;
|
|
reply = (struct _ioctl_reply *) xbuffer;
|
|
|
|
/*
|
|
* 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 ((drv = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
|
|
drv = 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 < drv->freelist_entries; fe++) {
|
|
ferq->driveno = drv->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(xbuffer, "volume %s", objectname);
|
|
if (vflag)
|
|
printf("volume %s\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer); /* 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(xbuffer, "plex name %s.p0 org striped 279k", objectname);
|
|
if (vflag)
|
|
printf(" plex name %s.p0 org striped 279k\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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++) {
|
|
drv = find_drive_by_devname(argv[o]); /* we know it exists... */
|
|
sprintf(xbuffer,
|
|
"sd name %s.p0.s%d drive %s size %lldb",
|
|
objectname,
|
|
o,
|
|
drv->label.name,
|
|
(long long) maxsize);
|
|
if (vflag)
|
|
printf(" sd name %s.p0.s%d drive %s size %lldb\n",
|
|
objectname,
|
|
o,
|
|
drv->label.name,
|
|
(long long) maxsize);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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 idx of the volume */
|
|
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 xbuffer[BUFSIZE];
|
|
struct _drive *drv; /* drive we're currently looking at */
|
|
struct _ioctl_reply *reply;
|
|
int ioctltype;
|
|
int error;
|
|
enum objecttype type;
|
|
off_t maxsize;
|
|
int fe; /* freelist entry idx */
|
|
struct drive_freelist freelist;
|
|
struct ferq { /* request to pass to ioctl */
|
|
int driveno;
|
|
int fe;
|
|
} *ferq = (void *) &freelist;
|
|
u_int64_t bigchunk; /* biggest chunk in freelist */
|
|
|
|
maxsize = QUAD_MAX;
|
|
reply = (struct _ioctl_reply *) xbuffer;
|
|
|
|
/*
|
|
* 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 ((drv = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
|
|
drv = 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 < drv->freelist_entries; fe++) {
|
|
ferq->driveno = drv->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(xbuffer, "volume %s", objectname);
|
|
if (vflag)
|
|
printf("volume %s\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer); /* 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(xbuffer, "plex name %s.p0 org raid4 279k", objectname);
|
|
if (vflag)
|
|
printf(" plex name %s.p0 org raid4 279k\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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++) {
|
|
drv = find_drive_by_devname(argv[o]); /* we know it exists... */
|
|
sprintf(xbuffer,
|
|
"sd name %s.p0.s%d drive %s size %lldb",
|
|
objectname,
|
|
o,
|
|
drv->label.name,
|
|
(long long) maxsize);
|
|
if (vflag)
|
|
printf(" sd name %s.p0.s%d drive %s size %lldb\n",
|
|
objectname,
|
|
o,
|
|
drv->label.name,
|
|
(long long) maxsize);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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 idx of the volume */
|
|
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 xbuffer[BUFSIZE];
|
|
struct _drive *drv; /* drive we're currently looking at */
|
|
struct _ioctl_reply *reply;
|
|
int ioctltype;
|
|
int error;
|
|
enum objecttype type;
|
|
off_t maxsize;
|
|
int fe; /* freelist entry idx */
|
|
struct drive_freelist freelist;
|
|
struct ferq { /* request to pass to ioctl */
|
|
int driveno;
|
|
int fe;
|
|
} *ferq = (void *) &freelist;
|
|
u_int64_t bigchunk; /* biggest chunk in freelist */
|
|
|
|
maxsize = QUAD_MAX;
|
|
reply = (struct _ioctl_reply *) xbuffer;
|
|
|
|
/*
|
|
* 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 ((drv = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
|
|
drv = 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 < drv->freelist_entries; fe++) {
|
|
ferq->driveno = drv->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(xbuffer, "volume %s", objectname);
|
|
if (vflag)
|
|
printf("volume %s\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer); /* 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(xbuffer, "plex name %s.p0 org raid5 279k", objectname);
|
|
if (vflag)
|
|
printf(" plex name %s.p0 org raid5 279k\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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++) {
|
|
drv = find_drive_by_devname(argv[o]); /* we know it exists... */
|
|
sprintf(xbuffer,
|
|
"sd name %s.p0.s%d drive %s size %lldb",
|
|
objectname,
|
|
o,
|
|
drv->label.name,
|
|
(long long) maxsize);
|
|
if (vflag)
|
|
printf(" sd name %s.p0.s%d drive %s size %lldb\n",
|
|
objectname,
|
|
o,
|
|
drv->label.name,
|
|
(long long) maxsize);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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 idx of the volume */
|
|
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 xbuffer[BUFSIZE];
|
|
struct _drive *drv; /* 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 idx */
|
|
struct drive_freelist freelist;
|
|
struct ferq { /* request to pass to ioctl */
|
|
int driveno;
|
|
int fe;
|
|
} *ferq = (void *) &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 *) xbuffer;
|
|
|
|
/*
|
|
* 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 ((drv = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */
|
|
drv = 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 < drv->freelist_entries; fe++) {
|
|
ferq->driveno = drv->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(xbuffer, "volume %s setupstate", objectname);
|
|
if (vflag)
|
|
printf("volume %s setupstate\n", objectname);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer); /* 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(xbuffer, "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(xbuffer, "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, xbuffer);
|
|
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 */
|
|
drv = find_drive_by_devname(argv[o]); /* we know it exists... */
|
|
sprintf(xbuffer,
|
|
"sd name %s.p%d.s%d drive %s size %lldb",
|
|
objectname,
|
|
p,
|
|
o >> 1,
|
|
drv->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,
|
|
drv->label.name,
|
|
(long long) maxsize[p]);
|
|
ioctl(superdev, VINUM_CREATE, xbuffer);
|
|
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 idx of the volume */
|
|
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 plx;
|
|
struct _volume vl;
|
|
int plexno;
|
|
|
|
if (argc == 0) { /* start everything */
|
|
fprintf(stderr, "usage: readpol <volume> <plex>|round\n");
|
|
return;
|
|
}
|
|
object = find_object(argv[1], &type); /* look for it */
|
|
if (type != volume_object) {
|
|
fprintf(stderr, "%s is not a volume\n", argv[1]);
|
|
return;
|
|
}
|
|
get_volume_info(&vl, object);
|
|
if (strcmp(argv[2], "round")) { /* not 'round' */
|
|
object = find_object(argv[2], &type); /* look for it */
|
|
if (type != plex_object) {
|
|
fprintf(stderr, "%s is not a plex\n", argv[2]);
|
|
return;
|
|
}
|
|
get_plex_info(&plx, object);
|
|
plexno = plx.plexno;
|
|
} else /* round */
|
|
plexno = -1;
|
|
|
|
/* Set the value */
|
|
message->index = vl.volno;
|
|
message->otherobject = plexno;
|
|
if (ioctl(superdev, VINUM_READPOL, message) < 0)
|
|
fprintf(stderr, "Can't set read policy: %s (%d)\n", strerror(errno), errno);
|
|
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 idx;
|
|
enum objecttype type;
|
|
int state;
|
|
|
|
for (idx = 1; idx < argc; idx++) {
|
|
object = find_object(argv[idx], &type); /* look for it */
|
|
if (type == invalid_object)
|
|
fprintf(stderr, "Can't find object: %s\n", argv[idx]);
|
|
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[idx],
|
|
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 plx;
|
|
struct _ioctl_reply reply;
|
|
struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply;
|
|
int idx;
|
|
enum objecttype type;
|
|
const char *msg;
|
|
off_t block;
|
|
|
|
if (op == checkparity)
|
|
msg = "Checking";
|
|
else
|
|
msg = "Rebuilding";
|
|
for (idx = 0; idx < argc; idx++) {
|
|
object = find_object(argv[idx], &type); /* look for it */
|
|
if (type != plex_object)
|
|
fprintf(stderr, "%s is not a plex\n", argv[idx]);
|
|
else {
|
|
get_plex_info(&plx, object);
|
|
if (!isparity((&plx)))
|
|
fprintf(stderr, "%s is not a RAID-4 or RAID-5 plex\n", argv[idx]);
|
|
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 = plx.checkblock; /* continue where we left off */
|
|
force = 0; /* don't reset after the first time */
|
|
ioctl(superdev, VINUM_PARITYOP, message);
|
|
get_plex_info(&plx, object);
|
|
if (Verbose) {
|
|
block = (plx.checkblock << DEV_BSHIFT) * (plx.subdisks - 1);
|
|
if (block != 0)
|
|
printf("\r%s at %s (%d%%) ",
|
|
msg,
|
|
roughlength(block, 1),
|
|
((int) (block * 100 / plx.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[idx]);
|
|
else
|
|
fprintf(stderr, "Rebuilt parity on %s\n", argv[idx]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Local Variables: */
|
|
/* fill-column: 50 */
|
|
/* End: */
|