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

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


#ifndef lint
static char rcsid_physd_c[] = "$Header:physd.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 the routines which maintain and manipulate the
 * physical disk descriptors for the vd database management program.
 */


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

#include	"ctl_msgs.h"
#include	"rvd_types.h"
#include	"ctl_pkt.h"
#include	"obj.h"
#include	"queue.h"
#include	"physd.h"
#include	"virtd.h"
#include	"vddb.h"
#include	"extern.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;

/* all_physds is the list header for a linked list of all the
 * physical disk descriptors in the system.
 */

struct	physd_q	all_physds;

/* Initialize the physical disk descriptors.  Right now this just means
 * initializing the queue.
 */

pd_init()

{
	q_init(&all_physds);
}

/* This routine is called to handle a control request to add a new physical
 * disk.  The desc argument is a list of items describing the disk to be
 * added; nitem is the number of items in the list.  We create a physical
 * disk descriptor, fill it in from the message items,
 * and chain the descriptor into the list of
 * disks.  Send a success or failure message to the other end.
 */

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

struct	sockaddr_in	*fhost;		/* guy at the other end */
char	*op1;				/* our operation name */
register struct	item	desc[];		/* message descriptor */
int	nitem;				/* number of items in descriptor */
{
	register struct	physd	*pd;	/* for holding descriptor */
	int	blocks;			/* size of disk in blocks */
	int	created;		/* date created		  */
	int	modified;		/* date modified	  */

	if ((pd = pd_lookup(desc[ITM_PADD_FILE].it_val)) != NULL) {
		fprintf(stderr, "Duplicate physical disk %s in db\n",
		    desc[ITM_PADD_FILE].it_val);
		return;
	}

	if (sscanf(desc[ITM_PADD_BLOCKS].it_val, "%D", &blocks) != 1) {
		fprintf(stderr, "Invalid physical disk size in db, disk %s\n",
		    desc[ITM_PADD_FILE].it_val);
		return;
	}

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

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

	pd = q_alloc(struct physd, PHYSD_TYPE);
	q_init(pd);
	(void)strncpy(pd->pd_file, desc[ITM_PADD_FILE].it_val, NFILE);

	pd->pd_blocks = blocks;
	pd->pd_disks = 0;
	pd->pd_used = 0;
	pd->pd_created = created;
	pd->pd_modified = modified;
	q_init(&(pd->vd_forw));

	ins_q_tail(pd, &all_physds);
}

/* Lookup the physical disk with the specified name and return a pointer to
 * its descriptor structure.  Returns NULL if none is found.
 */

struct physd *
pd_lookup(name)

register char	*name;			/* disk name */
{
	struct physd	*pd, *td;

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

	while(pd != td) {
		if(! strcmp(name, pd->pd_file))
			return(pd);
		pd = q_head(pd, struct physd *);
	}
	return(NULL);
}

/* Add a new virtual disk to the specified physical disk.  Increment the
 * virtual disk count and the number of used blocks on the disk.
 */

pd_vdadd(pd, vd)

register struct	physd	*pd;		/* physical disk */
register struct	virtd	*vd;		/* virtual disk */

{
	struct virtd	*sd, *td;

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

	while(sd != td) {
		if(vd->vd_offset < sd->vd_offset) {
			ins_q_before(vd, sd);
			goto cleanup;
		}
		sd = q_head(sd, struct virtd *);
	}
	ins_q_tail(vd, sd);
cleanup:
	pd->pd_used += vd->vd_blocks;
	pd->pd_disks++;
}

/* Delete a virtual disk to the specified physical disk.  Decrement the
 * virtual disk count and the number of used blocks on the disk.
 */

pd_vddel(pd, vd)

register struct	physd	*pd;		/* physical disk */
register struct virtd	*vd;		/* virtual disk */
{
	rem_q_elem(vd);
	pd->pd_disks--;
	pd->pd_used -= vd->vd_blocks;
}

pd_new()
{
	register struct	physd	*pd;	/* for holding descriptor */
	char	blkstr[SSIZE];		/* temp for holding block string */
	register int	len;		/* length of rcvd response */
	char	*cp;			/* Used to calculate sbuf length. */
	int	slen;			/* Used to calculate sbuf length. */

/*	printf("Add new physical disk\n");	*/
	pd = q_alloc(struct physd, PHYSD_TYPE);
	q_init(pd);

	printf("Physical disk file name: ");
	if (fgets(pd->pd_file, NFILE, stdin) == NULL)
		goto quit;

	pd->pd_file[strlen(pd->pd_file) - 1] = '\0';	/* punt trailing nl */
	if (strlen(pd->pd_file) == 0)
		goto quit;

	if (pd_lookup(pd->pd_file) != NULL) {
		fprintf(stderr, "Duplicate physical disk %s in db\n",
		    pd->pd_file);
		goto quit;
	}

	printf("Size in 512-byte blocks: ");
	if (fgets(blkstr, SSIZE, stdin) == NULL || sscanf(blkstr, "%D",
	    &pd->pd_blocks) != 1) {
		fprintf(stderr, "Error: must be numeric\n");
quit:		obj_free((caddr_t)pd);
		return(FALSE);
	}

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

	pd->pd_created = time(&(pd->pd_modified));
	pd->pd_disks = 0;
	pd->pd_used = 0;
	q_init(&(pd->vd_forw));

	if (sendctl) {
		pd_addf(pd, 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;
			}
		}
	}

	ins_q_tail(pd, &all_physds);

	return(TRUE);
}

/* Find space for adding a virtual disk of at least nblk blocks on the physical
 * disk whose name is specified by phys.  If the name is "", search all disks
 * for space.  Returns a pointer to the physical disk structure, and stores the
 * starting offset in the location pointed to by offptr.  Returns NULL if no
 * space is available.
 */

struct physd *
pd_findspc(phys, nblk, offptr)

char	*phys;
u_long	nblk;
int	*offptr;
{
	register int	off;
	register struct	physd	*pd;

	if (phys[0] == '\0') {
		struct physd	*pd, *td;

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

		while(pd != td) {
			if ((off = pd_findoff(pd, nblk)) >= 0) {
				*offptr = off;
				return(pd);
			}
			pd = q_head(pd, struct physd *);
		}
	}
	else {
		if ((pd = pd_lookup(phys)) == NULL)
			fprintf(stderr, "No such physical disk\n");
		else if ((off = pd_findoff(pd, nblk)) >= 0) {
			*offptr = off;
			return(pd);
		}
	}
	return(NULL);
}

/* Find the starting block number of the chunk of free space of at least nblk
 * blocks on the specified physical disk, and return it.  Returns -1 if no
 * such chunk exists.
 */

int
pd_findoff(pd, blocks)

register struct	physd	*pd;
register u_long	blocks;

{
	struct virtd	*vd, *td;
	int	nextfree, maxchunk, highoffset;

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

	nextfree = highoffset = 0;

	while(vd != td) {
		if(nextfree + blocks <= vd->vd_offset)
			goto found;
		nextfree = vd->vd_offset + vd->vd_blocks;
		if(highoffset < nextfree)
			highoffset = nextfree;
		vd = q_head(vd, struct virtd *);
	}
	if(nextfree + blocks <= pd->pd_blocks)
found:		return(nextfree);

	return(-1);
}

pd_write(of)

register FILE	*of;

{
	struct physd	*pd, *td;

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

	while(pd != td) {
		pd_addf(pd, sbuf);
		fputs(sbuf, of);
		putc('\n', of);
		vd_write(pd, of);
		pd = q_head(pd, struct physd *);
	}
	return(!ferror(of));
}

/* Format a control message to add the specifed physical disk.
 */

pd_addf(pd, buf)

	register struct	physd	*pd;
	char	*buf;
{
	csprintf(buf, 
"operation=add_physical\n\
filename=%s\n\
blocks=%D\n\
created=%d\n\
modified=%d\n",
	  pd->pd_file, pd->pd_blocks, pd->pd_created, pd->pd_modified);
}
