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

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


#ifndef lint
static char rcsid_virtd_c[] = "$Header:virtd.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"


/* This file contains all the general-purpose routines for managing
 * virtual disk descriptors for the vd database manager.  Descriptors
 * are created when the database file is read in, and may also be
 * created, modified, and deleted by user command.
 */

#include	<sys/types.h>
#include	<stdio.h>
#include	<errno.h>
#include	<machineio/vdconst.h>

#include	"rvd_types.h"
#include	"logging.h"
#include	"ctl_msgs.h"
#include	"custom.h"
#include	"obj.h"
#include	"ctl_pkt.h"
#include	"queue.h"
#include	"physd.h"
#include	"virtd.h"
#include	"extern.h"
#include	"vddb.h"
#include	"canon.h"

#undef	q_head
#define	q_head(p,t)	(t)((struct qelem *)p)->q_forw

extern char	passw[];
extern char	*myname;
extern char	server_name[];
extern boolean	passw_flag;

char	*getpass(), *fhostname();

extern struct physd_q	all_physds;

/* Look up the virtual disk with the specified name and return a pointer to
 * its virtual disk descriptor.  Returns NULL if no such disk exists.
 */

struct virtd *
vd_lookup(vdname)

register char	*vdname;		/* name to look up */

{
	struct physd	*td, *pd;

	td = (struct physd *) & all_physds.pq_forw;
	pd = q_head(td, struct physd *);

	while(pd != td) {
		struct virtd	*td, *vd;

		td = (struct virtd *) &(pd->vd_forw);
		vd = q_head(td, struct virtd *);

		while(vd != td) {
			if(! strcmp(vdname, vd->vd_pack))
				return(vd);
			vd = q_head(vd, struct virtd *);
		}
		pd = q_head(pd, struct physd *);
	}
	return(NULL);			/* no such disk */
}

/* Look up the virtual disk with the specified uid and return a pointer to
 * its virtual disk descriptor.  Returns NULL if no such disk exists.
 */

struct virtd *
vd_look_uid(vduid)
register u_long	vduid;		/* uid to look up */

{
	struct physd	*td, *pd;

	td = (struct physd *) &(all_physds.pq_forw);
	pd = q_head(td, struct physd *);

	while(pd != td) {
		struct virtd	*td, *vd;

		td = (struct virtd *) &(pd->vd_forw);
		vd = q_head(td, struct virtd *);

		while(vd != td) {
			if(vduid == vd->vd_uid)
				return(vd);
			vd = q_head(vd, struct virtd *);
		}
		pd = q_head(pd, struct physd *);
	}
	return(NULL);			/* no such disk */
}

/* Delete the specified virtual disk.
 */

vd_delete(vd)

register struct	virtd	*vd;		/* disk to delete */

{
	pd_vddel(vd->pd_forw, vd);
	time(&(vd->pd_forw->pd_modified));	/* timestamp modification */
	obj_free((char *)vd);			/* punt it */
}

/* Add a new virtual disk, described by the item list.  Read from rvddb.
 */

/*ARGSUSED*/
vd_add(fhost, op1, desc, nitem)

struct	sockaddr_in	*fhost;		/* foreign host making request */
char	*op1;				/* operation name */
register struct	item	desc[];		/* items describing disk */
int	nitem;				/* size of item list */

{
	register struct	virtd	*vd;	/* virt disk descriptor */
	register struct	physd	*pd;	/* phys disk descriptor */
	int	modes;			/* allowable spinup modes */
	int	offset;			/* offset on physical disk */
	int	created;		/* create date */
	int	modified;		/* modification date */
	u_long	blocks;			/* size in blocks */
	u_long	uid;			/* pack unique ID */

	if (sscanf(desc[ITM_VADD_MODES].it_val, "%D", &modes) != 1 ||
	    sscanf(desc[ITM_VADD_OFF].it_val, "%D", &offset) != 1 ||
	    sscanf(desc[ITM_VADD_BLOCKS].it_val, "%D", &blocks) != 1 ||
	    sscanf(desc[ITM_VADD_UID].it_val, "%D", &uid) != 1) {
		fprintf(stderr, "Bad db entry for virt disk %s\n",
		    desc[ITM_VADD_NAME].it_val);
		return;
	}

	if ((vd = vd_lookup(desc[ITM_VADD_NAME].it_val)) != NULL) {
		fprintf(stderr, "Duplicate db entry for virt disk name %s\n",
		    desc[ITM_VADD_NAME].it_val);
		return;
	}

	if ((vd = vd_look_uid(uid)) != NULL) {
		fprintf(stderr, "Duplicate db entry for virt disk uid %s\n", uid);
		return;
	}

	if ((pd = pd_lookup(desc[ITM_VADD_PHYS].it_val)) == NULL) {
		fprintf(stderr, "No phys disk in db for virt disk %s\n",
		    desc[ITM_VADD_NAME].it_val);
		return;
	}

	if (offset < 0 || (blocks + offset) > pd->pd_blocks) {
		fprintf(stderr, "Bad size or offset in db for virt disk %s\n",
		    desc[ITM_VADD_NAME].it_val);
		return;
	}

	if(sscanf(desc[ITM_VADD_CREATED].it_val, "%D", &created) != 1)
		time(&created);
	if(sscanf(desc[ITM_VADD_MODIFIED].it_val, "%D", &modified) != 1)
		time(&modified);

	vd = q_alloc(struct virtd, VIRTD_TYPE);
	q_init(vd);
	(void)strncpy(vd->vd_desc, desc[ITM_VADD_OWN].it_val, NDESC);
	(void)strncpy(vd->vd_pack, desc[ITM_VADD_NAME].it_val,
		sizeof(vd->vd_pack) - 1);
	(void)strncpy(vd->vd_ropasswd, desc[ITM_VADD_ROCAP].it_val,
		sizeof(vd->vd_ropasswd) - 1);
	(void)strncpy(vd->vd_expasswd, desc[ITM_VADD_EXCAP].it_val,
		sizeof(vd->vd_expasswd) - 1);
	(void)strncpy(vd->vd_shpasswd, desc[ITM_VADD_SHCAP].it_val,
		sizeof(vd->vd_shpasswd) - 1);
	vd->vd_uid = uid;
	vd->vd_modes = modes;
	vd->vd_offset = offset;
	vd->vd_blocks = blocks;
	vd->vd_created = created;
	vd->vd_modified = modified;
	vd->pd_back = vd->pd_forw = pd;
/*	vd->vd_flags = 0;	*/
	vd->vd_mode = RVDMNONE;
	vd->vd_links = 0;

	if (desc[ITM_VADD_OWNH].it_val != NULL &&
	    strcmp(desc[ITM_VADD_OWNH].it_val, "") != 0)
		vd->vd_host.s_addr = inet_addr(desc[ITM_VADD_OWNH].it_val);
	else
		vd->vd_host.s_addr = 0;

	pd_vdadd(pd, vd);
}

/* Like vd_add but interactive with terminal.
 */

vd_new()
{
	register struct	virtd	*vd;
	register struct	sockaddr_in	*sin;
	int	len;
	char	blkstr[SSIZE];
	char	*cp;			/* Used to calculate sbuf length. */
	int	slen;			/* Used to calculate sbuf length. */

/*	printf("Add new virtual disk\n");	*/
	vd = q_alloc(struct virtd, VIRTD_TYPE);
	q_init(vd);

	printf("Virtual disk name: ");	
	if (fgets(vd->vd_pack, sizeof(vd->vd_pack) - 1, stdin) == NULL ||
	    strlen(vd->vd_pack) == 0)
		goto quit;

	vd->vd_pack[strlen(vd->vd_pack) - 1] = '\0'; /* punt trailing nl */
	if (vd_lookup(vd->vd_pack) != NULL) {
		fprintf(stderr, "Duplicate virtual disk name\n");
		goto quit;
	}


	printf("Virtual disk uid: ");	
	if (fgets(blkstr, SSIZE, stdin) == NULL || sscanf(blkstr, "%D",
	    &vd->vd_uid) != 1) {
		fprintf(stderr, "Error: must be numeric\n");
		goto quit;
	}

	if (vd_look_uid(vd->vd_uid) != NULL) {
		fprintf(stderr, "Duplicate virtual disk uid\n");
		goto quit;
	}

	printf("Owner: ");
	fgets(vd->vd_desc, NDESC, stdin);
	if (strlen(vd->vd_desc) != 0)
		vd->vd_desc[strlen(vd->vd_desc) - 1] = '\0';

	strncpy(vd->vd_ropasswd, getpass("Read-only password:"),
		sizeof(vd->vd_ropasswd) - 1);
	strncpy(vd->vd_expasswd, getpass("Exclusive password:"),
		sizeof(vd->vd_expasswd) - 1);
	strncpy(vd->vd_shpasswd, getpass("Shared password:"),
		sizeof(vd->vd_shpasswd) - 1);

	printf("Disk size in 512-byte blocks: ");
	if (fgets(blkstr, SSIZE, stdin) == NULL || sscanf(blkstr, "%D",
	    &vd->vd_blocks) != 1) {
		fprintf(stderr, "Error: must be numeric\n");
		goto quit;
	}

	printf("Allowable modes: ");
	if (fgets(blkstr, SSIZE, stdin) == NULL || sscanf(blkstr, "%D",
	    &vd->vd_modes) != 1) {
		fprintf(stderr, "Error: must be numeric\n");
		goto quit;
	}

	printf("Owning host (<CR> for none): ");
	if (fgets(blkstr, SSIZE, stdin) != NULL && strlen(blkstr) != 1) {
		blkstr[strlen(blkstr) - 1] = '\0';
		if ((sin = resolve_host(blkstr)) != NULL)
			vd->vd_host.s_addr = sin->sin_addr.s_addr;
		else {
			fprintf(stderr, "Unknown owning host\n");
			goto quit;
		}
	}
	else
		vd->vd_host.s_addr = 0;

	printf("Physical disk name (<CR> for any): ");
	fgets(blkstr, SSIZE, stdin);
	blkstr[strlen(blkstr) - 1] = '\0';
	if ((vd->pd_forw = pd_findspc(blkstr, vd->vd_blocks,
	    &vd->vd_offset)) == NULL) {
		fprintf(stderr, "No space for virtual disk\n");
quit:		obj_free((caddr_t)vd);
		return(FALSE);
	}

	vd->vd_modified = time(&(vd->vd_created));

	printf("Are you sure (y or n)? ");
	if (fgets(blkstr, SSIZE, stdin) == NULL || blkstr[0] != 'y')
		return(FALSE);

	if (sendctl) {
		vd_addf(vd, sbuf);

		/* Add the Kerberos authenticator to sbuf.
		 * (The empty password field is required.)
		 */
#ifdef	KERBEROS
		if (passw_flag == FALSE) {
			csprintf(&sbuf[strlen(sbuf)], "password=\n");
			if (!get_auth(sbuf, server_name, myname, TRUE)) {
				fprintf(stderr, 
					"%s: could not create authenticator\n",
					myname);
				goto quit;
			}
		} else
#endif	KERBEROS
			csprintf(&sbuf[strlen(sbuf)], "password=%s\n", passw);

		/* Calculate the length of the buffer.  Take into account
		 * escaped null characters.  (This is necessary for Kerberos.)
		 */
		cp = sbuf + strlen(sbuf);
		while (*(cp-1) == '\\') {
			cp++;
			cp += strlen(cp);
		}
		slen = cp - sbuf;

		if ((len = ctl_exch(sbuf, slen, rbuf, BUFLEN, TIMEOUT,
		    NRXMIT)) == 0)
			fprintf(stderr, "Server not responding\n");
		else {
			rbuf[len] = '\0';

			if(! pkt_parse(rbuf))
				goto quit;

			if(strcmp(k[0], "success")) {
				fprintf(stderr,
					"%s (%s)\n", V(_ERROR), V(_KEYWORD));
				goto quit;
			}
		}
	}

	pd_vdadd(vd->pd_forw, vd);
	time(&(vd->pd_forw->pd_modified));
	return(TRUE);
}

/* Exchange pack names of two virtual disks
 */

vd_exch()
{
	register struct virtd	*vd1, *vd2;
	char	name1[VD_NAME_LEN+1], name2[VD_NAME_LEN+1];
	int	len;
	char	*cp;			/* Used to calculate sbuf length. */
	int	slen;			/* Used to calculate sbuf length. */

/*	printf("Exchange virtual disk names\n");	*/

	printf("First virtual disk name: ");
	if (fgets(name1, sizeof(name1) - 1, stdin) == NULL ||
			(len = strlen(name1)) == 0)
		goto quit;
	else
		name1[len - 1] = '\0';

	if ((vd1 = vd_lookup(name1)) == NULL) {
		fprintf(stderr, "No such virtual disk\n");
		goto quit;
	}

	printf("Second virtual disk name: ");
	if (fgets(name2, sizeof(name2) - 1, stdin) == NULL ||
			(len = strlen(name2)) == 0)
		return(FALSE);
	else
		name2[len - 1] = '\0';

	if ((vd2 = vd_lookup(name2)) == NULL) {
		fprintf(stderr, "No such virtual disk\n");
		goto quit;
	}

	if (sendctl) {
		csprintf(sbuf,
"operation=exchange_names\n\
name1=%s\n\
uid1=%D\n\
password1=%s\n\
name2=%s\n\
uid2=%D\n\
password2=%s\n",
		  vd1->vd_pack, vd1->vd_uid, vd1->vd_expasswd,
		  vd2->vd_pack, vd2->vd_uid, vd2->vd_expasswd);

		/* Add the Kerberos authenticator to sbuf.
		 */
#ifdef	KERBEROS
		if (passw_flag == FALSE) {
			if (!get_auth(sbuf, server_name, myname, TRUE)) {
				fprintf(stderr, 
					"%s: could not create authenticator\n",
					myname);
				goto quit;
			}
		}
#endif	KERBEROS

		/* Calculate the length of the buffer.  Take into account
		 * escaped null characters.  (This is necessary for Kerberos.)
		 */
		cp = sbuf + strlen(sbuf);
		while (*(cp-1) == '\\') {
			cp++;
			cp += strlen(cp);
		}
		slen = cp - sbuf;

		if ((len = ctl_exch(sbuf, slen, rbuf, BUFLEN, TIMEOUT,
		    NRXMIT)) == 0)
			fprintf(stderr, "Server not responding\n");
		else {
			rbuf[len] = '\0';

			if(! pkt_parse(rbuf))
				goto quit;

			if(strcmp(k[0], "success")) {
				fprintf(stderr, "%s (%s)\n", V(_ERROR),
				  V(_KEYWORD));
	quit:			return(FALSE);
			}
		}
	}

	strncpy(vd1->vd_pack, name2, sizeof(vd1->vd_pack) - 1);
	strncpy(vd2->vd_pack, name1, sizeof(vd2->vd_pack) - 1);

	return(TRUE);
}

/* Delete a virtual disk.
 */
vd_rm()
{
	register struct	virtd	*vd;
	register struct	sockaddr_in	*sin;
	int	len;
	int	uid_given = 0;
	u_long	uid;
	char	blkstr[SSIZE];
	char	*cp;			/* Used to calculate sbuf length. */
	int	slen;			/* Used to calculate sbuf length. */

/*	printf("Delete a virtual disk\n");	*/

	blkstr[0] = '\000';

	printf("Virtual disk name: ");
	if (fgets(blkstr, SSIZE, stdin) == NULL || strlen(blkstr) == 0) {
		printf("Virtual disk uid: ");	
		if (fgets(blkstr, SSIZE, stdin) == NULL || sscanf(blkstr, "%D",
		    &uid) != 1) {
			fprintf(stderr, "Error: must be numeric\n");
			goto quit;
		}
		else if((vd = vd_look_uid(uid)) == NULL) {
			fprintf(stderr, "No such virtual disk\n");
			goto quit;
		}

	}
	else {
		blkstr[strlen(blkstr) - 1] = '\0'; /* punt trailing nl */
		if ((vd = vd_lookup(blkstr)) == NULL) {
			fprintf(stderr, "No such virtual disk\n");
			goto quit;
		}
	}

	printf("Are you sure (y or n)? ");
	if (fgets(blkstr, SSIZE, stdin) == NULL || blkstr[0] != 'y')
		goto quit;

	if (sendctl) {
		csprintf(sbuf,"operation=delete_virtual\n");
		csprintf(&sbuf[strlen(sbuf)], "name=%s\nuid=%D\n",
				vd->vd_pack, vd->vd_uid);

		/* Add the Kerberos authenticator to sbuf.
		 * (The empty password field is required.)
		 */
#ifdef	KERBEROS
		if (passw_flag == FALSE) {
			csprintf(&sbuf[strlen(sbuf)], "password=\n");
			if (!get_auth(sbuf, server_name, myname, TRUE)) {
				fprintf(stderr, 
					"%s: could not create authenticator\n",
					myname);
				goto quit;
			}
		} else
#endif	KERBEROS
			csprintf(&sbuf[strlen(sbuf)], "password=%s\n", passw);

		/* Calculate the length of the buffer.  Take into account
		 * escaped null characters.  (This is necessary for Kerberos.)
		 */
		cp = sbuf + strlen(sbuf);
		while (*(cp-1) == '\\') {
			cp++;
			cp += strlen(cp);
		}
		slen = cp - sbuf;

		if ((len = ctl_exch(sbuf, slen, rbuf, BUFLEN, TIMEOUT,
		    NRXMIT)) == 0)
			fprintf(stderr, "Server not responding\n");
		else {
			rbuf[len] = '\0';

			if(! pkt_parse(rbuf))
				goto quit;

			if(strcmp(k[0], "success")) {
				fprintf(stderr, "%s (%s)\n", V(_ERROR),
				  V(_KEYWORD));
	quit:			return(FALSE);
			}
		}
	}

	vd_delete(vd);
	return(TRUE);
}


/* Print out a prompt and return TRUE if the user wants to change "str".
 * The use of fgets limits the string size and, to some extent, the
 * flushing of additional strings on the answer line.  The use of sscanf
 * allows leading white space to be parsed.  This is only used by vd_mod.
 */
#define	LINELEN 128

boolean
do_you_want_to_change(str)
	char	*str;
{
	char	answer[LINELEN];
	char	parsed_answer[LINELEN];

	printf("Do you want to change the %s <y/n>: ", str);
	if (fgets(answer, LINELEN, stdin) == NULL)
		return(FALSE);
	if (sscanf(answer, "%s", parsed_answer) != 1)
		return(FALSE);
	if (parsed_answer[0] == 'y' || parsed_answer[0] == 'Y')
		return(TRUE);
	return(FALSE);
}


vd_mod()
{
	register struct	virtd	*vd, *nvd;
	register struct	sockaddr_in	*sin;
	register char	*pw;
	int	len, nblk, mask;
	int	uid_given = 0;
	u_long	uid;
	char	blkstr[SSIZE];
	char	*cp;			/* Used to calculate sbuf length. */
	int	slen;			/* Used to calculate sbuf length. */

/*	printf("Modify an existing virtual disk\n");	*/

	blkstr[0] = '\000';
	printf("Virtual disk uid: ");	
	if(fgets(blkstr, SSIZE, stdin) == NULL)
		return(FALSE);
	
	if(strlen(blkstr) > 1) {
		if(sscanf(blkstr, "%D", &uid) != 1) {
			fprintf(stderr, "Error: must be numeric\n");
			return(FALSE);
		}
		if((vd = vd_look_uid(uid)) == NULL) {
			fprintf(stderr, "No such virtual disk\n");
			return(FALSE);
		}
		uid_given = 1;
	}
	printf("Virtual disk name: ");
	if (fgets(blkstr, VD_CAPAB_LEN, stdin) == NULL || strlen(blkstr) == 0)
		return(FALSE);

	blkstr[strlen(blkstr) - 1] = '\0'; /* punt trailing nl */
	if (!uid_given && (vd = vd_lookup(blkstr)) == NULL) {
		fprintf(stderr, "No such virtual disk\n");
		return(FALSE);
	}

	nvd = q_alloc(struct virtd, VIRTD_TYPE);
	*nvd = *vd;

	if(uid_given)
		strncpy(nvd->vd_pack, blkstr, sizeof(nvd->vd_pack));
		
	printf("Owner (%s): ", vd->vd_desc);
	fgets(blkstr, NDESC, stdin);
	if (strlen(blkstr) > 1) {
		blkstr[strlen(blkstr) - 1] = '\0';
		strncpy(nvd->vd_desc, blkstr, NDESC);
	}

	if (do_you_want_to_change("read-only password")) {
		pw = getpass("Read-only password:");
		strncpy(nvd->vd_ropasswd, pw, sizeof(vd->vd_ropasswd) - 1);
	}

	if (do_you_want_to_change("exclusive password")) {
		pw = getpass("Exclusive password:");
		strncpy(nvd->vd_expasswd, pw, sizeof(vd->vd_expasswd) - 1);
	}

	if (do_you_want_to_change("shared password")) {
		pw = getpass("Shared password:");
		strncpy(nvd->vd_shpasswd, pw, sizeof(vd->vd_shpasswd) - 1);
	}

	printf("Disk size in 512-byte blocks (%D): ", vd->vd_blocks);
	if (fgets(blkstr, SSIZE, stdin) != NULL && sscanf(blkstr, "%D",
	    &nblk) == 1) {
		nvd->vd_blocks = nblk;
		if (nvd->vd_blocks > vd->vd_blocks) {
			fprintf(stderr, "Error: can't increase disk size\n");
			goto quit;
		}
	}

	printf("Allowable modes (%D): ", vd->vd_modes);
	if (fgets(blkstr, SSIZE, stdin) != NULL && sscanf(blkstr, "%D",
	    &mask) == 1)
		nvd->vd_modes = mask;

	if (do_you_want_to_change("owning host")) {
		printf("Owning host (%s): ",
			vd->vd_host.s_addr ? fhostname(vd->vd_host.s_addr):"");
		if (fgets(blkstr,SSIZE,stdin) == NULL ) {
			fprintf(stderr, "Unknown owning host\n");
			goto quit;
		}
		if (strlen(blkstr) == 1) {
			nvd->vd_host.s_addr = 0;
		} else {
			blkstr[strlen(blkstr) - 1] = '\0';
			if ((sin = resolve_host(blkstr)) != NULL) {
				nvd->vd_host.s_addr = sin->sin_addr.s_addr;
			} else {
				fprintf(stderr, "Unknown owning host\n");
quit:				obj_free((caddr_t)nvd);
				return(FALSE);
			}
		}
	}

	printf("Are you sure (y or n)? ");
	if (fgets(blkstr, SSIZE, stdin) == NULL || blkstr[0] != 'y')
		goto quit;

	time(&(nvd->vd_modified));

	if (sendctl) {
		csprintf(sbuf,
"operation=modify_virtual\n\
name=%s\n\
owner=%s\n\
rocap=%s\n\
excap=%s\n\
shcap=%s\n\
modes=%D\n\
blocks=%D\n\
created=%D\n\
modified=%D\n\
ownhost=%s\n",
			nvd->vd_pack,
			nvd->vd_desc, nvd->vd_ropasswd, nvd->vd_expasswd,
			nvd->vd_shpasswd, nvd->vd_modes, nvd->vd_blocks,
			nvd->vd_created, nvd->vd_modified,
			(nvd->vd_host.s_addr ? inet_ntoa(nvd->vd_host) : "")
			);

		if(uid_given)
			csprintf(&sbuf[strlen(sbuf)], "uid=%D\n", nvd->vd_uid);

		/* Add the Kerberos authenticator to sbuf.
		 * (The empty password field is required.)
		 */
#ifdef	KERBEROS
		if (passw_flag == FALSE) {
			csprintf(&sbuf[strlen(sbuf)], "password=\n");
			if (!get_auth(sbuf, server_name, myname, TRUE)) {
				fprintf(stderr, 
					"%s: could not create authenticator\n",
					myname);
				goto quit;
			}
		} else
#endif	KERBEROS
			csprintf(&sbuf[strlen(sbuf)], "password=%s\n", passw);

		/* Calculate the length of the buffer.  Take into account
		 * escaped null characters.  (This is necessary for Kerberos.)
		 */
		cp = sbuf + strlen(sbuf);
		while (*(cp-1) == '\\') {
			cp++;
			cp += strlen(cp);
		}
		slen = cp - sbuf;

		if ((len = ctl_exch(sbuf, slen, rbuf, BUFLEN, TIMEOUT,
		    NRXMIT)) == 0)
			fprintf(stderr, "Server not responding\n");
		else {
			rbuf[len] = '\0';

			if(! pkt_parse(rbuf))
				goto quit;

			if(strcmp(k[0], "success")) {
				fprintf(stderr, "%s (%s)\n", V(_ERROR),
				  V(_KEYWORD));
				goto quit;
			}
		}
	}

	*vd = *nvd;				/* pretty grotty */
	obj_free((caddr_t)nvd);
	return(TRUE);
}

vd_write(pd, of)

register struct physd	*pd;
register FILE	*of;

{
	char	*inet_ntoa();
	struct virtd	*vd, *td;

	td = (struct virtd *) &(pd->vd_forw);
	vd = ((struct virtd *) td)->vd_forw;

	while(vd != td) {
		vd_addf(vd, sbuf);
		fputs(sbuf, of);
		putc('\n', of);
		vd = q_head(vd, struct virtd *);
	}
	return(!ferror(of));
}

vd_addf(vd, buf)

/* Format a control message to add the specified virtual disk.
 */

register struct	virtd	*vd;
register char	*buf;
{

	csprintf(buf,
"operation=add_virtual\n\
physical=%s\n\
name=%s\n\
uid=%D\n\
owner=%s\n\
rocap=%s\n\
excap=%s\n\
shcap=%s\n\
modes=%D\n\
offset=%D\n\
blocks=%D\n\
created=%D\n\
modified=%D\n\
ownhost=%s\n",
		vd->pd_forw->pd_file, vd->vd_pack, vd->vd_uid,
		vd->vd_desc, vd->vd_ropasswd, vd->vd_expasswd,
		vd->vd_shpasswd, vd->vd_modes, vd->vd_offset,
		vd->vd_blocks, vd->vd_created, vd->vd_modified,
		vd->vd_host.s_addr ? inet_ntoa(vd->vd_host) : "");
}

/* This is a little safer than the standard version
 */

char *
fgets(s, n, fp)

char	*s;
FILE	*fp;

{
	char	*os = s;
	int	c;

	while((c = getc(fp)) != EOF) {
		if(n > 1) {
			*s++ = c;
			--n;
		}
		if(c == '\n')
			break;
	}
	*s = '\0';
	if(c == EOF)
		return(NULL);
	return(os);
}
