/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:rvdtab.c 12.0$ */
/* $ACIS:rvdtab.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/rvd/client/RCS/rvdtab.c,v $ */

#ifndef lint
static char *rcsid = "$Header:rvdtab.c 12.0$";
#endif

/* This file is part of the Project Athena Remote Virtual Disk (RVD) libraries.
 *
 *	$Source: /ibm/acis/usr/src/ibm/rvd/client/RCS/rvdtab.c,v $
 *	$Author: root $
 *	$Header:rvdtab.c 12.0$
 *	$ACIS:rvdtab.c 12.0$
 */

#ifndef lint
static char *rcsid_rvdtab_c = "$Header:rvdtab.c 12.0$";
#endif lint

/* Copyright 1984 by the Massachusetts Institute of Technology */
/* See permission and disclaimer notice in the file "notice.h" */
#include "notice.h"


#define _RVDTAB_C_

#include "rvdlib.h"
#include "util.h"
#include "mTab.h"
#ifdef	KERBEROS
#include	<krb.h>
#endif	KERBEROS
#ifdef  VFS
#include <sys/wait.h>
#endif  VFS

extern	char	*myname;

static int rvd_n = -1;		     /* # of rvdtab entries */
static int rvd_tab = -1;	     /* file desc of aka /etc/rvd/rvdtab */
static rvdtab_t *rvd_db;	     /* rvdtab database */
static char *rvd_data;		     /* raw rvdtab data */

static	struct	vd_long_dev *rvd_device;
struct	vd_longstat rvd_stats;
static	struct	vd_longstat *stats = &rvd_stats;

static int _dflag = FALSE;	     /* debugging msgs on/off */
#define debug if(_dflag)printf

void
rvd_debugOn()
{
    _dflag = TRUE;
}

void
rvd_debugOff()
{
    _dflag = FALSE;
}


struct	vd_long_dev *
rvd_info(d)
int d;
{
register int i;
extern	int	vdcntrl;

	if (d < 0 || rvd_n < 0) {

		if(ioctl(vdcntrl, VDIOCGETSTAT, &stats)) {
			perror(myname);
			exit(1);
		}
		rvd_device = (struct vd_long_dev *)
			malloc(rvd_stats.num_drives * sizeof(struct vd_long_dev));
		if(rvd_device == 0) {
			fprintf(stderr,
				"%s: Could not get memory for device structs\n",
				myname);
			exit(1);
		}
		if(ioctl(vdcntrl, VDIOCGETDRIVE, &rvd_device)) {
			perror(myname);
			exit(1);
		}
	}
	if (d < 0)
		return(rvd_device);
	if (d >= rvd_stats.num_drives)
		return(seterr(EVDBAD,RVDEND),ZVDINFO);
		/* take the hint... */
	if (rvd_device[d].vd_device.state != SPUNDOWN &&
			rvd_device[d].vd_device.state != UNUSED &&
			rvd_device[d].vd_device.drive == d)
		return(&rvd_device[d]);
		/* bad hint.  do real work... */
	for (i = 0; i < rvd_stats.num_drives; i++) {
		if (rvd_device[d].vd_device.state != SPUNDOWN &&
				rvd_device[d].vd_device.state != UNUSED &&
				rvd_device[i].vd_device.drive == d)
			return(&rvd_device[i]);
	}
	return(seterr(EVDBAD,RVDEDOWN),ZVDINFO);
}


int				     /* => errno  */
rvd_createInit(argc, argv)
    int argc;
    char **argv;
{
    register rvdtab_t *r;

    if (argc < 5) {
        debug("rvd$createInit(%d, argv) -- too few args\n", argc);
        return(seterr(EVDBAD,RVDEARGN));
    }

    r = (rvdtab_t *)calloc(1, sizeof(rvdtab_t));
    if (!r)
        panic("rvd$createInit: ENOMEM");

    if (rvd_n >= 0)
        endrvdent();

    rvd_n = 1;
    rvd_db = r;
    *r = *(rvd_mktemp(argv[0], argv[1], argv[2], argv[3],
                       argv[4], (argc > 5) ? argv[5] : ""));
    r->flags |= (RVDTAB_VIP|RVDTAB_DEF);
    debug("rvd$createInit(%s, %d, %s, %s, %s, pw) ret\n",
          r->pack, r->dev, r->host, r->mode, r->dir);
    return(errno=0);
}


rvdtab_p
rvd_mktemp(pack, drive, host, modes, path, pw)
    char *pack, *drive, *host, *modes, *path, *pw;
{
    static rvdtab_t temp;

    bzero((char *)&temp, sizeof(temp));

    temp.pack = pack;
    temp.dev = (*drive == '?') ? -1 : atoi(drive);
    temp.host = host;
    temp.mode = modes;
    temp.dir = path;
    temp.pw = pw;

    return(&temp);
}


int				     /* => # rvdtab entries or -errno */
rvd_init(fn)
    char *fn;
{
    register rvdtab_t *r;
    int len;

    if (rvd_n >= 0)
        return(rvd_n);		     /* already init'd */

    rvd_info(-1);		     /* vdstats() */

    debug("rvd$init\n");

    if (util_yankFile(fn, (char **)&rvd_data, &len, &rvd_tab))
        return(-seterr(EVDBAD,RVDETAB));

    if (! (rvd_n = util_countLines(rvd_data, len)))
        return(errno=0);

    rvd_db = (rvdtab_t *)calloc(rvd_n, sizeof(rvdtab_t));
    if (!rvd_db)
        panic("rvd$init: ENOMEM");

    rvd_data = util_skipLine(rvd_data, TRUE);

    {/*
      * rvdtab format: [*%] disk drive host modes dir [pw] # comment
      */
    register int i;
    register char *d;
    register char *delim = " \t";

    for (i = rvd_n, r = rvd_db, d = rvd_data; i > 0; i--, r++) {
        d = util_skipSpace(d);
        if (d[0] == '*') {
            r->flags = (RVDTAB_VIP|RVDTAB_DEF);
            d = util_skipField(d, delim);
        } else if (d[0] == '%') {
            r->flags = RVDTAB_DEF;
            d = util_skipField(d, delim);
        } else
            r->flags = 0;
        r->pack = d;
        d = util_skipField(d, delim);
        r->dev = (d[0] == '?') ? -1 : atoi(d);
        r->host = d = util_skipField(d, delim);
        r->mode = d = util_skipField(d, delim);
	if (strlen(r->mode) == 0)
		r->mode = "rx";
        r->dir = d = util_skipField(d, delim);
        r->pw = d = util_skipField(d, delim);
        d = util_skipField(d, delim);
        d = util_skipLine(d, FALSE);
        debug("rvd$init: dev '%s' host '%s' pack '%s' mode '%s' dir '%s' pw '%s'\n",
                rvd_dev2nam(r->dev), r->host, r->pack, r->mode, r->dir, r->pw);
    }
    }
    sortrvdent();

    {/*
      * resolve rvdtab, vdstats, and mtab...
      */
    register struct vd_long_dev *d;

    for (d = rvd_device; d < &rvd_device[rvd_stats.num_drives]; d++) {
        if (d->vd_device.state == SPUNUP) {
            rvd_doneDrive(d->vd_device.drive);
#ifdef notdef
            for(r = rvd_db; r < &rvd_db[rvd_n]; r++) {
                if (!strncmp(d->vd_device.packname, r->pack, RVD_NAM)) {
                    if (r->dev < 0) {
                        r->dev = d->vd_device.drive;
                        r->server = d->vd_device.server;
                        r->flags |= (RVDTAB_UP|RVDTAB_MNT);
                    }
                }
            }
#endif
        }
    }
    }

    debug("rvd$init: => #%d\n", rvd_n);
    return(errno=0, rvd_n);
}


int				     /* => errno */
rvd_up(r, mode, pw, ign, fsck)
    rvdtab_t *r;
    char mode;
    char *pw;			     /* over-ride password */
    int ign;			     /* ignore mount() error */
    int fsck;			     /* fsck requested */
{
    int status = 0;
    int locked, ld;
    char modestr[2];
    char pass[RVD_PW];
    union wait mwait;


    if (r->dir[0] != '/') {
        debug("rvd$up: mount-dir '%s' not valid path\n", r->dir);
        return(errno = ENOTDIR);
    }

    if (r->flags&RVDTAB_UP) {
        debug("rvd$up: '%s:%s' already up\n", r->host, r->pack);
        return(seterr(EVDBAD,RVDEUP));
    }

    if (r->dev < 0 || r->dev >= rvd_stats.num_drives)
        return(seterr(EVDBAD,RVDEND));

    if (rvd_info(r->dev)) {
        debug("rvd$up: drive %d in use\n", r->dev);
        return(seterr(EVDBAD,RVDESPN));
    }

    if (pw)
        r->pw = pw;

    strncpy(pass, r->pw, RVD_PW);

    modestr[1] = NIL;
    if (mode == ' ') {
        mode = modestr[0] = r->mode[0];
    } else {
        modestr[0] = mode;
        if (!indexc(r->mode, mode))
            pass[0] = NIL;
    }
    if (!indexc(RVD_MODES,mode))
        return(seterr(EVDBAD,RVDEBMD));

    if (r->dev == -1) {
        ld = open(RVDTAB_SYS, O_RDONLY, 0);
        if (ld < 0) {
            debug("rvd$up: can't open '%s' for locking\n", RVDTAB_SYS);
        } else {
            flock(ld, LOCK_EX);
            locked = TRUE;
        }
        if ((r->dev = rvd_findFree()) < 0) {
            debug("rvd$up: can't find free drive\n");
            return(seterr(EVDBAD,RVDEND));
        }
    }

    if (!r->server.s_addr) {
        sinaddr_t *s;
        s = serverbyname(r->host, 0);
        if (s) {
            r->server = s->sin_addr;
        } else
            return(errno=EADDRNOTAVAIL);
    }

    debug("rvd$up: host '%s' pack '%s' drive %d mode '%c'\n",
            r->host, r->pack, r->dev, mode);

    status = rvd_spinup(r->server, r->pack, r->dev, mode, r->host, r->pw);

    if (locked) {
        flock(ld, LOCK_UN);
        close(ld);
    }

    if (errno = status) {
        debug("rvd$up: => errno %d rvderrno %d\n", errno, rvderrno);
        return(errno);
    }

    if (fsck && ((mode & ~RVDHARD) != RVD_RD)) {
        char buf[RVD_DEV];
        debug("rvd$up: fsck\n");
        status = util_runFile(TRUE, FSCK, rvdnam(buf,r->dev), 0);
    }

    if (status) {
        errno = status = EBADFS;
        goto UpFail;
    }

Mount:
    if (r->flags&RVDTAB_MNT) {
        debug("rvd$up: already mounted on '%s'\n", r->dir);
        goto NoMount;
    }

    debug("rvd$up: mount %s %s %c\n", rvd_dev2nam(r->dev), r->dir, mode);
#ifndef VFS
    mtab_mount(rvd_dev2nam(r->dev), r->dir,
		((mode & ~RVDHARD) == RVD_RD) ? FSTAB_RO : FSTAB_RW);
#else
    errno = 0;
    if (fork() == 0) {
        if ((mode & ~RVDHARD) == RVD_RD)
            execl("/etc/mount","mount","-r",rvd_dev2nam(r->dev),r->dir,0);
        else
            execl("/etc/mount","mount",rvd_dev2nam(r->dev),r->dir,0);
    }
    if (status = errno) goto UpFail;
    wait(&mwait);
    if ((mwait.w_termsig == 0) && (mwait.w_retcode != 0)) errno = EINVAL;
#endif

    if (status = errno) {
        if (ign)
            goto NoMount;
UpFail: rvd_spindown(r->dev);
        debug("rvd$up: => errno %d rvderrno %d\n", status, rvderrno);
        return(errno=status);
    }
    r->flags |= RVDTAB_MNT;

NoMount:
    r->flags |= RVDTAB_UP;
    rvd_doneDrive(r->dev);
    debug("rvd$up: returns\n");
    return(errno=status);
}


int				     /* => errno */
rvd_down(r, ign)
    rvdtab_t *r;
    int ign;
{
    union wait mwait;

#ifdef notdef
    if ((r->flags&RVDTAB_UP) == 0) {
        debug("rvd$down: '%s' not up\n", r->pack);
        return(seterr(EVDBAD,RVDEDOWN));
    }
#endif

    debug("rvd$down: drive %d dir '%s'\n", r->dev, r->dir);

    if (r->dev < 0 || r->dev >= rvd_stats.num_drives)
        return(seterr(EVDBAD,RVDEND));

    if (!rvd_info(r->dev)) {
        debug("rvd$down: nothing spunup on drive %d\n", r->dev);
        return(seterr(EVDBAD,RVDEDOWN));
    }

#ifndef VFS
    if (mtab_umount(rvd_dev2nam(r->dev))) {
        debug("rvd$down: umount => errno %d\n", errno);
        /* EINVAL means not mounted, so always ignore it. */
        if (!ign && errno != EINVAL)
            return(errno);
        debug("rvd$down: ignore umount() error\n");
    }
#else
    errno = 0;
    if (fork() == 0) {
        execl("/etc/umount","umount",rvd_dev2nam(r->dev),0);
    }
    if (errno) return(errno);
    wait(&mwait);
    if (mwait.w_termsig == 0)
        if (!ign && (mwait.w_retcode != 0)) return(EINVAL);
#endif

    r->flags &= ~(RVDTAB_MNT);

    if (rvd_spindown(r->dev)) {
        debug("rvd$down: spindown => errno %d\n", errno);
        return(errno);
    }
    r->flags &= ~(RVDTAB_UP);
    debug("rvd$down: ret\n");
    return(errno=0);
}


int				     /* => errno */
rvd_spindown(drive)
    int drive;
{
	errno = 0;

	ioctl(vdcntrl, VDIOCSPINDOWN, &drive);

	rvd_info(-1);

	debug("rvd$spindown: => errno %d\n", errno);
	return(errno);
}


int				     /* => errno */
rvd_spinup(server, pack, drive, modec, servername, pw)
	inaddr_t server;
	char	*pack;
	int	drive;
	char	modec;
	char	*servername;		/* Pointer to RVD server name. */
	char	*pw;
{
	struct	vd_spinup	vd_spinup;
	struct spinargs sargs;
	u_short mode;
	char buf[RVD_DEV];

	errno = 0;

	strncpy(sargs.name, pack, RVD_NAM);
	strncpy(sargs.capab, pw, RVD_PW);

	switch (modec) {
		case RVD_RD:
			mode = RVDMRO;
			break;

		case RVD_RD_HARD:
			mode = RVDMRO|RVDHARD;
			break;

		case RVD_EX:
			mode = RVDMEXC;
			break;

		case RVD_EX_HARD:
			mode = RVDMEXC|RVDHARD;
			break;

		case RVD_SH:
			mode = RVDMSHR;
			break;

		case RVD_SH_HARD:
			mode = RVDMSHR|RVDHARD;
			break;

		default:
			return(seterr(EVDBAD,RVDEBMD));
	}

	/* First try a Kerberos authenticated spinup.  If 
	 * it fails for any reason then try the regular spinup.
	 * The while loop is to avoid a goto.
	 */
#ifdef	KERBEROS
	while (1) {
		int	 status;
		KTEXT_ST authent;
		struct vd_auth_spinup vd_auth_spinup;
		char	 realm[512];

		if ((status = get_krbrlm(realm, 1)) != KSUCCESS) {
			debug("rvd$spinup: krb error was %s\n", 
					krb_err_txt[status]);
			break;
		}

		status = mk_ap_req(&authent,"rvdsrv",servername,realm,NULL);
		if (status != KSUCCESS) {
			debug("rvd$spinup: krb error was %s\n", 
					krb_err_txt[status]);
			break;
		}
		vd_auth_spinup.drive   = drive;
		vd_auth_spinup.uspin   = &sargs;
		vd_auth_spinup.mode    = mode;
		vd_auth_spinup.server  = in2sock(server);
		vd_auth_spinup.errp    = &rvderrno;
		vd_auth_spinup.authent = &authent;

		if (ioctl(vdcntrl, VDIOCAUTHSPINUP, &vd_auth_spinup) == 0) {
			debug("rvd$spinup: authenticated spinup worked\n");
			rvd_info(-1);
			return(0);
		}
		debug("rvd$spinup: authenticated spinup failed\n");
		debug("rvd$spinup: => errno %d rvderrno %d\n", errno, rvderrno);
		break;
	}
#endif	KERBEROS

	vd_spinup.drive = drive;
	vd_spinup.mode = mode;
	vd_spinup.server = in2sock(server);
	vd_spinup.uspin = &sargs;
	vd_spinup.errp = &rvderrno;

	errno = 0;
	rvderrno = 0;
	if (ioctl(vdcntrl, VDIOCSPINUP, &vd_spinup))
		debug("rvd$spinup: => errno %d rvderrno %d\n", errno, rvderrno);

	rvd_info(-1);
	return(errno);
}


void
rvd_motd(hostname, server)
char *hostname;
inaddr_t server;
{
	register rvdtab_t *r;

	for (r = rvd_db; r < &rvd_db[rvd_n]; r++) {
		if (server.s_addr == r->server.s_addr && (r->flags&RVDTAB_MOTD))
			return;
	}

	util_runFile(TRUE, RVD_GETM, hostname, 0);

}


static u_long Rvd = 0;		     /* rvdtab cursor */
static inaddr_t zilch_server;

struct	vd_long_dev *
rvd_findPack(name)
    char *name;
{
    register int i;
    register inaddr_t server;
    char host[255];
    char pack[RVD_NAM];

    return(ZVDINFO);

#ifdef notdef
    if (rvd_n < 0)
        rvd_info(-1);

    rvd_parse(name, host, pack);

    server = zilch_server;
    if (host[0]) {
        register sinaddr_t *sin = serverbyname(host, 0);
        if (!sin)
            return(errno=EADDRNOTAVAIL,ZVDINFO);
        server = sin->sin_addr;
    }

    for(i = 0; i < rvd_stats.num_drives; i++) {
        if (rvd_device[i].state == SPUNUP &&
            strncmp(rvd_device[i].packname, pack, RVD_NAM) == 0 &&
            (!server.s_addr || server.s_addr == rvd_device[i].server.s_addr))
        {
            return(&rvd_device[i]);
        }
    }
    return(rvderrno=RVDEPACK, errno=EVDBAD, ZVDINFO);
#endif
}


rvdtab_p			     /* => (rvdtab_t *) or NULL */
rvd_findEnt(pack, mode)
    char *pack;			     /* server:pack */
    char mode;			     /* preferred mode */
{
    register int i;
    register rvdtab_t *r;
    register sinaddr_t *sa = (sinaddr_t *)NULL;
    sinaddr_t server;
    char packname[RVD_NAM];
    char hostname[255];

    if (!pack) {
        Rvd = 0;
        return(errno=0, ZRVDTAB);
    }

    rvd_parse(pack, hostname, packname);

    debug("rvd$findEnt: find '%s:%s' mode '%c'\n", hostname, packname, mode);

	if (*hostname) {
		if (!(sa = serverbyname(hostname, 0))) {
			debug("rvd$findEnt: can't resolve %s\n", hostname);
			return(errno=EADDRNOTAVAIL, ZRVDTAB);
		}
		server = *sa;
		sa = &server;
		debug("rvd$findEnt: server addr %s\n",
			inet_ntoa(server.sin_addr.s_addr));
	}

    if (rvd_init(RVDTAB_SYS) < 0)
        return(ZRVDTAB);

    for (i = rvd_n; i > 0; i--) {
        r = &rvd_db[Rvd++ % rvd_n];
        debug("rvd$findEnt: pack '%s' dir '%s' mode '%s' host '%s'?\n",
              r->pack, r->dir, r->mode, r->host);
        /* mode, packname, and host must match... */
        if ((mode == ' ' || indexc(r->mode, mode)) &&
            ((*packname == '/' && !strcmp(packname, r->dir)) ||
             (*packname && !strncmp(packname, r->pack, RVD_NAM))))
        {
            if(sa && stricmp(hostname,r->host)) {
                if (!r->server.s_addr) {
                    register sinaddr_t *saddr = serverbyname(r->host, 0);
			debug("rvd$findEnt: resolve %s => ", r->host);
                    if (saddr) {
                        r->server = saddr->sin_addr;
			debug("%s\n", inet_ntoa(r->server.s_addr));
		    }
                }
		debug("rvd$findEnt: addrcmp %s vs ",
			inet_ntoa(sa->sin_addr.s_addr));
		debug("%s\n", inet_ntoa(r->server.s_addr));
		if (sa->sin_addr.s_addr != r->server.s_addr) {
			debug("rvd$findEnt: wrong server\n");
			continue;
		}
            }
            debug("rvd$findEnt: found\n");
            return(errno=0, r);
        }
    }

    debug("rvd$findEnt: '%s' not found\n", pack);
    seterr(EVDBAD,RVDENOENT);
    return(ZRVDTAB);
}

int				     /* => # of VIP packs still not up */
rvd_vipCheck()
{
    debug("rvd$vipCheck\n");
    return(rvd_doneDrive(-1));
}


static int			     /* => free drive # or ERROR */
rvd_findFree()
{
    register int i;

    debug("rvd$findFree\n");

    rvd_info(-1);		     /* get up-to-date info from kernel... */

    for (i = 0; i < rvd_stats.num_drives; i++) {
        if (rvd_info(i))
            continue;
        return(errno=0, i);
    }

    debug("rvd$findFree: no free drives\n");
    seterr(EVDBAD,RVDEND);
    return(ERROR);
}


static int
rvd_doneDrive(dev)
    int dev;
{
    register rvdtab_t *r;
    register int vip;		     /* very important packs */

    if (rvd_init(RVDTAB_SYS) < 0)
        return(0);

    debug("rvd$doneDrive(%d)\n", dev);
    for (vip = 0, r = rvd_db; r < &rvd_db[rvd_n]; r++) {
        if (dev == r->dev)
            r->flags &= ~(RVDTAB_VIP);
        else if (r->flags & RVDTAB_VIP)
            vip++;
    }
    debug("rvd$doneDrive: ret vip=%d\n", vip);
    return(vip);
}


void
rvd_parse(name, host, pack)
    char *name;
    char *host;
    char *pack;
{
    register char *cp = indexc(name, ':');

    if (cp) {
        *cp = NIL;
        if (host) strcpy(host, name);
        if (pack) strcpy(pack, cp+1);
	*cp = ':';
    } else {
        if (host) host[0] = NIL;
        if (pack) strcpy(pack, name);
    }
    debug("rvd$parse(%s) => %s:%s\n", name, host, pack);
}


caddr_t
rvd_dev2nam(dev)
    int dev;
{
    static char buf[RVD_DEV];

    vdnam(buf, dev);
    debug("rvd$dev2nam(%d) => %s\n", dev, buf);
    return(buf);
}


#define P printf

void
rvd_perror(err, r)
    int err;
    rvdtab_t *r;
{
    extern char *myname;
    register char *oldname = myname;

	switch(err) {
	case RVDETAB:
	fprintf(stderr,
		"Could not read the client RVD database file /etc/rvd/rvdtab.\n");
	fprintf(stderr,
		"Please make sure the file exists and has the proper format.\n");
	return;
	case RVDENOENT:
	fprintf(stderr,
		"RVD pack \"%s\" from server \"%s\" is not listed in /etc/rvd/rvdtab.\n",
	r->pack, r->host);
	fprintf(stderr,
		" Please specify packs listed in /etc/rvd/rvdtab.\n");
	return;
	case RVDEDOWN:
	fprintf(stderr,
		" The RVD pack \"%s\" from server \"%s\" is not spunup.\n",
		r->pack, r->host);
	fprintf(stderr,
		" No action was taken to spindown the pack.\n");
	return;
	case RVDEARGN:
	fprintf(stderr,
		" Too few arguments were given to the -c option.\n");
	return;
	case RVDEUP:
	fprintf(stderr,
		"RVD pack \"%s\" from server \"%s\" was already spunup,\n",
		r->pack, r->host);
	fprintf(stderr,
		" and it is probably mounted.  No action was taken to spinup and\n");
	fprintf(stderr,
		" mount the pack.\n");
	return;
	case RVDEBMD:
	if (*r->mode == ' ' || indexc(RVD_MODES, *r->mode))
		break;
	fprintf(stderr, "The mode '%c' is invalid.", *r->mode);
	fprintf(stderr,
		" Valid modes are 'r' (read-only) and 'x' (exclusive).\n");
	return;
	case RVDESPN:
	if (!rvd_info(r->dev))
		break;
	fprintf(stderr,
		"RVD pack \"%s\" from server \"%s\" was not spunup because\n",
		r->pack, r->host);
	fprintf(stderr, "RVD drive %d already has a pack spunup on it.",r->dev);
	fprintf(stderr, "\nPlease use a different drive number (0 thru %d).\n",
		(rvd_stats.num_drives-1));
	return;
	case RVDEND:
	if (r->dev >= 0 && r->dev < rvd_stats.num_drives)
		break;
	fprintf(stderr,
		" The drive number %s is invalid.\n",
		(r->dev < 0) ? "?" : itoa(r->dev));
	fprintf(stderr,
		" Please use drive numbers 0 thru %d (inclusive).\n",
		(rvd_stats.num_drives-1));
	return;
        case RVDENOTAVAIL:
	fprintf(stderr,
		" The RVD pack \"%s\" is not currently available\n", r->pack);
	fprintf(stderr, " because none of the servers managing the\n");
	fprintf(stderr, " pack (as listed in /etc/rvd/rvdtab) responded.\n");
	return;

	default:
	break;
	}
	fprintf(stderr," Server \"%s\" reports the following error\n", r->host);
	fprintf(stderr, " while trying to spinup RVD pack \"%s\"", r->pack);
	myname = "";
	prvderr(err, "");
	myname = oldname;
}


static rvdtab_t temp;
static int i,j;

sortrvdent()
{
    for(j=0;j<rvd_n;j++) {
	for(i=0;i<rvd_n-1;i++) {
	    if (!(rvd_db[i].flags&RVDTAB_DEF) &&
		 (rvd_db[i+1].flags&RVDTAB_DEF)) {
		temp = rvd_db[i+1];
		rvd_db[i+1] = rvd_db[i];
		rvd_db[i] = temp;
	    }
	}
    }
}

/*
 * Unix fstab-style interface to /etc/rvd/rvdtab
 *
 */

static rvdtab_t *firstent;

rvdtab_t *getrvdent()		     /* next rvdtab entry */
{
    register rvdtab_t *dot;

    debug("getrvdent()\n");

    if (rvd_init(RVDTAB_SYS) < 0)
        return(ZRVDTAB);
    dot = &rvd_db[Rvd % rvd_n];
    if (dot == firstent)
        return(ZRVDTAB);
    Rvd++;
    if (!firstent)
        firstent = &rvd_db[0];
    return(dot);
}

static rvdtab_t *firstnam;

rvdtab_t *getrvdnam(pack)	     /* by pack name */
    char *pack;
{
    register rvdtab_t *dot;

    debug("getrvdnam(%s)\n", pack);

    if (rvd_init(RVDTAB_SYS) < 0)
        return(ZRVDTAB);
    dot = rvd_findEnt(pack, ' ');
    if (dot == firstnam)
        return(ZRVDTAB);
    if (!firstnam)
        firstnam = dot;
    return(dot);
}

static rvdtab_t *firstdir;

rvdtab_t *getrvddir(dir)	     /* by mount directory */
    char *dir;
{
    register rvdtab_t *dot;

    debug("getrvddir(%s)\n", dir);

    if (rvd_init(RVDTAB_SYS) < 0)
        return(ZRVDTAB);
    dot = rvd_findEnt(dir, ' ');
    if (dot == firstdir)
        return(ZRVDTAB);
    if (!firstdir)
        firstdir = dot;
    return(dot);
}

static rvdtab_t *firsthost;

rvdtab_t *getrvdhost(host)	     /* by host name */
    char *host;
{
    register int len = strlen(host);
    register rvdtab_t *dot;
    char packname[255];

    debug("getrvdhost(%s)\n", host);

    if (rvd_init(RVDTAB_SYS) < 0)
        return(ZRVDTAB);
    strncpy(packname, host, len);
    packname[len] = ':';
    packname[len+1] = NIL;
    dot = rvd_findEnt(packname, ' ');
    if (dot == firsthost)
        return(ZRVDTAB);
    if (!firsthost)
        firsthost = dot;
    return(dot);
}

int setrvdent()			     /* reset file pointer => errno */
{
    debug("setrvdent()\n");

    if (rvd_init(RVDTAB_SYS) < 0)
        return(errno);
    firstent = firstnam = firstdir = firsthost = ZRVDTAB;
    return(Rvd = errno=0);
}

int endrvdent()			     /* close file */
{
    debug("endrvdent()\n");

    if (rvd_tab >= 0) close(rvd_tab);
#ifdef notdef
    if (rvd_db) free((char *)rvd_db);
    if (rvd_data) free((char *)rvd_data);
#endif
    rvd_tab = rvd_n = -1;
    rvd_db = firstent = firstnam = firstdir = firsthost = ZRVDTAB;
    rvd_data = ZCHAR;
    return(Rvd = errno=0);
}
