FreeBSD-5.3/sbin/vinum/commands.c

Compare this file to the similar file:
Show the results in this format:

/* 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: src/sbin/vinum/commands.c,v 1.60 2004/07/24 19:11:40 le Exp $
 */

#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");
    listconfig();
    checkupdates();					    /* make sure we're updating */
}

/* Read vinum config from a disk */
void
vinum_read(int argc, char *argv[], char *arg0[])
{
    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 */
	    strlcat(buffer, argv[i], sizeof(buffer));
	    strlcat(buffer, " ", sizeof(buffer));
	}
    }
    ioctl(superdev, VINUM_READCONFIG, &buffer);
    if (reply->error != 0) 				    /* error in config */
	fprintf(stdout, "** %s: %s\n", reply->msg, strerror(reply->error));
    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. */
    }
}

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 {
		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 = 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) {
	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] [drive]\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"
	"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 (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 (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 (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 (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 (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: */