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

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


/*
 *	$Source: /ibm/acis/usr/src/ibm/rvd/client/RCS/main.c,v $
 *	$Author: root $
 *	$Header:main.c 12.0$
 *	$ACIS:main.c 12.0$
 */

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


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

#include "logging.h"

#include <ctype.h>
#ifdef	KERBEROS
#include <krb.h>
#endif	KERBEROS

extern errno;

char	*myname;
unsigned int	log_flag = LOG_ERR;

char	*vd_control_name = "/dev/rvdcontrol";

int	nullfunc();
int	spinup();
int	spindown();
int	vd_stats();
int	vd_abort();
int	updown();
int	newvd();
int	rvdhosts();

struct rvd_cmds {
	char	*name;
	int	(*func)();
};
struct	rvd_cmds	rvd_priv_cmds[] = {
	"up", updown,
	"down", updown,
	0, nullfunc,
};
struct	rvd_cmds	rvd_norm_cmds[] = {
	"spinup", spinup,
	"spindown", spindown,
	"vdstats", vd_stats,
	"vdabort", vd_abort,
	"newvd", newvd,
	"rvdhosts", rvdhosts,
	0, nullfunc,
};

int	vdcntrl;

struct sockaddr_in	*resolve_host();

main(argc, argv)
int	argc;
char	**argv;
{
register struct	rvd_cmds	*cmd;
register char	*name;
char	*rindex();

	if((name = rindex(argv[0], '/')) == 0)
		myname = name = argv[0];
	else
		myname = ++name;

	if((vdcntrl = open(vd_control_name, O_RDONLY, 0)) < 0) {
		fprintf(stderr, "%s: ", name); perror(vd_control_name);
		return(1);
	}

	for(cmd = rvd_priv_cmds; cmd->name; cmd++)
		if(strcmp(name, cmd->name) == 0 && cmd->func)
			return((*cmd->func)(argc, argv));

	setuid(getuid());

	for(cmd = rvd_norm_cmds; cmd->name; cmd++)
		if(strcmp(name, cmd->name) == 0 && cmd->func)
			return((*cmd->func)(argc, argv));

	fprintf(stderr, "rvdclient: unknown cmd %s\n", name);
	return(1);
}

nullfunc()
{
}

spinup(argc, argv)
	int	argc;
	char	**argv;
{
	struct	spinargs	argblock;
	struct	vd_spinup	vd_spinup;
	char	*modestring, *modename;
	char	buf[80];
	int	pflag = 1, fflag = 0, uid;
		
	argblock.capab[0] = '\0';
	uid = getuid();

	if (argc == 6 && ! strcmp(argv[1], "-f")) {
		++fflag;
		--argc;
		++argv;
	}

	if (argc < 5 || argc > 6) {
		fprintf(stderr, 
		  "usage:\n\t%s [-f] drive server name mode [password]\n",
		  myname);
		exit(1);
	}

	if ((vd_spinup.server = resolve_host(argv[2])) == NULL) {
		fprintf(stderr, "%s: unknown server address %s.\n", myname,
		  argv[2]);
		exit(1);
	}

	if (sscanf(argv[1], "%d", &vd_spinup.drive) != 1){
		fprintf(stderr, "%s: drive not a integer %s.\n",
		  myname, argv[1]);
		exit(1);
	}

	strncpy(argblock.name, argv[3], VD_NAME_LEN);
	modestring = argv[4];

	switch(*modestring) {
	case 'r':
	case 'R':
		vd_spinup.mode = RVDMRO;
		modename = "read-only";
		break;
	case 'x':
	case 'X':
		vd_spinup.mode = RVDMEXC;
		modename = "exclusive use";
		break;
	case 's':
	case 'S':
		vd_spinup.mode = RVDMSHR;
		modename = "shared";
		break;
	default:
		fprintf(stderr, "%s: invalid mode %s.\n", myname, modestring);
		exit(1);
	}

	if(isupper(*modestring))		/* Hard spinup option */
		vd_spinup.mode |= RVDHARD;

	if(fflag)
		strcpy(argblock.capab,getpass("Authorization: ", VD_CAPAB_LEN));
	else if (argc == 6)
		strncpy(argblock.capab, argv[5], VD_CAPAB_LEN);
	else
		pflag = 0;

	vd_spinup.uspin = &argblock;
	vd_spinup.errp = &rvderrno;

	/* If no password has been supplied then first try a Kerberos 
	 * authenticated spinup.  Any failures will fallback to prompting
	 * for the password.  Note that get_krbrlm does not limit the size
	 * of the returned string.  This possible problem has been minimized
	 * (not eliminated) by having realm allocated as the last item on
	 * this local stack.  This solution is subject to compiler whims.
	 */
#ifdef	KERBEROS
	while (pflag == 0) {
		int	 status;
		KTEXT_ST authent;
		struct vd_auth_spinup vd_auth_spinup;
		char	 realm[512];

		if ((status = get_krbrlm(realm, 1)) != KSUCCESS) {
			fprintf(stderr, "%s: Kerberos error was: %s\n",
					myname, krb_err_txt[status]);
			break;
		}
		status = mk_ap_req(&authent,"rvdsrv",argv[2],realm,NULL);
		if (status != KSUCCESS) {
			fprintf(stderr, "%s: Kerberos error was: %s\n",
					myname, krb_err_txt[status]);
			break;
		}
		vd_auth_spinup.drive   = vd_spinup.drive;
		vd_auth_spinup.uspin   = vd_spinup.uspin;
		vd_auth_spinup.mode    = vd_spinup.mode;
		vd_auth_spinup.server  = vd_spinup.server;
		vd_auth_spinup.errp    = vd_spinup.errp;
		vd_auth_spinup.authent = &authent;
		if (ioctl(vdcntrl, VDIOCAUTHSPINUP, &vd_auth_spinup) == 0)
			exit(0);
		break;
	}
#endif	KERBEROS

	for(; ; ) {
		if (ioctl(vdcntrl, VDIOCSPINUP, &vd_spinup) == 0)
			break;

		if (errno != EVDBAD) {
			perror(myname);
			exit(1);
		}
		if (! pflag && rvderrno == RVDEBPWD) {
			strcpy(argblock.capab, 
				getpass("Capability: ", VD_CAPAB_LEN));
			++pflag;
			continue;
		}
		sprintf(buf, "pack %s at server %s on drive %d in %s mode.",
		  argblock.name, fhostname(vd_spinup.server->sin_addr), vd_spinup.drive, modename);
		prvderr(rvderrno, buf);
		exit(1);
	}

	exit(0);
}

spindown(argc, argv)
int	argc;
char	*argv[];
{
struct	vd_longstat	vd_longstat;
struct	vd_longstat	*stats = &vd_longstat;
long	drive;
int	allflag = 0;
int	n = 0;

	if (argc != 2) {
		printf ("usage:\n\t%s drive\n\t%s -a\n", myname, myname);
		exit(1);
	}

	if(strcmp(argv[1], "-a") == 0)
		++allflag;
	else if (sscanf(argv[1], "%d", &drive) != 1) {
		fprintf(stderr, "%s: Drive must be integer. %s\n",
		  myname, argv[1]);
		exit(1);
	}

	if (allflag) {
		if(ioctl(vdcntrl, VDIOCGETSTAT, &stats)) {
			perror(myname);
			exit(1);
		}
		for (drive = 0; drive < vd_longstat.num_drives; ++drive)
			n |= down(drive);
		exit(n);
	} else
		exit(down(drive));
}

down(d)
{
	char	buf[200];
	int	i = -1;

	if(ioctl(vdcntrl, VDIOCSPINDOWN, &d)) {
		sprintf(buf, "%s: drive %d ", myname, d);
		perror(buf);
	} else
		i = 0;
	return(i);
}


vd_abort(argc, argv)
int	argc;
char	*argv[];
{
char	buf[200];
long	drive;

	if (argc != 2) {
		fprintf(stderr, "usage:\t%s drive\n", myname);
		exit(1);
	}

	if (sscanf(argv[1], "%d", &drive) != 1) {
		fprintf(stderr, "%s: Drive must be integer. %s\n",
		  myname, argv[1]);
		exit(1);
	}

	if(ioctl(vdcntrl, VDIOCABORT, &drive)) {
		sprintf(buf, "%s: drive %d ", myname, drive);
		perror(buf);
		exit(-1);
	}
	exit(0);
}


char	*vdstate[] =
{	"spun-down",
	"uninitialized",
	"spin-up/wait",
	"spun-up",
	"spin-down/wait",
	"resyncing",
	"server crashed"
};
char	*access = "?rs?x";

vd_stats(argc, argv)
int	argc;
char	*argv[];
{
struct	vd_longstat	vd_longstat;
struct	vd_longstat	*stats = &vd_longstat;
struct	vd_long_dev	*drives;
int	i, k;
int	sflag,qflag,hdr;

	if ((argc < 1) || (argc > 2)) {
syntax_err:
		fprintf(stderr, "usage: %s [-[a] [q] [s]]\n", myname);
		exit(1);
	}

	qflag = FALSE;
	sflag = FALSE;

	if (argc > 1) {	
		if (argv[1][0] != '-')
			goto syntax_err;
		switch (argv[1][1]) {
		case 'a':
			sflag = TRUE;
			qflag = TRUE;
			break;
		case 'q':
			qflag = TRUE;
			break;
		case 's':
			sflag = TRUE;
			break;
		default:
			goto syntax_err;
		}
	}

	if(ioctl(vdcntrl, VDIOCGETSTAT, &stats)) {
		perror(myname);
		exit(1);
	}

/* Display statistics */
	if (sflag) {
		puts("Statistics of virtual disks:\n");
	}
	printf("Maximum of %d drives.\n", vd_longstat.num_drives);

	drives = (struct vd_long_dev *)malloc(vd_longstat.num_drives * sizeof(struct vd_long_dev));
	if(drives == 0) {
		fprintf(stderr, "%s: Could not get memory for drive structs\n",
			myname);
		exit(1);
	}
	if(ioctl(vdcntrl, VDIOCGETDRIVE, &drives)) {
		perror(myname);
		exit(1);
	}
	hdr = FALSE;
	for (i = 0; i < vd_longstat.num_drives; i++, drives++) {
		if ((drives->vd_device.state == OFF) ||
			(drives->vd_device.state == UNUSED))
			continue;
		if (! hdr) {
			hdr = TRUE;
			printf("\nDrive\tMode\tStatus\t%-16s%7s\t%s\t\t%s\n",
			  "State", "Blocks", "Server", "Name");
		}
		printf("%3d\t  %c\t%3d\t%-16s%7d\t%s\t%s\n",
			drives->vd_device.drive, access[drives->vd_device.mode],
			drives->vd_device.status,
			vdstate[drives->vd_device.state],
			drives->vd_device.nblocks,
			fhostname(drives->vd_device.server), drives->args.name);
	}
	if (! hdr)
		fputs("no virtual disks active\n", stderr);

	if (sflag) {
		puts("\nOperating statistics:\n");
		printf("Bad Blocks:\t%7d\tBad Checksums:\t%7d\tBad types:\t%7d\n",
			vd_longstat.vdstat.bad_blk,
			vd_longstat.vdstat.bad_cksum,
			vd_longstat.vdstat.bad_type);
		printf("Time-outs:\t%7d\tUnmatched nonce:%7d\tErrors received:%7d\n",
			vd_longstat.vdstat.timeout,
			vd_longstat.vdstat.bad_nonce,
			vd_longstat.vdstat.err_rcv);
		printf("Bad state:\t%7d\tInvalid data:\t%7d\tInvalid version:%7d\n",
			vd_longstat.vdstat.bad_state,
			vd_longstat.vdstat.bad_data,
			vd_longstat.vdstat.bad_vers);
		printf("Bad format:\t%7d\tPkt Rejects:\t%7d\tPushes:\t\t%7d\n",
			vd_longstat.vdstat.bad_frmt,
			vd_longstat.vdstat.pkt_rej,
			vd_longstat.vdstat.pushes);
		putchar('\n');
		printf("Packets sent:\t%7d\tReceived:\t%7d\tQuick Rexmits:\t%7d\n",
			vd_longstat.vdstat.pkts_sent,
			vd_longstat.vdstat.pkts_rcvd, vd_longstat.vdstat.rxmts);
		printf("Blocks read:\t%7d\tWritten:\t%7d\tLong Rexmits:\t%7d\n",
			vd_longstat.vdstat.blk_rqs,
			vd_longstat.vdstat.blk_wrt,
			vd_longstat.long_rxmts);
	}

	if (qflag){
		puts("\nQueue statistics:\n");
		for (i = 0; i < STQLEN; i++) {
			if(vd_longstat.vdstat.q_size[i] == 0)
				continue;
			printf("%5d: (%d)\t", i, vd_longstat.vdstat.q_size[i]);
			for(k = 1; k != 0 && k <= vd_longstat.vdstat.q_size[i]; k <<= 1)
				putchar('*');
			putchar('\n');
		}
	}
	exit(0);
}
