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

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

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

#ifndef lint
static char *rcsid_updown_c = "$Header:rvdupdown.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 _UPDOWN_C_

#include "rvdlib.h"
#include "mTab.h"
#include "util.h"

int up_usage(), down_usage();
int err_createinit(), err_option(), err_up(), err_down(), err_passwd();
int do_all(), do_defaults(), do_up(), do_down();
int do_toc(), do_signal();

caddr_t do_getpass();

static int _dflag = FALSE;
#define debug if(_dflag)printf

extern	char *myname;			     /* and this too... */

static int up;			     /* true='up', false='down' */
static char mode = ' ';		     /* up mode */

static int verbose = FALSE;	     /* verbose messages */
static int all = FALSE;		     /* all packs */
static int defaults = FALSE;	     /* default packs */
static int toc = FALSE;		     /* do rvdtab/vdstats TOC */
static int fsck = TRUE;		     /* run fsck */
static int retry = RVD_RETRY;	     /* retry count */
static int interval = RVD_INTERVAL;  /* retry interval */
static int ign = FALSE;		     /* ignore errors */


updown(argc, argv)
int argc;
char **argv;
{
    int e = 0;

    debug("main: sigtrap\n");
    signals_trap(do_signal);

    up = (int)indexs(myname, "up");

    if (argc == 1) {
        if (up)
            up_usage();
        else
            down_usage();
        exit(0);
    }

    while (--argc) {
        register char *arg = *(++argv);
        switch (arg[0]) {
            case '-':
                while (*(++arg)) {
                    switch (*arg) {
                        case 'a':
                            all++;
                            debug("option: all\n");
                            break;
                        case 'c':
                            if (!up)
                                down_usage(), exit(-1);
#ifdef notdef
                            if (!isWorkstation()) {
                                debug("%s: is not workstation\n", myname);
                                seteuid(getuid());
                            } else {
                                debug("%s: is workstation\n", myname);
                            }
#endif
                            debug("option: create rvdtab\n");
                            if (rvd_createInit(--argc, ++argv)) {
                                e = 0, err_createinit();
				up_usage();
			    }
                            else
				{
				do_toc(20);
                                mode = ' ', e = do_all(TRUE);
				}
                            goto SortaDone;
                        case 'd':
                            defaults++;
                            debug("option: defaults\n");
                            break;
                        case 'f':
                            fsck = FALSE;
                            debug("option: no fsck\n");
                            break;
                        case 'k':
                            ign++;
                            debug("option: ignore errors\n");
                            break;
                        case 'p':
                            debug("option: out of place -p flag\n");
                            err_passwd(argc, argv);
                            if (argc > 1)
                                --argc;
                            break;
                        case 't':
                            toc++;
                            debug("option: toc\n");
                            break;
                        case 'v':
                            verbose++;
                            debug("option: verbose\n");
                            break;
                        case RVD_EX:
                        case RVD_RD:
                            mode = *arg;
                            debug("option: mode '%c'\n", mode);
                            break;
                        case RVD_SH:
                            mode = RVD_EX;
                            break;
                        case 'D':
                            _dflag = TRUE;
                            rvd_debugOn();
                            mtab_debugOn();
                            util_debugOn();
                            debug("option: debugging on\n");
                            break;
                        case 'R':
                            retry = atoi(++arg);
                            retry = abs(retry);
                            debug("option: %d retries\n", retry);
                            *(arg+1) = NIL;
                            break;
                        case 'T':
                            interval = atoi(++arg);
                            interval = abs(interval);
                            debug("option: %d sec interval\n", interval);
                            *(arg+1) = NIL;
                            break;
                        default:
                            err_option(*arg);
                    }
                }
                break;
            default:
                if (up) {
                    char *pw = NULL;
                    if (argc > 2 && strncmp("-p", *(argv+1), 2) == 0) {
                        argc -= 2;
                        argv += 2;
                        pw = *argv;
                        debug("option: -p %s\n", pw);
                    }
                    e |= do_up(arg, pw);
                } else {
                    e |= do_down(arg);
                }
        }
    }

    if (all)
        e |= do_all(FALSE);
    else if (defaults)
        e |= do_defaults();

SortaDone:			     /* one last thing to check for... */

    if (toc)
        do_toc(20);

#ifndef VFS
    mtab_sync();
#endif
    endrvdent();

    exit(-e);
}


int				     /* => vip count */
do_all(askPW)
    int askPW;
{
    register int i;

    debug("do_all(%d)\n", askPW);

    for(i = retry ;; i--) {
        register rvdtab_t *r;

        setrvdent();
        while (r = getrvdent()) {
            if (mode != ' ' && !indexc(rvd_getModes(r),mode)) {
                debug("do_all: wrong mode %s %s\n", rvd_getPack(r), rvd_getModes(r));
                continue;
            }
            if (up) {
                char *pw = NULL;
TryWithNewPW:
                if (rvd_up(r, mode, pw, ign, fsck)) {
                    if (errno == EVDBAD && rvderrno == RVDEBPWD && askPW && !rvd_havePW(r)) {
                        seterr(0,0);
                        pw = do_getpass(r);
                        goto TryWithNewPW;
                    }
                }
                if ((errno == EVDBAD && (rvderrno == RVDEUP || rvderrno == RVDESPN)) &&
                    !verbose)
                {
                    continue;
                }
                if (errno || verbose)
                    err_up(rvd_getHost(r), rvd_getPack(r), rvd_getDir(r),
                           rvd_getDrive(r), rvd_selMode(mode, r));
                if (!errno)
                    rvd_motd(rvd_getHost(r), rvd_getServer(r));
            } else {
                if (rvd_down(r, ign)) {
                    if ((errno == EVDBAD && rvderrno == RVDEDOWN) && !verbose)
                        continue;
                }
                if (errno || verbose)
                    err_down(rvd_getHost(r), rvd_getPack(r), rvd_getDir(r),
                             rvd_getDrive(r));
            }
        }
        if (i <= 0 || (up && !rvd_vipCheck()))
            break;
        debug("sleep(%d)\n", interval);
        sleep(interval);
    }
    return(up && rvd_vipCheck());
}

int				     /* => vip count */
do_defaults()
{
    register int i;

    debug("do_defaults()\n");

    for(i = retry ;; i--) {
        register rvdtab_t *r;

        setrvdent();
        while (r = getrvdent()) {
            if (!rvd_isDefault(r) || (mode != ' ' && !indexc(rvd_getModes(r),mode))) {
                debug("do_defaults: not def or wrong mode %s %s\n", rvd_getPack(r), rvd_getModes(r));
                continue;
            }
            if (up) {
                if ((!rvd_up(r, mode, NULL, ign, fsck) ||
                    (errno == EVDBAD && (rvderrno == RVDEUP || rvderrno == RVDESPN))) &&
                    !verbose)
                {
                    if (!errno)
                        rvd_motd(rvd_getHost(r), rvd_getServer(r));
                    continue;
                }
                err_up(rvd_getHost(r), rvd_getPack(r), rvd_getDir(r), rvd_getDrive(r), rvd_selMode(mode, r));
                if (!errno)
                    rvd_motd(rvd_getHost(r), rvd_getServer(r));
            } else {
                if (rvd_down(r, ign)) {
                    if ((errno == EVDBAD && rvderrno == RVDEDOWN) && !verbose)
                        continue;
                }
                if (errno || verbose)
                    err_down(rvd_getHost(r), rvd_getPack(r), rvd_getDir(r),
                             rvd_getDrive(r));
            }
        }
        if (i <= 0 || (up && !rvd_vipCheck()))
            break;
        sleep(interval);
    }
    return(up && rvd_vipCheck());
}

int				     /* => 0 or ERROR */
do_up(packname, pw)
	char	*packname;
	char	*pw;
{
	int	n, err, status;
	rvdtab_t *r;
	char	*hp;
	char	host[255];
	char	pack[RVD_NAM];

	debug("do_up(%s)\n", packname);

	rvd_parse(packname, host, pack);
	setrvdent();


	/* The while loop tries to spin up all instances of packname in rvdtab.
	 * Note that packname could be either the packname or mount directory.
	 */

	/* But this seems stupid.  Why spin up packs over each other?  We'll
	 * stop once a pack has been successfully spun up.  Of course, this
	 * assumes that packs with the same name mount in the same place.
	 * So what?  If they don't, don't give them the same name...
	 */

	n = 0;
	err = 0;
	while (r = getrvdnam(packname)) {
		n++;
		seterr(0,0);

		/* Make sure modes match.
		 */
		if (mode != ' ' && !indexc(rvd_getModes(r),mode)) {
			debug("do_up: wrong mode %s %s\n",
				rvd_getPack(r), rvd_getModes(r));
			printf("up error: wrong mode, %s.  Pack %s has mode %s\n",
				mode, rvd_getPack(r), rvd_getModes(r));
			err = ERROR;
			continue;
		}

		/* Try to spin up pack, retrying if a password is required.
		 */
		if ((status = rvd_up(r, mode, pw, ign, fsck)) != NULL) {
			if (errno == EVDBAD && 
			    rvderrno == RVDEBPWD && !rvd_havePW(r)) {
				seterr(0,0);
				pw = do_getpass(r);
				status = rvd_up(r, mode, pw, ign, fsck);
			}
		}
		hp = *host ? host : r->host;

		/* If the spinup failed, print an error.  (It is not an
		 * error if the drive is already spun up.)  If there is
		 * no error then print the motd for the server.
		 */
		if (status != NULL) {
			if (errno == EVDBAD && 
			    (rvderrno == RVDEUP || rvderrno == RVDESPN)) {
				rvd_motd(hp, rvd_getServer(r));
				seterr(0,0);
			} else {
				err = ERROR;
				err_up(hp, rvd_getPack(r), rvd_getDir(r),
					rvd_getDrive(r), rvd_selMode(mode,r));
			}
		} else {
			if (verbose)
				err_up(hp, rvd_getPack(r), rvd_getDir(r), 
					rvd_getDrive(r), rvd_selMode(mode,r));
			rvd_motd(hp, rvd_getServer(r));
			break;
		}
        }

	/* Report an error if no entries were found in rvdtab.
	 */
	if (n == 0) {
		err = ERROR;
		seterr(EVDBAD,RVDENOENT);
		err_up(host, pack, "", -1, mode);
	}
	return(err);
/* else {
		seterr(EVDBAD,RVDENOTAVAIL);
		err_up(host, pack, "", -1, mode);
	}
*/
}



int				     /* => errno */
do_down(packname)
	char	*packname;
{
	rvdtab_t *r;
	int	n;
	int	err;
	char	*hp;
	char	host[255];
	char	pack[RVD_NAM];

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

	rvd_parse(packname, host, pack);
	setrvdent();

	n = 0;
	err = 0;
	while (r = getrvdnam(packname)) {
		n++;
		seterr(0,0);

		/* Make sure modes match.
		 */
		if (mode != ' ' && !indexc(rvd_getModes(r),mode)) {
			debug("do_down: wrong mode %s %s\n", 
				rvd_getPack(r), rvd_getModes(r));
			continue;
		}

		/* Try to spindown this pack and report any errors.
		 * It is not an error if the pack is already spun down.
		 */
		hp = (*host ? host : rvd_getHost(r));
		if (rvd_down(r, ign)) {	/* Failure */
			if (errno == EVDBAD && rvderrno == RVDEDOWN)
				continue;
			err = ERROR;
			err_down(hp, rvd_getPack(r), 
				rvd_getDir(r), rvd_getDrive(r));
		} else {		/* Success */
			seterr(0,0);
			if (verbose) {
				err_down(hp, rvd_getPack(r), 
					rvd_getDir(r), rvd_getDrive(r));
			}
		}
	}

	if (n == 0) {
		err = ERROR;
		seterr(EVDBAD,RVDENOENT);
		err_down(host, pack, "", -1);
	}

	return(err);
}

extern	char *vdstate[];
#define	STATE(x) (vdstate[x])

extern	char *access;
#define	ACCESS(x) (access[x])

#define P printf
#define M if(verbose)printf

do_toc(timeout)
int	timeout;
{
register struct	vd_long_dev  *d;
register int	hdr = 0;
register struct	vd_long_dev  *drives = rvd_info(-1);
extern	struct	vd_longstat	rvd_stats;

    if (!drives) {
	P("\nCould not obtain information about client RVD system!\007\n");
        return;
    }

    P("\nMaximum of %d drives.\n", rvd_stats.num_drives);

    for (d = drives; d < &drives[rvd_stats.num_drives]; d++) {
        register mtab_t *m;

        if ((d->vd_device.state == OFF) || (d->vd_device.state == UNUSED))
            continue;

        if (! hdr) {
            hdr = 1;
            P("\nDrive\tServer\t\tMode\tMount-directory (State)\n");
        }

        debug("do_toc: mtab...\n");
        m = mtab_byNam(rvd_dev2nam(d->vd_device.drive));

        debug("do_toc: serverbyaddr()\n");
        P("%3d\t%-15s %c\t%-15s (%s)\n",
            d->vd_device.drive,
            serverbyaddr(d->vd_device.server, timeout),
            ACCESS(d->vd_device.mode),
	    (m ? m->m_path : ""),
            STATE(d->vd_device.state));
    }
    if (! hdr)
        P("\nNo virtual disks active\n");
}


do_signal()
{
    P("\n\n%s: Interrupt!\007\nCurrent state of client RVD is:\n", myname);
    do_toc(3);
    P("\n");
#ifndef VFS
    mtab_sync();
#endif
    exit(-1);
}

caddr_t
do_getpass(r)
rvdtab_t  *r;
{
	char	*pw;
	char	*getpass();

	pw = getpass("Password: ", VD_CAPAB_LEN);
	debug("do_getpass: pw='%s'\n", pw);
	return(pw);
}

up_usage()
{
    P("Usage:\n %s [-adfrtvx] [-R#] [-T#] [pack [-p passwd] ...]\n", myname);
    P(" %s -[ftv] [-R#] [-T#] -c pack driveno server mode mount-directory [passwd]\n", myname);
}

down_usage()
{
    P("usage: %s [-adtvx] [pack ...]\n", myname);
}

err_createinit()
{
    switch(errno) {
        case EVDBAD:
            P("usage: %s -[ftv] [-R#] [-T#] -c pack drive# server mode mount-directory [passwd]\n", myname);
            break;
        default:
            perror("rvd$createInit");
    }
}

err_option(c)
    char c;
{
    static int bleh = 1;

    P("%s: the -%c flag is not a legal option and is being ignored.\n", myname, c);
    if (bleh) {
        if (up)
            up_usage();
        else
            down_usage();
        bleh = 0;
    }
}

err_passwd(argc, argv)
int argc;
char **argv;
{
register int havepw = (argc > 1);

    P("\n%s: the -p flag must be preceded by a pack name%s.\n", myname,
      havepw ? " and followed by a password" : "");
    P(" the -p flag%s being ignored.\n",
      havepw ? " and the argument after the -p flag are" : " is");
}

err_up(host, pack, dir, drive, mode)
    char *host, *pack, *dir;
    char mode;
    int drive;
{
    debug("err_up: host='%s' pack='%s' mntdir='%s' mode='%c'\n", host, pack, dir, mode);

    P("\n%s %s the following entry:\n", myname, errno ? "error on" : "message for");
    prvdent(host, pack, dir, drive, mode);

    switch(errno) {
        case EVDBAD:
            rvd_perror(rvderrno,
                       rvd_mktemp(pack, itoa(drive), host, strc2s(mode), dir, ""));
            break;
        case EADDRNOTAVAIL:
            P(" The server name \"%s\" is not a known server.\n", host);
            P(" Please make sure you have not mistyped the server name or\n");
	    P(" obtain the help of a consultant.\n");
            break;
        case ENOTDIR:
        case ENOENT:
            P(" The path name \"%s\" is not a valid mount directory name.\n", dir);
            P(" It must%s name a directory.\n",
              (*dir == '/') ? "" : " be an absolute path and");
            break;
        case EBADFS:
            P(" The pack \"%s\" from server \"%s\" contains file system errors\n",
              pack, host);
            P(" which cannot be automatically repaired and therefore should not\n");
            P(" be mounted.  The pack was not spunup or mounted.  Please obtain the\n");
            P(" help of a consultant.\n");
            break;
        case ENODEV:
            P(" The Unix special files /dev/vd%da and /dev/rvd%da do not exist.\n",
              drive, drive);
            P(" In order to use drive number %d, they must exist.  Please make sure\n",
              drive);
            P(" your workstation has been set up properly and install the standard\n");
            P(" suite of RVD special files in /dev, or obtain the help of a consultant.\n");
            break;
        case ENOTBLK:
            P(" The Unix special file /dev/vd%da is not a block-special file.\n", drive);
            P(" Please make sure your workstation has been set up properly and install\n");
            P(" the standard suite of RVD special files in /dev, or obtain the help\n");
            P(" of a consultant.\n");
            break;
        case EBUSY:
            P(" Failure to mount RVD pack \"%s\" on directory \"%s\".\n", pack, dir);
            P(" One or more of the following is probably the cause:\n");
            P(" . A process has a file open somewhere in %s\n",dir);
            P(" . %s is not a directory.\n", dir);
            P(" . there are too many file systems mounted already.\n");
            P(" . %s's file system has a bad super-block.\n", pack);
            P(" . not enough memory to read %s's cylinder information.\n", pack);
            P(" . I/O errors occurred while reading %s's super-block\n", pack);
            P("   or cylinder information.\n");
            break;
        case 0:
            P(" RVD pack \"%s\" from server \"%s\" spunup on drive %d\n",
              pack, host, drive);
            P(" is mounted on directory \"%s\" in %s mode.\n", dir,
              (mode == 'r' ? "read-only" : "exclusive"));
            break;
        case ERROR:
            panic("err_up");
        default:
		P(" Unix(tm) provided the following error condition: ");
		fflush(stdout);
		perror("");
    }
}

err_down(host, pack, dir, drive)
    char *host, *pack, *dir;
    int drive;
{
    debug("err_down: host='%s' pack='%s' mntdir='%s'\n", host, pack, dir);

    P("\n%s %s the following entry:\n", myname, errno ? "error on" : "message for");
    prvdent(host, pack, dir, drive, ' ');

    switch(errno) {
        case EVDBAD:
            rvd_perror(rvderrno,
                       rvd_mktemp(pack, itoa(drive), host, strc2s(mode), dir, ""));
            break;
        case EADDRNOTAVAIL:
            P(" The server name \"%s\" is not a known server.\n", host);
            P(" Please make sure you have not mistyped the server name.\n");
            break;
        case ENODEV:
            P(" The Unix special files /dev/vd%da and /dev/rvd%da do not exist.\n",
              drive, drive);
            P(" In order to use drive number %d, they must exist.  Please make sure\n",
              drive);
            P(" your workstation has been set up properly and install the standard\n");
            P(" suite of RVD special files in /dev, or obtain the help of a consultant.\n");
            break;
        case EBUSY:
            P(" The file system rooted at directory %s is currently being\n", dir);
            P(" used by some process.  Therefore, the pack \"%s\" cannot\n", pack);
            P(" be dismounted at this time.  Please make sure there are no processes\n");
            P(" connected to, or accessing a file, on that file system.\n");
            break;
        case 0:
            P(" RVD pack \"%s\" from server \"%s\" dismounted from\n",
              pack, host);
            P(" directory %s and spundown from drive %d.\n", dir, drive);
            break;
        case ERROR:
            panic("err_down");
        default:
		P(" Unix(tm) provided the following error condition: ");
		fflush(stdout);
		perror("");
    }
}


prvdent(host, pack, dir, drive, mode)
    char *host, *pack, *dir;
    int drive;
    char mode;
{
    P("pack=< %s > drive=< %s > server=< %s > mode=< %c > mount-dir=< %s >\n\n",
      strlen(pack) ? pack : "?",
      (drive < 0) ? "?" : itoa(drive),
      strlen(host) ? host : "?",
      indexc(RVD_MODES,mode) ? mode : '?',
      strlen(dir) ? dir : "?");
}
