/*
 * 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/server/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.  This includes reading them in from the
 * database files.  These routines are pretty ugly as they still use
 * the old database file format, in which the physical disk descriptions
 * are spread out across two different database files, and in which the
 * physical disks are restricted to be UNIX raw devices.
 */


#include	<sys/types.h>
#include	<stdio.h>
#include	<sys/file.h>
#include	<netinet/in.h>
#include	<machineio/vdconst.h>
#ifdef	KERBEROS
#include	<krb.h>
#endif	KERBEROS

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

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

/* The static 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 is the main database input routine.  It opens the rvd master file
 * (whose name is stored in the global "rvdmaster") and reads it in one
 * record at a time.  Each in-use record describes one physical disk, so
 * a physical disk descriptor is allocated for it and a small amount of
 * data copied to it.  Then the database info file for that physical disk
 * is opened and its header is read into the physical disk descriptor.
 * Next, the virtual disk input routine is called to read in all the virtual
 * disk descriptors from the database info file, the info file is closed,
 * and the physical disk descriptor is chained into the list.  When done,
 * the rvdmaster file is closed.
 * Any errors occuring while reading the master file are fatal; errors in
 * reading any of the database info files result in that physical disk
 * not being used.
 */

pd_input()

{
#ifdef	__UNDEF__
	register FILE	*mfd;		/* master file descriptor */
	register FILE	*dbfd;		/* database info file descriptor */
	struct	mfrec	mfr;		/* temp for holding master file rec */
	char	dbfname[NFILE];		/* temp for making db info file name */
	register struct	physd	*pd;	/* current physical disk descriptor */

	if ((mfd = fopen(rvdmaster, "r")) == NULL) { /* can't open master */
	    syslog(LOG_ERR_, 
	       "Unable to open master file; assuming control initialization");
	    return;
	}

	while (fread((char *)&mfr, sizeof(struct mfrec), 1, mfd) == 1) {

		if (strncmp(mfr.mf_rdname, "", 8) == 0) /* unused */
			continue;

		pd = (struct physd *)obj_alloc(sizeof(struct physd),
		    PHYSD_TYPE);
		(void)strcpy(pd->pd_file, "/dev/r");	/* bogosity */
		(void)strncat(pd->pd_file, mfr.mf_rdname, 8);
		pd->blocks = mfr.mf_nblk;

/* Construct name of database info file: "db" || raw device name */

		(void)strcpy(dbfname, "db");
		(void)strncat(dbfname, mfr.mf_rdname, 8);
		if ((dbfd = fopen(dbfname, "r")) == NULL) {
			syslog(LOG_ERR, 
			       "pd_input: error opening %s: %m",
			       dbfname);
			obj_free((char *)pd);	/* give up on this pd */
			continue;
		}

		if (fread((char *)&pd->pd_rawdr[0], DBHDRSIZE, 1, dbfd) != 1) {
			syslog(LOG_ERR, 
			       "pd_input: read error on %s: %m",
			       dbfname);
			(void)fclose(dbfd);
			obj_free((char *)pd);	/* give up on this pd */
			continue;
		}

		if ((pd->pd_fd = open(pd->pd_file, O_RDWR, 0)) < 0) {
			syslog(LOG_ERR, 
			       "pd_input: can't open %s: %m",
			       pd->pd_file);
			(void)fclose(dbfd);
			obj_free((char *)pd);	/* give up on this pd */
			continue;
		}

		if (!vd_input(dbfd, pd, pd->pd_disks)) { /* error in vd's */
			(void)fclose(dbfd);
			obj_free((char *)pd);
			continue;
		}

		(void)fclose(dbfd);
		ins_q_tail(pd, &all_physds);	/* queue the descriptor */
	}

	if (ferror(mfd)) {			/* error reading master file */
	    syslog(LOG_ALERT, "pd_input: reading master file");
	    exit(1);
	}

	(void)fclose(mfd);
#endif	__UNDEF__
}


/* 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, open a new
 * file descriptor on that disk, and chain the descriptor into the list of
 * disks.  Send a success or failure message to the other end.
 */

/*ARGSUSED*/
pd_add(fhost, op, desc, nitem)
	struct	sockaddr_in	*fhost;	/* guy at the other end */
	char	*op;			/* our operation name */
	struct item	desc[];		/* message descriptor */
	int	nitem;			/* number of descriptor items */
{
	register struct physd	*pd;	/* for holding descriptor */
	int	blocks;			/* size of disk in blocks */
	int	created;		/* date created		*/
	int	modified;		/* date modified	*/
	int	pdfd;			/* file desc. for phys disk */
	char	*nonce;			/* nonce string */
	char	*pw;			/* password string */
	char	*uname;			/* Authenticated user name. */
	boolean	authent_ok;		/* Authenticator OK flag. */

	uname = NULL;
	authent_ok = FALSE;
	nonce = desc[ITM_PADD_NONCE].it_val;
	if ((pw = desc[ITM_PADD_PASSW].it_val) == NULL)
		pw = "";

	/* If an authenticator is present, is the user on the operations
	 * or administration access control lists?
	 */
#ifdef	KERBEROS
    {
	KTEXT	authent;

	if ((authent = (struct ktext *)desc[ITM_PADD_AUTH].it_val) != NULL) {
		if ((uname = auth_to_user(authent, fhost->sin_addr)) != NULL) {
			if (priv_user(uname, TRUE, FALSE))
				authent_ok = TRUE;
		}
	}

    }
#endif	KERBEROS

	/* If there is no authenticator then is the password the operations 
	 * or administration password?
	 */
	if (!authent_ok) {
		if (!authorized(pw,TRUE,FALSE)) { 
			if (loglevel(LOG_CLIENT_ERROR)) {
			    syslog(LOG_INFO,
				"add physical: bad password for device %s (%s)",
				desc[ITM_PADD_FILE].it_val,
				inet_ntoa(fhost->sin_addr));
			}
			ctl_failure(fhost, op, nonce, "unauthorized operation");
			return;
		}
	}

	if ((pd = pd_lookup(desc[ITM_PADD_FILE].it_val)) != NULL) {
		if (loglevel(LOG_CLIENT_ERROR)) {
		    syslog(LOG_INFO,
			"add physical: file %s already in use, (%s)",
			desc[ITM_PADD_FILE].it_val,
			inet_ntoa(fhost->sin_addr));
		}
		ctl_failure(fhost, op, nonce, "file already in use");
		return;
	}

	if (sscanf(desc[ITM_PADD_BLOCKS].it_val, "%D", &blocks) != 1) {
		if (loglevel(LOG_CLIENT_ERROR))
			syslog(LOG_INFO,"add physical: invalid size, (%s)",
		       inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce, "invalid size");
		return;
	}

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

	if ((pdfd = open(desc[ITM_PADD_FILE].it_val, O_RDWR, 0)) < 0) {
		if (loglevel(LOG_ERROR))
			syslog(LOG_INFO,"add physical: can't open %s, (%s)",
		       desc[ITM_PADD_FILE].it_val, inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce, "can't open physical disk");
		return;
	}

	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;
	pd->pd_fd = pdfd;
	pd->pd_mode |= RVD_USE_PHYS;
	q_init(&(pd->vd_forw));

	ins_q_tail(pd, &all_physds);

	if (loglevel(LOG_SPINS)) {
		if (uname != NULL) {
		    syslog(LOG_INFO,
			"add physical: physical device %s added by %s from %s",
			desc[ITM_PADD_FILE].it_val,
			uname, inet_ntoa(fhost->sin_addr));
		} else {
		    syslog(LOG_INFO,
			"add physical: physical device %s added by %s",
			desc[ITM_PADD_FILE].it_val,
			inet_ntoa(fhost->sin_addr));
		}
	}

	ctl_success(fhost, op, nonce);
}


/* Delete the physical disk specified by the descriptor.
 */

/*ARGSUSED*/
pd_del(fhost, op, desc, nitem)
	struct	sockaddr_in	*fhost;		/* requesting foreign host */
	char	*op;				/* operation name */
	register struct	item	desc[];		/* operation descriptor */
	int	nitem;				/* number of items in desc */
{
	struct physd	*pd;
	char	*pw;			/* password string */
	char	*uname;			/* Authenticated user name. */
	char	*nonce;			/* nonce string */
	boolean	authent_ok;		/* Authenticator OK flag. */

	pd = NULL;
	uname = NULL;
	authent_ok = FALSE;
	nonce = desc[ITM_PDEL_NONCE].it_val;
	if ((pw = desc[ITM_PDEL_PASSW].it_val) == NULL)
		pw = "";

	/* If an authenticator is present, is the user on the operations list?
	 */
#ifdef	KERBEROS
    {
	KTEXT	authent;

	if ((authent = (struct ktext *)desc[ITM_PDEL_AUTH].it_val) != NULL) {
		if ((uname = auth_to_user(authent, fhost->sin_addr)) != NULL) {
			if (priv_user(uname, FALSE, FALSE))
				authent_ok = TRUE;
		}
	}
    }
#endif	KERBEROS

	/* If no authenticator, check for the operations password.
	 */
	if (!authent_ok) {
		if (!authorized(pw, TRUE, FALSE)) {
			if (loglevel(LOG_CLIENT_ERROR))
			    syslog(LOG_INFO,
				"delete physical: bad password on %s by %s",
				desc[ITM_PDEL_NAME].it_val, 
				inet_ntoa(fhost->sin_addr));
			ctl_failure(fhost, op, nonce, "unauthorized operation");
			return;
		}
	}

	if ((pd = pd_lookup(desc[ITM_PDEL_NAME].it_val)) == NULL) {
		if (loglevel(LOG_CLIENT_ERROR))
			syslog(LOG_INFO,
				"delete physical: no such disk, name=%s,(%s)",
				desc[ITM_PDEL_NAME].it_val,
				inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce, "no such physical disk");
		return;
	}
			
	if (pd->pd_disks != 0) {
		if (loglevel(LOG_CLIENT_ERROR))
			syslog(LOG_INFO,
				"delete physical: %s still has %d virtual disks allocated, (%s)",
				pd->pd_disks, desc[ITM_PDEL_NAME].it_val,
				inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce, "virtual disks still allocated");
		return;
	}
	if (close(pd->pd_fd) != NULL) {
		if (loglevel(LOG_CLIENT_ERROR))
			syslog(LOG_INFO,
				"delete physical: can not close %s, (%s)",
				pd->pd_disks, desc[ITM_PDEL_NAME].it_val,
				inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce, "can not close physical disk");
		return;
	}
	if (loglevel(LOG_SPINS)) {
		if (uname != NULL) {
		    syslog(LOG_INFO,
			"delete physical: physical disk %s deleted by %s from %s",
			desc[ITM_PDEL_NAME].it_val,
			uname, inet_ntoa(fhost->sin_addr));
		} else {
		    syslog(LOG_INFO,
			"delete physical: physical disk %s deleted from %s",
			desc[ITM_PDEL_NAME].it_val,
			inet_ntoa(fhost->sin_addr));
		}
	}

	rem_q_elem(pd);
	obj_free(pd);
	ctl_success(fhost, op, nonce);
}

/* Enable the physical disk specified by the descriptor.
 * If the password checks out and the disk exists, this routine just
 * sets the RVD_USE_PHYS bit in the disk's pd_mode field.
 */

/*ARGSUSED*/
pd_use(fhost, op, desc, nitem)
	struct	sockaddr_in	*fhost;		/* requesting foreign host */
	char	*op;				/* operation name */
	register struct	item	desc[];		/* operation descriptor */
	int	nitem;				/* number of items in desc */
{
	struct physd	*pd;
	char	*pw;			/* password string */
	char	*uname;			/* Authenticated user name. */
	char	*nonce;			/* nonce string */
	boolean	authent_ok;		/* Authenticator OK flag. */

	pd = NULL;
	uname = NULL;
	authent_ok = FALSE;
	nonce = desc[ITM_PUSE_NONCE].it_val;
	if ((pw = desc[ITM_PUSE_PASSW].it_val) == NULL)
		pw = "";

	/* If an authenticator is present, is the user on the operations list?
	 */
#ifdef	KERBEROS
    {
	KTEXT	authent;

	if ((authent = (struct ktext *)desc[ITM_PUSE_AUTH].it_val) != NULL) {
		if ((uname = auth_to_user(authent, fhost->sin_addr)) != NULL) {
			if (priv_user(uname, FALSE, FALSE))
				authent_ok = TRUE;
		}
	}
    }
#endif	KERBEROS

	/* If no authenticator, check for the operations password.
	 */
	if (!authent_ok) {
		if (!authorized(pw, FALSE, FALSE)) {
			if (loglevel(LOG_CLIENT_ERROR))
			    syslog(LOG_INFO,
				"use physical: bad password on %s by %s",
				desc[ITM_PUSE_NAME].it_val, 
				inet_ntoa(fhost->sin_addr));
			ctl_failure(fhost, op, nonce, "unauthorized operation");
			return;
		}
	}

	if ((pd = pd_lookup(desc[ITM_PUSE_NAME].it_val)) == NULL) {
		if (loglevel(LOG_CLIENT_ERROR))
			syslog(LOG_INFO,
				"use physical: no such disk, name=%s,(%s)",
				desc[ITM_PUSE_NAME].it_val,
				inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce, "no such physical disk");
		return;
	}
			
	if ((pd->pd_mode & RVD_USE_PHYS) != NULL) {
		if (loglevel(LOG_CLIENT_ERROR))
			syslog(LOG_INFO,
				"use physical: %s is already enabled, (%s)",
				desc[ITM_PUSE_NAME].it_val,
				inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce, "physical disk already enabled");
		return;
	}
			
	if (loglevel(LOG_SPINS)) {
		if (uname != NULL) {
		    syslog(LOG_INFO,
			"use physical: physical disk %s enabled by %s from %s",
			desc[ITM_PUSE_NAME].it_val,
			uname, inet_ntoa(fhost->sin_addr));
		} else {
		    syslog(LOG_INFO,
			"use physical: physical disk %s enabled from %s",
			desc[ITM_PUSE_NAME].it_val,
			inet_ntoa(fhost->sin_addr));
		}
	}

	pd->pd_mode |= RVD_USE_PHYS;
	ctl_success(fhost, op, nonce);
}

/* Disable the physical disk specified by the descriptor.
 * If the password checks out and the disk exists, this routine just
 * clears the RVD_USE_PHYS bit in the disk's pd_mode field.
 */

/*ARGSUSED*/
pd_duse(fhost, op, desc, nitem)
	struct	sockaddr_in *fhost;	/* requesting foreign host */
	char	*op;			/* operation name */
	struct	item desc[];		/* operation descriptor */
	int	nitem;			/* number of items in desc */
{
	struct physd	*pd;
	char	*pw;			/* password string */
	char	*uname;			/* Authenticated user name. */
	char	*nonce;			/* nonce string */
	boolean	authent_ok;		/* Authenticator OK flag. */

	pd = NULL;
	uname = NULL;
	authent_ok = FALSE;
	nonce = desc[ITM_PDUSE_NONCE].it_val;
	if ((pw = desc[ITM_PDUSE_PASSW].it_val) == NULL)
		pw = "";

	/* If an authenticator is present, is the user on the operations list?
	 */
#ifdef	KERBEROS
    {
	KTEXT	authent;

	if ((authent = (struct ktext *)desc[ITM_PDUSE_AUTH].it_val) != NULL) {
		if ((uname = auth_to_user(authent, fhost->sin_addr)) != NULL) {
			if (priv_user(uname, FALSE, FALSE))
				authent_ok = TRUE;
		}
	}
    }
#endif	KERBEROS


	/* If no authenticator, check for the operations password.
	 */
	if (!authent_ok) {
		if (!authorized(pw, FALSE, FALSE)) {
			if (loglevel(LOG_CLIENT_ERROR)) {
			    syslog(LOG_INFO,
				"disuse physical: bad password on %s by %s",
				desc[ITM_PDUSE_NAME].it_val, 
				inet_ntoa(fhost->sin_addr));
			}
			ctl_failure(fhost, op, nonce, "unauthorized operation");
			return;
		}
	}

	if ((pd = pd_lookup(desc[ITM_PDUSE_NAME].it_val)) == NULL) {
		if (loglevel(LOG_CLIENT_ERROR))
			syslog(LOG_INFO,
				"disuse physical: no such disk, name=%s,(%s)",
				desc[ITM_PDUSE_NAME].it_val,
				inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce, "no such physical disk");
		return;
	}
			
	if ((pd->pd_mode & RVD_USE_PHYS) == NULL) {
		if (loglevel(LOG_CLIENT_ERROR))
			syslog(LOG_INFO,
				"disuse physical: %s is already disabled, (%s)",
				desc[ITM_PDUSE_NAME].it_val,
				inet_ntoa(fhost->sin_addr));
		ctl_failure(fhost, op, nonce,
			"physical disk is already disabled");
		return;
	}
			
	if (loglevel(LOG_SPINS)) {
		if (uname != NULL) {
		    syslog(LOG_INFO,
			"disuse physical: physical disk %s disabled by %s from %s",
			desc[ITM_PDUSE_NAME].it_val,
			uname, inet_ntoa(fhost->sin_addr));
		} else {
		    syslog(LOG_INFO,
			"disuse physical: physical disk %s disabled from %s",
			desc[ITM_PDUSE_NAME].it_val,
			inet_ntoa(fhost->sin_addr));
		}
	}

	pd->pd_mode &= ~RVD_USE_PHYS;
	ctl_success(fhost, op, nonce);
}


/* 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 */
{
	pd->pd_disks--;
	pd->pd_used -= vd->vd_blocks;
	rem_q_elem(vd);
}
