root/sbin/vinum/commands.c
/* commands.c: vinum interface program, main commands */
/*-
 * Copyright (c) 1997, 1998
 *      Nan Yang Computer Services Limited.  All rights reserved.
 *
 *  Written by Greg Lehey
 *
 *  This software is distributed under the so-called ``Berkeley
 *  License'':
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. 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.14 2000/11/14 20:01:23 grog Exp grog $
 * $FreeBSD: src/sbin/vinum/commands.c,v 1.31.2.6 2003/06/06 05:13:29 grog Exp $
 */

#define _KERNEL_STRUCTURES

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <fstab.h>
#include <netdb.h>
#include <paths.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <dev/raid/vinum/vinumhdr.h>
#include <dev/raid/vinum/request.h>
#include "vext.h"
#include <sys/types.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/wait.h>
#include <readline/readline.h>
#include <devstat.h>

static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen);
static void reparse(char *buf, char *tmp);

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 = "/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(buffer, BUFSIZE, dfd);

        if (configline == NULL) {
            if (ferror(dfd))
                perror("Can't read config file");
            break;
        }
        if (hist)
            fprintf(hist, "%s", buffer);
        file_line++;                                        /* count the lines */

        reparse(buffer, commandline);

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

/* Read vinum config from a disk */
void
vinum_read(int argc, char *argv[], char *arg0[])
{
    int error;
    char buffer[BUFSIZE];                                   /* read config file in here */
    struct _ioctl_reply *reply;
    int i;

    reply = (struct _ioctl_reply *) &buffer;
    if (argc < 1) {                                         /* wrong arg count */
        fprintf(stderr, "Usage: read drive [drive ...]\n");
        return;
    }
    strcpy(buffer, "read ");
    for (i = 0; i < argc; i++) {                            /* each drive name */
        strcat(buffer, argv[i]);
        strcat(buffer, " ");
    }

    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, &buffer);
    if (reply->error != 0) {                                /* error in config */
        fprintf(stdout, "** %s: %s\n", reply->msg, strerror(reply->error));
        error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */
        if (error != 0)
            perror("Can't save Vinum config");
    } else {
        error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */
        if (error != 0)
            perror("Can't save Vinum config");
        make_devices();
    }
    checkupdates();                                         /* make sure we're updating */
}

#ifdef VINUMDEBUG
void
vinum_debug(int argc, char *argv[], char *arg0[])
{
    struct debuginfo info;

    if (argc > 0) {
        info.param = atoi(argv[0]);
        info.changeit = 1;
    } else {
        info.changeit = 0;
        sleep(2);                                           /* give a chance to leave the window */
    }
    ioctl(superdev, VINUM_DEBUG, (caddr_t) & info);
}
#endif

void
vinum_modify(int argc, char *argv[], char *arg0[])
{
    fprintf(stderr, "Modify command is currently not implemented\n");
    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 */
    }
}

void
vinum_resetconfig(int argc, char *argv[], char *arg0[])
{
    char reply[32];
    int error;

    if (! isatty (STDIN_FILENO)) {
        fprintf (stderr, "Please enter this command from a tty device\n");
        return;
    }
    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 */
}

/* 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 (hist)
            fflush(hist);                                   /* 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) {
#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 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 */

    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;
    }
    openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN);
    get_sd_info(&sd, sdno);
    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 */
        int devs = getnumdevs();
        struct statinfo statinfo;
        char *namelist;
        char *enamelist;                                    /* end of name list */
        int i;
        char **token;                                       /* list of tokens */
        int tokens;                                         /* and their number */

        bzero(&statinfo, sizeof(struct statinfo));
        statinfo.dinfo = malloc(devs * sizeof(struct statinfo));
        namelist = malloc(devs * (DEVSTAT_NAME_LEN + 8));
        token = malloc((devs + 1) * sizeof(char *));
        if ((statinfo.dinfo == NULL) || (namelist == NULL) || (token == NULL)) {
            fprintf(stderr, "Can't allocate memory for drive list\n");
            return;
        }
        bzero(statinfo.dinfo, sizeof(struct devinfo));

        tokens = 0;                                         /* no tokens yet */
        if (getdevs(&statinfo) < 0) {                       /* find out what devices we have */
            perror("Can't get device list");
            return;
        }
        namelist[0] = '\0';                                 /* start with empty namelist */
        enamelist = namelist;                               /* point to the end of the list */

        for (i = 0; i < devs; i++) {
            struct devstat *stat = &statinfo.dinfo->devices[i];

            if ((((stat->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) /* disk device */
                 || ((stat->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_STORARRAY)) /* storage array */
            &&((stat->device_type & DEVSTAT_TYPE_IF_MASK) != DEVSTAT_TYPE_IF_OTHER) /* and not md */
            &&((stat->device_type & DEVSTAT_TYPE_PASS) == 0) /* and not passthrough */
            &&((stat->device_name[0] != '\0'))) {           /* and it has a name */
                sprintf(enamelist, "%s%s%d", _PATH_DEV, stat->device_name, stat->unit_number);
                token[tokens] = enamelist;                  /* point to it */
                tokens++;                                   /* one more token */
                enamelist = &enamelist[strlen(enamelist) + 1]; /* and start beyond the end */
            }
        }
        free(statinfo.dinfo);                               /* don't need the list any more */
        vinum_read(tokens, token, &token[0]);               /* start the system */
        free(namelist);
        free(token);
        list_defective_objects();                           /* and list anything that's down */
    } 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;

                default:
                    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
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 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;

        default:                                            /* can't get here */
            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 */
}

/*
 * Reparse a line from the configuration file
 */
static void
reparse(char *buf, char *tmp)
{
    char *ptr;
    const char *ws = " \t\r\n";
    int dodevpath = 0;
    int doskip = 0;

    strcpy(tmp, buf);
    ptr = strtok(tmp, ws);
    if (ptr == NULL || *ptr == '#')
        return;
    if (strcmp(ptr, "drive") != 0)
        return;
    strcpy(buf, ptr);
    while ((ptr = strtok(NULL, ws)) != NULL) {
        if (dodevpath) {
                dodevpath = 0;
                ptr = getdevpath(ptr, 0);
        } else if (doskip) {
                doskip = 0;
        } else if (strcmp(ptr, "drive") == 0) {
                doskip = 1;
        } else if (strcmp(ptr, "device") == 0) {
                dodevpath = 1;
        }
        strcat(buf, " ");
        strcat(buf, ptr);
    }
}

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"
        "create [-f description-file]\n"
        "          Create a volume as described in description-file\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"
        "debug\n"
        "          Cause the volume manager to enter the kernel debugger.\n"
        "debug flags\n"
        "          Set debugging flags.\n"
        "detach [plex | subdisk]\n"
        "          Detach a plex or subdisk from the volume or plex to which it is\n"
        "          attached.\n"
        "info [-v]\n"
        "          List information about volume manager state.\n"
        "init [-v] [-w] plex\n"
        "          Initialize a plex by writing zeroes to all its subdisks.\n"
        "label volume\n"
        "          Create a volume label\n"
        "list [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n"
        "          List information about specified objects\n"
        "l [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n"
        "          List information about specified objects (alternative to\n"
        "          list command)\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"
        "printconfig [file]\n"
        "          Write a copy of the current configuration to file.\n"
        "makedev\n"
        "          Remake the device nodes in " _PATH_DEV "vinum.\n"
        "move drive [subdisk | plex | drive]\n"
        "          Move the subdisks of the specified object(s) to drive.\n"
        "quit\n"
        "          Exit the vinum program when running in interactive mode.  Nor-\n"
        "          mally this would be done by entering the EOF character.\n"
        "read disk [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"
        "resetconfig\n"
        "          Reset the complete vinum configuration.\n"
        "resetstats [-r] [volume | plex | subdisk]\n"
        "          Reset statistisc 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.\n"
        "setdaemon [value]\n"
        "          Set daemon configuration.\n"
        "start\n"
        "          Read configuration from all vinum drives.\n"
        "start [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"
    };
    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 */
}

int
checkupdates(void)
{
    int options;

    if (ioctl(superdev, VINUM_GETDAEMON, &options) < 0)
        fprintf(stderr, "Can't get daemon options: %s (%d)\n", strerror(errno), errno);
    if (options & daemon_noupdate) {
        fprintf(stderr, "*** Warning: configuration updates are disabled. ***\n");
        return 1;
    } else
        return 0;
}

/* 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(void)
{
    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;

    devicename = getdevpath(devicename, 0);

    /*
     * 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);
            free(devicename);
            return &drive;                                  /* return the name of the drive */
        }
    }
    fprintf(stderr, "Can't generate a drive name\n");
    /* NOTREACHED */
    free(devicename);
    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 0
    make_vol_dev(vol.volno, 1);                             /* and create the devices */
#endif
    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 */
    union freeunion {
        struct drive_freelist freelist;
        struct ferq {                                       /* request to pass to ioctl */
            int driveno;
            int fe;
        } ferq;
    } freeunion;
    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++) {
            freeunion.ferq.driveno = drive->driveno;
            freeunion.ferq.fe = fe;
            if (ioctl(superdev, VINUM_GETFREELIST, &freeunion.freelist) < 0) {
                fprintf(stderr,
                    "Can't get free list element %d: %s\n",
                    fe,
                    strerror(errno));
                longjmp(command_fail, -1);
            }
            bigchunk = bigchunk > freeunion.freelist.sectors ? bigchunk : freeunion.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 256k", objectname);
    if (vflag)
        printf("  plex name %s.p0 org striped 256k\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 0
    make_vol_dev(vol.volno, 1);                             /* and create the devices */
#endif
    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 */
    union freeunion {
        struct drive_freelist freelist;
        struct ferq {                                       /* request to pass to ioctl */
            int driveno;
            int fe;
        } ferq;
    } freeunion;
    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++) {
            freeunion.ferq.driveno = drive->driveno;
            freeunion.ferq.fe = fe;
            if (ioctl(superdev, VINUM_GETFREELIST, &freeunion.freelist) < 0) {
                fprintf(stderr,
                    "Can't get free list element %d: %s\n",
                    fe,
                    strerror(errno));
                longjmp(command_fail, -1);
            }
            bigchunk = bigchunk > freeunion.freelist.sectors ? bigchunk : freeunion.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 256k", objectname);
    if (vflag)
        printf("  plex name %s.p0 org raid4 256k\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 0
    make_vol_dev(vol.volno, 1);                             /* and create the devices */
#endif
    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 */
    union freeunion {
        struct drive_freelist freelist;
        struct ferq {                                       /* request to pass to ioctl */
            int driveno;
            int fe;
        } ferq;
    } freeunion;
    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++) {
            freeunion.ferq.driveno = drive->driveno;
            freeunion.ferq.fe = fe;
            if (ioctl(superdev, VINUM_GETFREELIST, &freeunion.freelist) < 0) {
                fprintf(stderr,
                    "Can't get free list element %d: %s\n",
                    fe,
                    strerror(errno));
                longjmp(command_fail, -1);
            }
            bigchunk = bigchunk > freeunion.freelist.sectors ? bigchunk : freeunion.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 256k", objectname);
    if (vflag)
        printf("  plex name %s.p0 org raid5 256k\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 0
    make_vol_dev(vol.volno, 1);                             /* and create the devices */
#endif
    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 */
    union freeunion {
        struct drive_freelist freelist;
        struct ferq {                                       /* request to pass to ioctl */
            int driveno;
            int fe;
        } ferq;
    } freeunion;
    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 & 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++) {
                freeunion.ferq.driveno = drive->driveno;
                freeunion.ferq.fe = fe;
                if (ioctl(superdev, VINUM_GETFREELIST, &freeunion.freelist) < 0) {
                    fprintf(stderr,
                        "Can't get free list element %d: %s\n",
                        fe,
                        strerror(errno));
                    longjmp(command_fail, -1);
                }
                bigchunk = bigchunk > freeunion.freelist.sectors ? bigchunk : freeunion.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 256k", objectname, p);
            if (vflag)
                printf("  plex name %s.p%d org striped 256k\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 0
    make_vol_dev(vol.volno, 1);                             /* and create the devices */
#endif
    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 == 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(&vol, 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(&plex, object);
        plexno = plex.plexno;
    } else                                                  /* round */
        plexno = -1;

    /* Set the value */
    message->index = vol.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 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]);
                }
            }
        }
    }
}