/*
 * hooks.c
 *
 * This file contains procedures that will be used by programs other than emtd
 *   to authenticate and talk to an ADM server.
 *
 * $Author: dm3e $
 * $Date: 1993/01/08 19:22:35 $
 * $Revision: 1.4 $
 * $State: Exp $
 * $Locker: dm3e $
 */

/***********************************************************
        Copyright 1992 by Carnegie Mellon University

                      All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/

#include "emtd.h"
#include <varargs.h>
#include <volser.h>
#include <fcntl.h>

#define LIFETIME (MAXKTCTICKETLIFETIME)
#define ADM_EVAL(E,R,A,M) admlib_clt_Evaluate(admconn, E, R, A, M)
#define REAUTH_MIN_TIME (60*60)
#define SERVER info.site[i].server
#define PART info.site[i].partition
#define TYPE info.site[i].type
#define ZAPTIME (5*60)

static struct ktc_encryptionKey my_key;	/* OUT */
static struct ktc_token my_token;
static struct ktc_token *my_tokenp = &my_token;
static long         my_viceID;
static unsigned long admHostId;
static unsigned short admPortId;
static unsigned short admServiceId;
static unsigned short adm_connection_valid = 0;
static struct rx_connection *admconn;
static struct ktc_token admtoken;
static char        *zapfile_path;
struct ktc_encryptionKey akey;	/* OUT */

struct siteinf {
  char               *server;
  char               *partition;
  int                 type;
};

struct volinf {
  char               *name;
  long                rwid;
  long                roid;
  long                bkid;
  int                 nsites;
  struct siteinf     *site;
};

/***************************************************************************
 * [exported] hook_LogObj
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Dec  1 16:55:32 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long hook_LogObj(obj)
admint_objectPtr_t obj;
{
    admlib_obj_LogExp(ADMLIB_LOG_INTERACTIVE, stdout, "", obj);
    admlib_log_Log(ADMLIB_LOG_INTERACTIVE, stdout, "\n");
}

/***************************************************************************
 * [exported] emtd_Broadcast
 *
 * Description:
 *      This is a dummy procedure that allows hooks to link properly.
 *      --- sigh --- It's a hack.
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Dec  2 15:59:31 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long emtd_Broadcast(va_alist)
va_dcl
{
    return OK;
}

/***************************************************************************
 * [exported] ok_getst
 *
 * Description:
 *      adapted from getst() in librkb
 *
 *      ok_getst() takes a file descriptor, a string and a count.  It reads
 *      from the file until either it has read "count" characters, or until
 *      it reads a null byte.  When finished, what has been read exists in
 *      the given string "s".  If "count" characters were actually read, the
 *      last is changed to a null, so the returned string is always null-
 *      terminated.  ok_getst() returns the number of characters read,
 *      including the null terminator.
 *
 *      If there is a read error, it returns -1 (like the read(2) system call)
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 16:33:19 1992 - created by David Allen Markley
 *
 ***************************************************************************/
static 
ok_getst (fd, s, n)
     int                 fd;
     register char      *s;
     int                 n;
{
  register            count = n;
  int                 err;

  while ((err = read (fd, s, 1)) > 0 && --count)
    if (*s++ == '\0')
      return (n - count);
  if (err < 0)
    return (-1);
  *s = '\0';
  return (n - count);
}

#ifdef USE_MITKERBEROS
/***************************************************************************
 * [exported] GetKeyFromKeyFile
 *
 * Description:
 *      Reads the key file, and searches for the desired key. Returns the
 *      found key, or NULL. It also copies the key into the space pointed
 *      to by the argument key.
 *
 * Arguments:
 *      file		- The path to the key file.
 *      service		- The name of the service of the desired key.
 *      instance	- The name of the instance of the desired key.
 *      kvno		- The version number of the desired key.
 *      		If this is == -1, it gets the key with the highest kvno
 *      key 		- A pointer to where to place the found key.
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 16:32:40 1992 - created by David Allen Markley
 *
 ***************************************************************************/
char               *
GetKeyFromKeyFile (file, service, instance, kvno, key)
     char               *file,
                        *service,
                        *instance;
     long                kvno;
     struct ktc_encryptionKey *key;
{
  int                 stab,
                      found = 0;
  char                serv[SNAME_SZ];
  char                inst[INST_SZ];
  char                rlm[REALM_SZ];
  unsigned char       akey[8];
  unsigned char       vno;
  int                 count;

  if ((stab = open (file, O_RDONLY, 0400)) < 0) {
    fprintf (stderr, "emtd: Error reading %s\n", file);
    exit (errno);
  }
  /* argh. getst doesn't return error codes, it silently fails */
  while (((count = ok_getst (stab, serv, SNAME_SZ)) > 0)
	 && ((count = ok_getst (stab, inst, INST_SZ)) > 0)
	 && ((count = ok_getst (stab, rlm, REALM_SZ)) > 0)) {
    if (((count = read (stab, (char *) &vno, 1)) != 1) ||
	((count = read (stab, (char *) akey, 8)) != 8)) {
      if (count < 0)
	fprintf (stderr, "emtd: Error reading from key file.\n");
      else
	fprintf (stderr, "emtd: Key file truncated.\n");
      exit (errno);
    }
    if (!strcasecmp (service, serv) && !strcasecmp (instance, inst))
      if ((-1 == kvno) || (vno == kvno)) {
	count = 0;
	found = 1;
	break;
      }
  }
  if (count < 0) {
    fprintf (stderr, "emtd: Error reading %s\n", file);
    exit (errno);
  }
  (void) close (stab);
  if (found) {
    bcopy ((char *) akey, (char *) key, sizeof (struct ktc_encryptionKey));
    return (char *) akey;
  }
  fprintf (stderr, "hook: key not found in key file.\n");
  return NULL;
}
#endif				/* USE_MITKERBEROS */

/***************************************************************************
 * [exported] AuthenticateMe
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 16:30:12 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
AuthenticateMe ()
{
  register long       code;
  char               *name = EMTD_WORK_NAME;
  char               *instance = EMTD_WORK_INSTANCE;
  char                id = EMTD_WORK_ID;
  long keyId = 0;

  code = ka_Init (0);
  if (code) {
    fprintf (stderr, "Couldn't initialize auth package\n");
    exit (code);
  }
  setpag ();
#ifdef KERBEROS_ENV		/* ANDREW_CMU_EDU */
  ktc_newpag ();
#endif
#ifdef USE_MITKERBEROS
  GetKeyFromKeyFile (KEYFILE, name, instance, -1, &my_key);
#else
  emtd_conf_GetLatestKey(NULL, &keyId, &my_key);
#endif				/* USE_MITKERBEROS */
  fprintf (stdout, "Ticket file: %s\n", ktc_tkt_string ());
  code = emt_GetUserTickets (name, instance, THISCELL, &my_key, LIFETIME,
			     my_tokenp, &my_viceID);
  if (code) {
    fprintf (stderr, "hook: Error becoming %s.%s \(%ld\).\n",
	     name, instance, code);
    exit (code);
  }
}

/***************************************************************************
 * [exported] SetupADMConnection
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 17:38:24 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
SetupADMConnection (cellName, hostId, portId, serviceId, lifetime)
     char               *cellName;
     unsigned long       hostId;
     unsigned short      portId;
     unsigned short      serviceId;
     unsigned long       lifetime;
{
  register long       code;
  struct ktc_token   *token;
  struct admlib_rpc_connDesc ConnDesc;
  struct admlib_auth_tokenDesc TokenDesc;

  printf ("Getting adm connection.\n");
  ConnDesc.peer.host = hostId;
  ConnDesc.peer.port = portId;
  ConnDesc.peer.service = serviceId;
  ConnDesc.authenticated = TRUE;
  ConnDesc.level = rxkad_crypt;
  bcopy (&my_key, &(TokenDesc.key), sizeof (struct ktc_encryptionKey));
  strcpy (TokenDesc.clientPrincipal.name, EMTD_WORK_NAME);
  strcpy (TokenDesc.clientPrincipal.instance, EMTD_WORK_INSTANCE);
  strcpy (TokenDesc.clientPrincipal.cell, "");
  strcpy (TokenDesc.serverPrincipal.name, "adm");
  strcpy (TokenDesc.serverPrincipal.instance, "admin");
  strcpy (TokenDesc.serverPrincipal.cell, "");
  TokenDesc.lifeTime = lifetime;
  TokenDesc.maint = FALSE;
  if (code = admlib_auth_NewToken (&TokenDesc, &(ConnDesc.token)))
    return code;
  bcopy (&(ConnDesc.token), &admtoken, sizeof (struct ktc_token));
  IF_RTN (admlib_rpc_NewConnection (&ConnDesc, &admconn));
}

/***************************************************************************
 * [exported] CheckAuthentication
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Jul  8 08:49:43 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
CheckAuthentication ()
{
  struct timeval      tp;

  gettimeofday (&tp, NULL);
  if (tp.tv_sec + REAUTH_MIN_TIME > my_tokenp->endTime) {
    printf ("Trying to reauth.\n");
    emt_GetUserTickets (EMTD_WORK_NAME, EMTD_WORK_INSTANCE, THISCELL,
			&my_key, LIFETIME, my_tokenp, &my_viceID);
  }
  gettimeofday (&tp, NULL);
  if (tp.tv_sec + REAUTH_MIN_TIME > admtoken.endTime) {
    ReauthADM ();
  }
}

/***************************************************************************
 * [exported] StartADMConnection
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 17:30:19 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
StartADMConnection (server, port)
     char               *server;
     char               *port;
{
  register long       code;

  if (server)
    admlib_conf_GetHostIdByHostName (server, &admHostId);
  else
    admlib_conf_GetHostIdByHostName (EMTD_DEFAULT_ADM_SERVER, &admHostId);
  if (port)
    admlib_conf_GetPortIdByPortName (port, &admPortId);
  else
    admlib_conf_GetPortIdByPortName (ADMLIB_CONF_PORTNAME, &admPortId);
  admServiceId = ADMLIB_CONF_SERVICEID;
  IF_RTN (SetupADMConnection
	  ( /* cellName */ (char *) 0, admHostId, admPortId, admServiceId,
	   (long) (25 * 60 * 60)));
  adm_connection_valid = 1;
}


/***************************************************************************
 * [exported] ReauthADM
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 17:28:01 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
ReauthADM ()
{
  register long       code;

  if (adm_connection_valid) {
    if (code = admlib_rpc_DestroyConnection (admconn)) {
      adm_connection_valid = 0;
      fprintf (stderr, "emtd: error closing adm connection.\n");
    }
    if (code = SetupADMConnection
	( /* cellName */ (char *) 0, admHostId, admPortId, admServiceId,
	 (long) (25 * 60 * 60))) {
      fprintf (stderr, "hook: error connecting with adm \(%ld\).\n", code);
      exit (code);
    }
    adm_connection_valid = 1;
  }
}

/***************************************************************************
 * [exported] GetVolumeInfo
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 17:53:21 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
GetVolumeInfo (volume, server, partition, id, sites)
     char               *volume,
                       **server,
                       **partition;
     int                *id,
                        *sites;
{
  int                 i,
                      pair_err;
  register long       code;
  admint_objectPtr_t  obj,
                      subobj,
                      value = ADMLIB_OBJ_EMPTY;
  long                admcode;
  char               *admerr = NULL;

  IF_RTN (admlib_obj_ConvertToObj
	  (&obj, admlib_obj_FromList,
	   admlib_obj_FromIdentifier, "emt-get-entry",
	   admlib_obj_FromString, volume,
	   ADMLIB_OBJ_END));
  hook_LogObj (obj);
  if (code = ADM_EVAL (obj, &value, &admcode, &admerr))
    return code;
  if (admcode) {
    printf ("ADM_EVAL: %d %s.\n", admcode, admerr);
    hook_LogObj (value);
    return admcode;
  }
  IF_RTN (admlib_obj_ConvertFromObj
	  (value, admlib_obj_ToList,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToNumber, sites,
	   admlib_obj_ToListPartial,	/* BEGIN SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBSUBLIST */
	   admlib_obj_ToString, server,
	   admlib_obj_ToString, partition,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBSUBLIST */
	   ADMLIB_OBJ_END,
	   admlib_obj_ToIgnore,	/* END SUBLIST */
	   admlib_obj_ToListPartial,	/* BEGIN SUBLIST */
	   admlib_obj_ToNumber, id,
	   ADMLIB_OBJ_END,
	   admlib_obj_ToIgnore,	/* END SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END));
  return OK;
}

/***************************************************************************
 * [exported] GetVolumeSites
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 18:25:56 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
GetVolumeSites (volname, info)
     char               *volname;
     struct volinf      *info;
{
  int                 i,
                      pair_err;
  int                 rwid,
                      roid,
                      bkid;
  register long       code;
  admint_objectPtr_t  obj,
                      subobj,
                      value = ADMLIB_OBJ_EMPTY;
  long                admcode;
  char               *admerr = NULL;

  IF_RTN (admlib_obj_ConvertToObj
	  (&obj, admlib_obj_FromList,
	   admlib_obj_FromIdentifier, "emt-get-entry",
	   admlib_obj_FromString, volname,
	   ADMLIB_OBJ_END));
  hook_LogObj (obj);
  if (code = ADM_EVAL (obj, &value, &admcode, &admerr))
    return code;
  if (admcode) {
    printf ("ADM_EVAL: %d %s.\n", admcode, admerr);
    hook_LogObj (value);
    return admcode;
  }
  IF_RTN (admlib_obj_ConvertFromObj
	  (value, admlib_obj_ToList,
	   admlib_obj_ToString, &(info->name),
	   admlib_obj_ToIgnore,
	   admlib_obj_ToNumber, &(info->nsites),
	   admlib_obj_ToObj, &subobj,
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToNumber, &(info->rwid),
	   admlib_obj_ToNumber, &(info->roid),
	   admlib_obj_ToNumber, &(info->bkid),
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END));

  if (NULL == (info->site = (struct siteinf *)
	       malloc (sizeof (struct siteinf) * info->nsites)))
                        return -1;
  i = 0;
  while (admlib_obj_Type (subobj) != ADMINT_TYPE_EMPTY) {
    IF_RTN (admlib_obj_ConvertFromObj
	    (subobj, admlib_obj_ToPair,
	     admlib_obj_ToList,	/* BEGIN SUBLIST */
	     admlib_obj_ToString, &(info->site[i].server),
	     admlib_obj_ToString, &(info->site[i].partition),
	     admlib_obj_ToNumber, &(info->site[i].type),
	     ADMLIB_OBJ_END,	/* END SUBLIST */
	     admlib_obj_ToObj, &subobj));
    printf ("subobj: %s %s\n", info->site[i].server,
	    info->site[i].partition);
    i++;
  }
  return OK;
}

/***************************************************************************
 * [exported] GetMoreVolumeInfo
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 18:03:06 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
GetMoreVolumeInfo (server, partition, volid, used, quota, name)
     char               *server,
                        *partition;
     long                volid;
     int                *used,
                        *quota;
     char               **name;
{
  int                 i,
                      pair_err;
  register long       code;
  admint_objectPtr_t  obj,
                      value = ADMLIB_OBJ_EMPTY;
  long                admcode;
  char               *admerr = NULL;

  IF_RTN (admlib_obj_ConvertToObj
	  (&obj, admlib_obj_FromList,
	   admlib_obj_FromIdentifier, "emt-list-one-volume",
	   admlib_obj_FromString, server,
	   admlib_obj_FromString, partition,
	   admlib_obj_FromNumber, volid,
	   ADMLIB_OBJ_END));
  hook_LogObj (obj);
  if (code = ADM_EVAL (obj, &value, &admcode, &admerr))
    return code;
  if (admcode) {
    printf ("ADM_EVAL: %d %s.\n", admcode, admerr);
    hook_LogObj (value);
    return admcode;
  }
  IF_RTN (admlib_obj_ConvertFromObj
	  (value, admlib_obj_ToList,
	   admlib_obj_ToString, name,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToNumber, quota,
	   admlib_obj_ToNumber, used,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   ADMLIB_OBJ_END));
  return OK;
}

/***************************************************************************
 * [exported] FreeSiteInfo
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Jul  8 08:25:10 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
FreeSiteInfo (info)
     struct volinf      *info;
{
  register int        i;

  for (i = 0; i < info->nsites; i++) {
    free (info->site[i].server);
    free (info->site[i].partition);
  }
  free (info->site);
}

/***************************************************************************
 * [exported] VolumeIsReplicated
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Jul  8 08:27:55 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
VolumeIsReplicated (volname, reps)
     char               *volname;
long *reps;
{
  char               *server,
                     *partition;
  int                 id,
                      sites;
  long code;

  if (OK != (code = GetVolumeInfo (volname, &server, &partition, &id, &sites))) {
      *reps = -1;
      return code;
  }
  *reps = sites;
  return OK;
}

/***************************************************************************
 * [exported] VolumeRemoveSites
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 19:03:09 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
VolumeRemoveSites (volname, sysname, treename)
     char               *volname;
     char               *sysname;
     char               *treename;
{
  struct volinf       info;
  int                 i;
  char                path[MAXPATHLEN];

  CheckAuthentication ();
  IF_RTN (GetVolumeSites (volname, &info));

  if (NULL == zapfile_path) {
    sprintf (path, "%s/zapfile", EMTD_WORKING_DIRECTORY);
    zapfile_path = copy_string (path);
  }
  printf ("name:%s\n# sites: %d\nrwid: %ld\nroid: %ld\nbkid: %ld\n",
	  info.name, info.nsites, info.rwid, info.roid, info.bkid);
  for (i = 0; i < info.nsites; i++) {
    printf ("%s %s %d\n", SERVER, PART, TYPE);
    switch (TYPE) {
    case ITSROVOL:
      printf ("Removing: %s %s %ld\n", SERVER, PART, info.rwid);
      RemoveSingleSite (SERVER, PART, info.rwid, info.roid);
      break;
    case ITSRWVOL:
      VolumeMove (volname, SERVER, PART, info.rwid, sysname, treename);
      break;
    }
  }
}

/***************************************************************************
 * [exported] VolumeZapSites
 *
 * Description:
 *      Zaps the RO sites that were removed more than an hour ago...
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Jul  8 09:51:53 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
VolumeZapSites ()
{
  FILE               *zapinfd,
                     *zapoutfd;
  char                path[MAXPATHLEN],
                      buffer[MAXBUFSIZE];
  struct timeval      tp;
  int                 i;
  char               *server,
                     *partition,
                     *name;
  long                code,
                      remtime,
                      volid,
                      quota,
                      used,
                      total_quota = 0;

  if (NULL == zapfile_path) {
    sprintf (path, "%s/zapfile", EMTD_WORKING_DIRECTORY);
    zapfile_path = copy_string (path);
  }
  if (NULL == (zapinfd = fopen (zapfile_path, "r"))) {
    fprintf (stderr, "hook: error opening %s (%ld).\n", zapfile_path, errno);
    return 0;
  }
  sprintf (path, "%s.NEW", zapfile_path);
  if (NULL == (zapoutfd = fopen (path, "w"))) {
    fprintf (stderr, "hook: error opening %s (%ld).\n", path, errno);
    exit (errno);
  }
  while (fgets (buffer, MAXBUFSIZE, zapinfd) != NULL) {
    gettimeofday (&tp, NULL);
    i = 0;
    remtime = next_long (&i, buffer);
    if ((tp.tv_sec - remtime) > ZAPTIME) {
      server = next_string (&i, buffer);
      partition = next_string (&i, buffer);
      volid = next_long (&i, buffer);
      name = NULL;
      code = GetMoreVolumeInfo (server, partition, volid, &used, &quota,&name);
      if (NULL == code) {
	  fprintf(stderr, "hooks: Zapping %s\n", name);
	  ZapSingleSite (server, partition, volid);
	  total_quota += used;
	  fprintf(stderr, "hooks: reclaimed %ldk bytes of disk space.\n",used);
      }
      free (server);
      free (partition);
    } else {
      fputs (buffer, zapoutfd);
      fflush (zapoutfd);
    }
  }
  fclose (zapinfd);
  fclose (zapoutfd);
  rename (path, zapfile_path);
  fprintf(stderr,"hooks: *** reclaimed a total of %ldk bytes of disk space.\n",
	  total_quota);
}


/***************************************************************************
 * [exported] RemoveSingleSite
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 19:25:41 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
RemoveSingleSite (server, partition, rwid, roid)
     char               *server;
     char               *partition;
     long                rwid,
                         roid;
{
  admint_objectPtr_t  obj,
                      value = ADMLIB_OBJ_EMPTY;
  register long       code;
  long                admcode;
  char               *admerr = NULL;
  FILE               *zapfd;
  struct timeval      tp;

  if (code = admlib_obj_ConvertToObj
      (&obj, admlib_obj_FromList,
       admlib_obj_FromIdentifier, "emt-remove-site",
       admlib_obj_FromString, server,
       admlib_obj_FromString, partition,
       admlib_obj_FromNumber, rwid,
       ADMLIB_OBJ_END))
    return code;
  hook_LogObj (obj);
  if (code = ADM_EVAL (obj, &value, &admcode, &admerr))
    return code;
  if (admcode) {
    printf ("ADM_EVAL: %d %s.\n", admcode, admerr);
    hook_LogObj (value);
    printf ("admerr: %s\n", admerr);
    return admcode;
  }
  if (NULL == (zapfd = fopen (zapfile_path, "a"))) {
    fprintf (stderr, "hook: error opening %s (%ld).\n", zapfile_path, errno);
    exit (errno);
  }
  gettimeofday (&tp, NULL);
  fprintf (zapfd, "%ld,%s,%s,%ld\n", tp.tv_sec, server, partition, roid);
  fclose (zapfd);
}

/***************************************************************************
 * [exported] SetZapfilePath
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Jul  8 09:48:54 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
SetZapfilePath (path)
     char               *path;
{
  zapfile_path = path;
}

/***************************************************************************
 * [exported] ZapSingleSite
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 19:39:50 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
ZapSingleSite (server, partition, volid)
     char               *server;
     char               *partition;
     long                volid;
{
  admint_objectPtr_t  obj,
                      value = ADMLIB_OBJ_EMPTY;
  register long       code;
  long                admcode;
  char               *admerr = NULL;
  
  if (code = admlib_obj_ConvertToObj
      (&obj, admlib_obj_FromList,
       admlib_obj_FromIdentifier, "emt-volume-zap",
       admlib_obj_FromString, server,
       admlib_obj_FromString, partition,
       admlib_obj_FromNumber, volid,
       ADMLIB_OBJ_END))
    return code;
  hook_LogObj (obj);
  if (code = ADM_EVAL (obj, &value, &admcode, &admerr))
    return code;
  if (admcode) {
    printf ("ADM_EVAL: %d %s.\n", admcode, admerr);
    hook_LogObj (value);
    printf ("admerr: %s\n", admerr);
    return admcode;
  }
}

/***************************************************************************
 * [exported] VolumeMove
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Jul  7 19:21:02 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
VolumeMove (volname, from_server, from_partition, volid, sysname, treename)
     char               *volname;
     char               *from_server;
     char               *from_partition;
     long                volid;
     char               *sysname;
     char               *treename;
{
  admint_objectPtr_t  obj,
                      value = ADMLIB_OBJ_EMPTY;
  register long       code;
  long                admcode,
                      used,
                      quota;
  char               *admerr = NULL,
                      buffer[MAXPATHLEN];
  char               *to_server,
                     *to_partition,
                     *name;
  FILE               *pfile;
  int                 i,
                      moved = 0;

  name = NULL;
  IF_RTN(GetMoreVolumeInfo(from_server, from_partition, volid, &used,
			   &quota, &name));

  sprintf (buffer, "%s -a unreplicate -q %ld -s %s -t %s", EMTD_SITE_PATH,
	   quota, sysname, treename);
  puts (buffer);
  if (NULL != (pfile = popen (buffer, "r"))) {
    while (fgets (buffer, MAXBUFSIZE, pfile) != NULL) {
      printf ("hook: %s", buffer);
      i = 0;
      to_server = next_string (&i, buffer);
      to_partition = next_string (&i, buffer);
      printf ("server: %s, partition: %s", to_server, to_partition);
      if (!moved) {
	code = admlib_obj_ConvertToObj
	  (&obj, admlib_obj_FromList,
	   admlib_obj_FromIdentifier, "emt-move-volume",
	   admlib_obj_FromNumber, volid,
	   admlib_obj_FromString, from_server,
	   admlib_obj_FromString, from_partition,
	   admlib_obj_FromString, to_server,
	   admlib_obj_FromString, to_partition,
	   ADMLIB_OBJ_END);
	free (to_server);
	free (to_partition);
	if (code)
	  return code;
	hook_LogObj (obj);
	if (code = ADM_EVAL (obj, &value, &admcode, &admerr))
	  return code;
	if (admcode) {
	    printf ("ADM_EVAL: %d %s.\n", admcode, admerr);
	    hook_LogObj (value);
	    return admcode;
	}
	moved++;
      }
    }
    pclose (pfile);
  }
  return 0;
}

/***************************************************************************
 * [exported] GetConfiguration
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Wed Jul  8 08:59:58 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long 
GetConfiguration (path, config)
     char               *path;
     tree_t             *config;
{
  emtd_GetConfig(path, config);
}
