#ifndef _BLURB_
#define _BLURB_
/*

            Coda: an Experimental Distributed File System
                             Release 3.1

          Copyright (c) 1987-1995 Carnegie Mellon University
                         All Rights Reserved

Permission  to  use, copy, modify and distribute this software and its
documentation is hereby granted,  provided  that  both  the  copyright
notice  and  this  permission  notice  appear  in  all  copies  of the
software, derivative works or  modified  versions,  and  any  portions
thereof, and that both notices appear in supporting documentation, and
that credit is given to Carnegie Mellon University  in  all  documents
and publicity pertaining to direct or indirect use of this code or its
derivatives.

CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
ANY DERIVATIVE WORK.

Carnegie  Mellon  encourages  users  of  this  software  to return any
improvements or extensions that  they  make,  and  to  grant  Carnegie
Mellon the rights to redistribute these changes without encumbrance.
*/

static char *rcsid = "$Header: advice_srv.c,v 3.3 95/10/09 19:24:14 mre Locked $";
#endif /*_BLURB_*/





/*  
 *  Clean up activities:
 *      1) Create a single file system error routine (see CreateDataDirectory).
 *      2) Create an advice.err file to log known errors for entry into the database
 *
 */


#ifdef __cplusplus
extern "C" {
#endif __cplusplus

#include <sys/table.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <assert.h> 
#include <struct.h>
#include <sysent.h>
#include <sys/wait.h> 
#include <sys/stat.h>
#include <sys/dir.h>
#include <pwd.h>
#include <errno.h>
#include <strings.h>
#include <libc.h>
#include <lwp.h>
#include <rpc2.h>

extern void path(char *, char *, char *);
extern int table(int id, int index, char *addr, int nel, int lel);
extern int ffilecopy(FILE*, FILE*);
extern char **environ;

//extern int unlink(char *path);
#ifdef __cplusplus
}
#endif __cplusplus

#include <util.h>
#include <bstree.h>
#include "admon.h"
#include "adsrv.h"
#include <advice.h>
#include "advice_srv.h"
#include "miss.h"

char *discomiss = "/usr/coda/etc/discomiss";
char *reconnection = "/usr/coda/etc/reconnection";

int ASRinProgress = 0;
int ReconnectionQuestionnaireInProgress = 0;

int CHILDpid = 0;
int CHILDresult = -1;

int NumASRStarted = 0;
int NumRecoSurveys = 0;
int NumDiscoMissSurveys = 0;

/***** Function Definitions *****/
void InitEnvironment();
void InitHostName();
void InitUserData();
void InitCounters();
void IncrementCounter();
void InitLogFile();
void InitEventFile();
void InitDataDirectory();
void PrintMissList(char *filename);
void InitMissQueue();
void ErrorReport(char *);
void PrintUsage();
void CreateREADMEFile(char *);
void InitializeDirectory(char *, char *);
void SetAccessLists(char *, char *);
void CommandLineArgs(int, char **);
void InitiateNewUser();
RPC2_Handle connect_to_machine(char *_name);
void Init_RPC();
void InformVenusOfOurExistance(char *);
int GetAdvice(char *);
void Shutdown(int);
void Child(int);
void UserDrivenEvent(int);
void DataMover();
void UserEventHandler(char *);
void ShutdownHandler(char *);
void EndASREventHandler(char *);
int execute_tcl(char *script, char *args[]);
//int TestDiscoMiss();

/* Command Line Variables */
int debug = 0;
int verbose = 0;
int AutoReply = 1;               /* -a will set this value */ /* Leave set until PCM works */
int LogLevel = 0;                /* -d */

/* RPC Variables */
extern RPC2_PortalIdent rpc2_LocalPortal;
char ShortHostName[MAXHOSTNAMELEN];
char HostName[MAXHOSTNAMELEN];
RPC2_Handle VenusCID = -1;

typedef struct {
int presentedCount;
int requestedCount;
} InternalCounter;

/* Internal Counters */
InternalCounter PCMcount;
InternalCounter HWAcount;
InternalCounter DMcount;
InternalCounter Rcount;
InternalCounter RPcount;
InternalCounter IASRcount;
InternalCounter WCMcount;
InternalCounter LCcount;

/* Internal Variables */
extern int errno;
char error_msg[1024];

char UserName[16];
int uid = -1;
int thisPID;

char BaseDir[MAXPATHLEN];
char WorkingDir[MAXPATHLEN];
char tmpDir[MAXPATHLEN];

char tmpReconnectionFileName[MAXPATHLEN];
char ReconnectionFileName[MAXPATHLEN];

int WeLostTheConnection = FALSE;
int AwaitingUserResponse = FALSE;
int shutdownFlag = FALSE;
int userdrivenFlag = FALSE;

/* Venus Version Information */
int VenusMajorVersionNumber;
int VenusMinorVersionNumber;

FILE *LogFile;
char LogFileName[256];       /* DEFAULT: /coda/usr/<username>/.questionnaires/advice.log */

FILE *EventFile;
char EventFileName[256];     /* DEFAULT: /coda/usr/<username>/.questionnaires/advice.event */

/******************************************************************
 *************************  MAIN Routine  *************************
 ******************************************************************/

main(int argc, char *argv[])
{
  RPC2_Handle cid;
  RPC2_RequestFilter reqfilter;
  RPC2_PacketBuffer *reqbuffer;
  long rc;
  char c, s;
  int userdrivenpid, asrendpid, shutdownpid;

  // Initialization
  InitEnvironment();		    // Make sure the environment variables are set...
  InitHostName();                   // Get our HostName for later use...
  thisPID = getpid();               // Get the pid 
  InitUserData();                   // Get uid and UserName (must preceed CommandLineArgs)
  CommandLineArgs(argc, argv);      // Parse command line arguments
  InitDataDirectory();              // Create a unique directory in which to store data
  InitLogFile();                    // Open the LogFile
  InitEventFile();		    // Open the EventFile
  InitMissQueue();

  Init_RPC();                                 // Initialize RPC...
  assert(IOMGR_Initialize() == LWP_SUCCESS);  // and IOMGR


  /****************************************************
   ******   Create LWPs to handle signal events  ******
   ****************************************************/

  // SIGCHLD signals the completion of an ASR.
  assert((LWP_CreateProcess((PFIC)EndASREventHandler,
			   DFTSTACKSIZE*1024, LWP_NORMAL_PRIORITY,
			   (char *)&c, "ASR End Signal Handler",
			   (PROCESS *)&asrendpid)) == (LWP_SUCCESS));

  // SIGTERM signals the AdviceMonitor to shut down
  assert(LWP_CreateProcess((PFIC)ShutdownHandler,
			   DFTSTACKSIZE*1024, LWP_NORMAL_PRIORITY,
			   (char *)&s, "Shutdown Handler",
			   (PROCESS *)&shutdownpid) == LWP_SUCCESS);

  // SIGUSR1 signals user-driven interaction.
  assert((LWP_CreateProcess((PFIC)UserEventHandler,
			    DFTSTACKSIZE*1024, LWP_NORMAL_PRIORITY,
			   (char *)&c, "User-driven Event Handler",
			   (PROCESS *)&userdrivenpid)) == (LWP_SUCCESS));

  // Initialize signal handlers
  signal(SIGTERM, Shutdown);
  assert(!signal(SIGCHLD, Child));
  signal(SIGUSR1, UserDrivenEvent);

  // Inform Venus of the availability of an AdviceMonitor for this user
  InformVenusOfOurExistance(HostName);

  // Set up the request filter:  we only service ADMON subsystem requests.
  reqfilter.FromWhom = ANY;
  reqfilter.OldOrNew = OLDORNEW;
  reqfilter.ConnOrSubsys.SubsysId = ADMONSUBSYSID;

  // Loop forever, waiting for Venus to ask us for advice
  LogMsg(100,LogLevel,LogFile, "main:  Entering Request Loop");
  struct timeval Expiry;
  Expiry.tv_sec = 5;
  Expiry.tv_usec = 0;
  if (LogLevel==1000) RPC2_DebugLevel = 1000;
  for ( ; ; ) {
    if (WeLostTheConnection)
      exit(0);

    // Wait for a request
    rc = RPC2_GetRequest(&reqfilter, &cid, &reqbuffer, &Expiry, NULL, NULL, NULL) ;
    if (rc == RPC2_TIMEOUT) {
      DataMover();
      continue;
    }
    else if (rc != RPC2_SUCCESS) {
      LogMsg(0,LogLevel,LogFile, "main: ERROR ==> GetRequest (%s)", RPC2_ErrorMsg((int)rc));
      LogMsg(0,LogLevel,EventFile, "GetRequest: %s", RPC2_ErrorMsg((int)rc));
    }

    // Venus has asked us for advice -- do something about it!
    rc = AdMon_ExecuteRequest(cid, reqbuffer, NULL);
    if (rc != RPC2_SUCCESS) {
      LogMsg(0,LogLevel,LogFile, "main: ERROR ==> ExecuteRequest (%s)", RPC2_ErrorMsg((int)rc));
      LogMsg(0,LogLevel,EventFile, "ExecuteRequest: %s", RPC2_ErrorMsg((int)rc));
    }
  }
}


/**********************************************
 *************  Utility Routines  *************
 **********************************************/

void InitEnvironment() {
    int error = 0;

    error += setenv("TCL_LIBRARY", TCL, 1);
    error += setenv("TK_LIBRARY", TK, 1);

    assert(error == 0);
}

void InitHostName() {
  assert(gethostname(HostName, MAXHOSTNAMELEN) == 0);
  strcpy(ShortHostName, HostName);
  for (int i = 0; i < strlen(HostName); i++)
	  if (ShortHostName[i] == '.')
		  ShortHostName[i] = 0;
  sprintf(HostName, "localhost");
}

void InitUserData() {
  struct passwd *pwent;

  uid = getuid();

  /* Get the user's name */
  pwent = getpwuid(uid);
  strcpy(UserName, pwent->pw_name);

  sprintf(BaseDir, "/coda/usr/%s/.questionnaires", UserName);
}

void InitCounters() {
    PCMcount.presentedCount = 0;
    PCMcount.requestedCount = 0;
    HWAcount.presentedCount = 0;
    HWAcount.requestedCount = 0;
    DMcount.presentedCount = 0;
    DMcount.requestedCount = 0;
    Rcount.presentedCount = 0;
    Rcount.requestedCount = 0;
    RPcount.presentedCount = 0;
    RPcount.requestedCount = 0;
    IASRcount.presentedCount = 0;
    IASRcount.requestedCount = 0;
    WCMcount.presentedCount = 0;
    WCMcount.requestedCount = 0;
    LCcount.presentedCount = 0;
    LCcount.requestedCount = 0;
}

#define REQUESTED 0
#define PRESENTED 1
void IncrementCounter(InternalCounter *counter, int presented) {
    counter->requestedCount++;
    if (presented)
	    counter->presentedCount++;
}

void InitDataDirectory() {
  struct timeval curTime;
  char timeString[26];
  char dateNtime[15];
  int i;

  /* Generate dateNtime based on current date and time (mmmdd.hh:mm:ss) */
  (void) gettimeofday(&curTime, NULL);
  (void) strcpy(timeString, ctime(&curTime.tv_sec));
  for (i = 0; i < 3; i++) dateNtime[i] = timeString[i+4];
  for (i = 3; i < 14; i++) dateNtime[i] = timeString[i+5];
  dateNtime[14] = '\0';
  if (dateNtime[3] == ' ') dateNtime[3] = '0'; /* don't want blank */
  dateNtime[5] = '.';

  /* Create a unique Coda directory in which to dump our data */
  /* WorkingDir: /coda/usr/<username>/.questionnaires/<HOSTNAME>_<PID>_<DATE:TIME> */
  sprintf(WorkingDir, "%s/%s_%d_%s", BaseDir, ShortHostName, thisPID, dateNtime);
  InitializeDirectory(WorkingDir, UserName);

  // Create a temporary directory in which to deposit data before moving it into /coda
  sprintf(tmpDir, "/tmp/%s_%d_%s", ShortHostName, thisPID, dateNtime);
  InitializeDirectory(tmpDir, UserName);
}

void InitLogFile() {
    /* LogFileName: 
         /coda/usr/<username>/.questionnaires/<HOSTNAME>_<PID>_<DATE:TIME>/advice.log  */
    sprintf(LogFileName, "%s/advice.log", WorkingDir);

    LogFile = fopen(LogFileName, "a");
    if (LogFile == NULL) {
        fprintf(stderr, "LOGFILE (%s) initialization failed (errno=%d)\n\n", LogFileName, errno);
	fflush(stderr);
    }

    struct timeval now;
    gettimeofday(&now, 0);
    char *s = ctime(&now.tv_sec);
    LogMsg(0,LogLevel,LogFile,"LOGFILE initialized with LogLevel = %d at %s",
           LogLevel,ctime(&now.tv_sec));
    LogMsg(0,LogLevel,LogFile,"My pid is %d",getpid());
}

void InitEventFile() {
    /* EventFileName:
       /coda/usr/<username>/.questionnaires/<HOSTNAME>_<PID>_<DATE:TIME>/advice.event  */
    sprintf(EventFileName, "%s/advice.event", WorkingDir);

    EventFile = fopen(EventFileName, "a");
    if (LogFile == NULL) 
        LogMsg(0,LogLevel,LogFile, "EVENTFILE (%s) initialization failed (errno=%d)", EventFileName, errno);
}

void ErrorReport(char *message)
{
  LogMsg(0,LogLevel,LogFile, "%s", message);
  exit(1);
}

void PrintUsage() {
  fprintf(stderr, "advice_srv [-d <LogLevel>] [i] [-v]\n");
  fflush(stderr);
  exit(-1);
}

/*PRIVATE*/ 
void CommandLineArgs(int argc, char **argv) {
  extern int optind;
  int arg_counter = 1;

  while ((arg_counter = getopt(argc, argv, "ad:iv")) != EOF)
    switch(arg_counter) {
      case 'a':
	      // Undocumented feature 
	      // DEFAULT is ON (until PCM is functional)
              fprintf(stderr, "AutoReply turned on.\n");
              AutoReply = 1;
              break;
      case 'd':
              debug = 1;
	      LogLevel = atoi(argv[optind-1]);
              break;
      case 'i':
	      InitiateNewUser();
	      break;
      case 'v':
              verbose = 1;
              break;
      default:
              PrintUsage();
    }
}

void InitiateNewUser()
{
  /* Give them some sort of tutorial?? */
  fprintf(stderr, "Tutorial for new users doesn't exist!  Bug Maria...\n");

  /* Create a unique Coda directory in which to dump our data */
  /* dflt: /coda/usr/<username>/.questionnaires */
  InitializeDirectory(BaseDir, UserName);

  fprintf(stderr, "New user (%s) initiated.\n", UserName);
}


void CreateDataDirectory(char *dirname) {
  /* Create the directory */
  if (mkdir(dirname, 0755) != 0) {
      switch (errno) {
	  case EACCES:
	      fprintf(stderr, "advice_srv ERROR:  You do not have the necessary access rights to:\n");
	      fprintf(stderr, "\t\tmkdir %s'.\n\n", dirname);
	      fprintf(stderr, "\t==>  Please obtain tokens and retry.  <==\n");
	      fflush(stderr);
	      exit(-1);
	      break;
	  case ENOENT:
	      fprintf(stderr, "\n");
	      fprintf(stderr, "If this is the first time you have run the advice monitor,\n");
              fprintf(stderr, "then please do the following:\n");
              fprintf(stderr, "\t1) Make sure you are connected to the servers.\n");
              fprintf(stderr, "\t2) Rerun the advice monitor with the -i switch.\n");
              fprintf(stderr, "The -i switch gives you some instructions about how to use\n");
              fprintf(stderr, "the advice monitor and initializes a directory in which to\n");
              fprintf(stderr, "store the data collected by the advice monitor.\n\n");
              fprintf(stderr, "WARNING:  If you have previously run the advice monitor with\n");
              fprintf(stderr, "\tthe -i switch, do NOT follow these instructions.  If you \n");
              fprintf(stderr, "\tdo, you risk creating a conflict which will need to be\n");
              fprintf(stderr, "\trepaired manually.\n\n");
              fprintf(stderr, "Contact Maria Ebling (mre@cs) if you have further problems.\n\n");
              exit(-1);
	      break;
          case EEXIST:
	      fprintf(stderr, "\n");
	      fprintf(stderr, "The file or directory (%s) already exists.\n", dirname);
	      fprintf(stderr, "If you were running with the -i switch, you've probably \n");
	      fprintf(stderr, "already initializedthe necessary directory.  Try again \n");
	      fprintf(stderr, "without the -i switch.\n");
	      fprintf(stderr, "\n");
	      fprintf(stderr, "If you weren't running with the -i swtich, please contact\n");
	      fprintf(stderr, "Maria Ebling (mre@cs).\n");
	      exit(-1);
	      break;
      default:
	  fprintf(stderr, "advice_srv ERROR:  Unknown error (errno=%d) with 'mkdir %s'.\n", errno, dirname);
	  fflush(stdout);
	  exit(-1);
      }
  }
}


void SetAccessLists(char *dirname, char *username) {
  char commandName[MAXPATHLEN];

  /* Set the access lists correctly to give the user limited access */
  if (strcmp(username,"mre") != 0) {
      sprintf(commandName, "%s sa %s %s rliw", CFS, dirname, username);
      if (system(commandName) != 0) {
	  fprintf(stderr, "advice_srv WARNING:  Could not set access list appropriately.\n");
	  fprintf(stderr, "                     Command was %s\n\n", commandName);
	  fflush(stderr);
      }
  }

  /* Set the access lists correctly to give me full access */
  bzero(commandName, MAXPATHLEN);
  sprintf(commandName, "%s sa %s mre all", CFS, dirname);
  if (system(commandName) != 0) {
    fprintf(stderr, "advice_srv WARNING:  Could not set access list appropriately.\n");
    fprintf(stderr, "                     Command was %s\n\n", commandName);
    fflush(stderr);
  }

}


void CreateREADMEFile(char *dirname) {
  char readmePathname[MAXPATHLEN];
  FILE *README;

  /* Generate a README file in the directory */
  if (strncmp(dirname, "/coda", 5) == 0)
      sprintf(readmePathname, "%s/README", dirname);
  else
      sprintf(readmePathname, "%s/#README", dirname);

  README = fopen(readmePathname, "w+");
  if (README == NULL) {
    fprintf(stderr, "advice_srv ERROR:  Cannot create README file: %s\n", readmePathname);
    fprintf(stderr, "                   Errno = %d\n", errno);
    fflush(stderr);
    exit(-1);
  }

  fprintf(README, "This directory contains data for the advice monitor.\n");
  fprintf(README, "Do NOT modify the contents of any file in this directory!\n");
  fprintf(README, "This directory and the data it contains will automatically\n");
  fprintf(README, "be processed by a nightly daemon and then removed.\n\n");
  fprintf(README, "If you have any questions, please contact Maria Ebling <mre@cs>.\n");
  fflush(README);
  fclose(README);
  chmod(readmePathname, 00444 /* world readable */);
}

void InitializeDirectory(char *dirname, char *username) {
  CreateDataDirectory(dirname);

  if (strncmp(dirname, "/coda", 5) == 0)
      SetAccessLists(dirname, username);

  CreateREADMEFile(dirname);
}


#ifdef MACH
// This is a Mach-specific way to do this.  For non-Mach architectures, read on...
#define ARGSIZE 4096
char* getcommandname(int pid) {
	char arguments[ARGSIZE];
	int *ip; 
	register char	*cp;
	char		c;
	char		*end_argc;
	int rc;

	if ((rc = table(TBL_ARGUMENTS, pid, arguments, 1, ARGSIZE)) != 1) {
	    LogMsg(0,LogLevel,LogFile,"getcommandname: ERROR ==> table() call failed");
	    LogMsg(0,LogLevel,EventFile,"table: %d\n", rc);
	    return((char *)0);
	}

	end_argc = &arguments[ARGSIZE];

	ip = (int *)end_argc;
	/* words must be word aligned! */
	if ((unsigned)ip & 0x3)
		ip = (int*)((unsigned)ip & ~0x3);
#ifdef	mips
	/* one exception frame worth of zeroes too */
	ip -= 10; /* EA_SIZE bytes */
#endif	mips
	ip -= 2;		/* last arg word and .long 0 */
	while (*--ip)
	    if (ip == (int *)arguments)
		return((char *)0);

	*(char *)ip = ' ';
	ip++;
	for (cp = (char *)ip; cp < end_argc; cp++) {
	    c = *cp & 0177;
	    if (c == 0)
		break; 	
	    else if (c < ' ' || c > 0176) 
		*cp = '?';
	}
	*cp = 0;
	cp = (char *)ip;
	return(cp);
}

#else

/*
 * NON-Mach-specific way to get command names (sort of)
 *
 * This function assumes that the following command will identify the command
 * associated with pid 12845 and deposit that command in the file /tmp/command.
 * If this isn't true on the system you're working on, then the command names
 * which appear in various tcl scripts will be incorrect or non-existant.
 * ps ac 12845 | awk '{ if ($5 == "COMMAND") next; print $5 > "/tmp/command"}'
 */
char* getcommandname(int pid) {
    char tmpfile[36];
    char commandString[MAXPATHLEN];
    int rc;
    FILE *f;

    sprintf(tmpfile, "/tmp/advice_srv.%d", thisPID);
	
    sprintf(commandString, "ps axc %d | awk '{ if ($5 == \"COMMAND\") next; print $5 > \"%s\"}'", pid, tmpfile);

    rc = system(commandString);
    f = fopen(tmpfile, "r");
    fscanf(f, "%s", commandname);
    fclose(f);
    unlink(tmpfile);
    return(commandname);
}
#endif

char *GetCommandName(int pid) {
    char *commandname;
    static char CommandName[MAXPATHLEN];

    commandname = getcommandname(pid);
    if (commandname == NULL)
	sprintf(CommandName, "Unknown");
    else
	sprintf(CommandName, "%s", commandname);
    return(CommandName);
}

char *GetStringFromTimeDiff(long time_difference) {
    long seconds, minutes, hours, days;
    static char the_string[32];

    if (time_difference < 60) {
        sprintf(the_string, "%d second%s", time_difference, (time_difference>1)?"s":"");
        return(the_string);
    }

    minutes = time_difference / 60;  // Convert to minutes
    seconds = time_difference % 60;
    if (minutes < 60) {
	if (seconds > 0)
	    sprintf(the_string, "%d minute%s %d second%s", minutes, (minutes>1)?"s,":",", seconds, (seconds>1)?"s":"");
        else
            sprintf(the_string, "%d minute%s", minutes, (minutes>1)?"s":"");
        return(the_string);
    }
 
    hours = minutes / 60;  // Convert to hours
    minutes = minutes % 60;
    if (hours < 24) {
	if (minutes > 0)
	    sprintf(the_string, "%d hour%s, %d minute%s", hours, (hours>1)?"s":"", minutes, (minutes>1)?"s":"");
        else
	    sprintf(the_string, "%d hour%s", hours, (hours>1)?"s":"");
        return(the_string);
    }

    days = hours / 24;  // Convert to days
    hours = hours % 24;
    if (hours > 0)
	sprintf(the_string, "%d day%s, %d hour%s", days, (days>1)?"s":"", hours, (hours>1)?"s":"");
    else
	sprintf(the_string, "%d day%s", days, (days>1)?"s":"");
    return(the_string);
}

char *GetTimeFromLong(long the_time) {
  static char the_string[10];
  struct tm *lt = localtime((long *)&the_time);
  sprintf(the_string, "%02d:%02d:%02d", lt->tm_hour, lt->tm_min, lt->tm_sec);
  return(the_string);
}

char *GetDateFromLong(long the_time) {
  static char the_string[10];
  struct tm *lt = localtime((long *)&the_time);
  sprintf(the_string, "%02d/%02d/%02d", lt->tm_mon+1, lt->tm_mday, lt->tm_year);
  return(the_string);
}

char *TimeString(long the_time) {
  static char the_string[20];
  struct tm *lt = localtime((long *)&the_time);
  sprintf(the_string, "%02d/%02d/%02d %02d:%02d:%02d", lt->tm_mon+1, lt->tm_mday, lt->tm_year, lt->tm_hour, lt->tm_min, lt->tm_sec);
  return(the_string);
}

void PrintCounters() {
    LogMsg(1000,LogLevel,LogFile, "PCM=(%d/%d); DM=(%d/%d); R=(%d/%d); IASR=(%d/%d); HWA=(%d/%d); RP=(%d/%d); WCM=(%d/%d); LC=(%d/%d)\n", PCMcount.presentedCount, PCMcount.requestedCount, DMcount.presentedCount, DMcount.requestedCount, Rcount.presentedCount, Rcount.requestedCount, IASRcount.presentedCount, IASRcount.requestedCount, HWAcount.presentedCount, HWAcount.requestedCount, RPcount.presentedCount, RPcount.requestedCount, WCMcount.presentedCount, WCMcount.requestedCount, LCcount.presentedCount, LCcount.requestedCount);
}


/******************************************************
 ***********  RPC2 Initialization Routines  ***********
 ******************************************************/

/* Establish a connection to the server running on machine machine_name */
RPC2_Handle connect_to_machine(char *machine_name)
{
  RPC2_Handle cid;
  RPC2_HostIdent hid;
  RPC2_PortalIdent pid;
  RPC2_SubsysIdent sid;
  long rc;
  RPC2_BindParms bp;

  hid.Tag = RPC2_HOSTBYNAME;
  if (strlen(machine_name) >= 64) { /* Not MAXHOSTNAMELEN because rpc2.h uses "64"! */
      sprintf(error_msg, "Machine name %s too long!", machine_name);
      ErrorReport(error_msg);
    } ;
  strcpy(hid.Value.Name, machine_name);
  pid.Tag = RPC2_PORTALBYINETNUMBER;
  pid.Value.InetPortNumber = htons(ADSRVPORTAL);
  sid.Tag = RPC2_SUBSYSBYID;
  sid.Value.SubsysId = ADSRVSUBSYSID; 

  bp.SecurityLevel = RPC2_OPENKIMONO;
  bp.EncryptionType = NULL;
  bp.SideEffectType = NULL;
  bp.ClientIdent = NULL;
  bp.SharedSecret = NULL;
  rc = RPC2_NewBinding(&hid, &pid, &sid, &bp, &cid);
  if (rc != RPC2_SUCCESS) {
      sprintf(error_msg, "%s\nCan't connect to machine %s", RPC2_ErrorMsg((int)rc),
              machine_name);
      ErrorReport(error_msg);
    };
  return(cid);
} ;

void Init_RPC()
{
  PROCESS mylpid;
  RPC2_PortalIdent *portallist[1], portal1;
  RPC2_SubsysIdent sid;
  long rc ;

  if (LWP_Init(LWP_VERSION, LWP_NORMAL_PRIORITY, &mylpid) != LWP_SUCCESS) {
    sprintf(error_msg, "Can't Initialize LWP");   /* Initialize LWP package */
    ErrorReport(error_msg);
  }

  /* Initialize RPC2 Package */
  portallist[0] = &portal1; 

  portal1.Tag = RPC2_PORTALBYINETNUMBER;
  portal1.Value.InetPortNumber = NULL;

  rc = RPC2_Init(RPC2_VERSION, NULL, portallist, 1, -1, NULL) ;
  if (rc != RPC2_SUCCESS) {
    sprintf(error_msg, "%s:  Can't Initialize RPC2", RPC2_ErrorMsg((int)rc));
    ErrorReport(error_msg);
  }

  /* Export Venus subsystem */
  sid.Tag = RPC2_SUBSYSBYID;
  sid.Value.SubsysId = ADMONSUBSYSID;
  rc = RPC2_Export(&sid) != RPC2_SUCCESS ;
  if (rc != RPC2_SUCCESS) {
    sprintf(error_msg, "%s\nCan't export the advice subsystem", RPC2_ErrorMsg((int)rc));
    ErrorReport(error_msg);
  }
  //  VenusService_EnqueueRequest = 1; 
}


void InformVenusOfOurExistance(char *hostname) {
  long rc;
  int pgrp;
  RPC2_Integer Major;
  RPC2_Integer Minor;

  pgrp = getpgrp(getpid());

  LogMsg(1000,LogLevel,LogFile,"InformVenusOfOurExistance: Binding to venus");
  VenusCID = connect_to_machine(hostname);

  LogMsg(1000,LogLevel,LogFile,
	"InformVenusOfOurExistance: NewAdviceService(%s, %d, %d, %d, %d, %d)...", 
	hostname, uid, rpc2_LocalPortal.Value.InetPortNumber, pgrp, 
	ADSRV_VERSION, ADMON_VERSION);

  rc = NewAdviceService(VenusCID, (RPC2_String)hostname, (RPC2_Integer)uid, (RPC2_Integer)rpc2_LocalPortal.Value.InetPortNumber, (RPC2_Integer)pgrp, (RPC2_Integer)ADSRV_VERSION, (RPC2_Integer)ADMON_VERSION, &Major, &Minor);

  VenusMajorVersionNumber = (int)Major;
  VenusMinorVersionNumber = (int)Minor;

  if (rc != RPC2_SUCCESS) {
      fprintf(stderr,"InformVenusOfOurExistance:  NewAdviceService call failed\n");
      fprintf(stderr,"\tCheck the venus.log file for the error condition.\n");
      fprintf(stderr,"\tIt is probably version skew between venus and advice_srv.\n");
      LogMsg(0,LogLevel,LogFile, "InformVenusOfOurExistance: ERROR ==> NewAdviceService call failed (%s)", RPC2_ErrorMsg((int)rc));
      LogMsg(0,LogLevel,LogFile, "\t Check the venus.log file for the error condition.");
      LogMsg(0,LogLevel,LogFile, "\t It is probably version skew between venus and advice_srv.");
      LogMsg(0,LogLevel,EventFile, "NewAdviceService: VersionSkew or UserNotExist");
      exit(-1);
  }
  else {
    LogMsg(0,LogLevel,LogFile,"Version information:");
    LogMsg(0,LogLevel,LogFile,"\tAdvice Monitor Version = %d",ADVICE_MONITOR_VERSION);
    LogMsg(0,LogLevel,LogFile,"\tVenus Version = %d.%d", VenusMajorVersionNumber, VenusMinorVersionNumber);
    LogMsg(0,LogLevel,LogFile,"\tADSRV Version = %d",ADSRV_VERSION);
    LogMsg(0,LogLevel,LogFile,"\tADMON Version = %d\n",ADMON_VERSION);
  }
}

SolicitAdviceOnNextHoardWalk() {
    long rc;

    LogMsg(0,LogLevel,LogFile, "E SolicitAdviceOnNextHoardWalk");

    rc = SolicitHoardWalkAdvice(VenusCID, (RPC2_Integer)uid);

    if (rc != RPC2_SUCCESS) {
	    LogMsg(0,LogLevel,LogFile, "SolicitHoardWalkAdvice call failed\n");
    }
    LogMsg(0,LogLevel,LogFile, "L SolicitAdviceOnNextHoardWalk");
}


UnsolicitAdviceOnNextHoardWalk() {
    long rc;

    LogMsg(0,LogLevel,LogFile, "E UnsolicitAdviceOnNextHoardWalk");

    rc = UnsolicitHoardWalkAdvice(VenusCID, (RPC2_Integer)uid);

    if (rc != RPC2_SUCCESS) {
	    LogMsg(0,LogLevel,LogFile, "UnsolicitHoardWalkAdvice call failed\n");
    }
    LogMsg(0,LogLevel,LogFile, "L UnsolicitAdviceOnNextHoardWalk");
}


/*****************************************************
 *******  Helper Routine for User Interaction  *******
 *****************************************************/

int GetAdvice(char *request) 
{
  long rc;
  int rdfds = 0;
  int NFDS = 32;
  struct timeval Expiry;
  char buf[128];
  int op;

  Expiry.tv_sec = 60;
  Expiry.tv_usec = 0;

  rdfds = (1 << 0);

  printf("Please input advice regarding %s:  ", request); 
  fflush(stdout);
  rc = IOMGR_Select(NFDS, &rdfds, 0, 0, 0);
  switch (rc) {
    case 0:
	  LogMsg(100,LogLevel,LogFile,"GetAdvice: Timeout");
          break;
    case -1:
	  sprintf(error_msg, "IOMGR_Select errored.\n%d\n", RPC2_ErrorMsg((int)rc));
	  ErrorReport(error_msg);
    case 1:
	  rc = read(0, buf, 128);
	  LogMsg(100,LogLevel,LogFile, "GetAdvice: buf = *%s*", buf);
	  if (strncmp(buf, "fetch", 3) == 0)
	    op = PseudoFetch;
	  else if (strncmp(buf, "timeout", 7) == 0)
	    op = PseudoTimeout;
	  else if (strncmp(buf, "hoard", 5) == 0)
	    op = PseudoHOARDimmedFETCH;
	  else
	    op = PseudoUnknown;
	  return(op);
    default:
	  sprintf(error_msg, "IOMGR_Select returned too many fds, %d\n",rc);
	  ErrorReport(error_msg);
	  return(PseudoUnknown);
  }

  return(PseudoUnknown);
}


/***********************************************************************
 ***************** Routines to help Spawn Subprocesses *****************
 ***********************************************************************/

int execute_tcl(char *script, char *args[]) {
  LogMsg(100,LogLevel,LogFile, "E execute_tcl(%s)", script);

  int rc = fork();
  if (rc == -1) {
    LogMsg(0,LogLevel,LogFile, "execute_tcl: ERROR ==> during fork (rc = %d)", rc);
    LogMsg(0,LogLevel,EventFile, "execute_tcl: ERROR ==> during fork (rc = %d)", rc);
    return(-1);
  }
  else if (rc) {                          
    // parent process 
      CHILDpid = rc;
      LogMsg(1000,LogLevel,LogFile, "execute_tcl (Parent): CHILDpid = %d", CHILDpid);
      LWP_WaitProcess((char *)&CHILDpid);
  }
  else {                                  
    // child process 
      if (execve(script, (const char **) args, (const char **) environ)) {
	LogMsg(0,LogLevel,LogFile, "execute_tcl (Child): ERROR ==> during execl");
	LogMsg(0,LogLevel,EventFile, "execute_tcl (Child): ERROR ==> during execl");
	return(-1);
      }
  }

  LogMsg(100,LogLevel,LogFile,"L execute_tcl()");
  return(0);
}

int fork_tcl(char *script, char *args[]) {
  LogMsg(100,LogLevel,LogFile, "E fork_tcl(%s)", script);

  int rc = fork();
  if (rc == -1) {
    LogMsg(0,LogLevel,LogFile, "fork_tcl: ERROR ==> during fork (rc = %d)", rc);
    LogMsg(0,LogLevel,EventFile, "fork_tcl: ERROR ==> during fork (rc = %d)", rc);
    return(-1);
  }
  else if (rc) {                          
    // parent process 
//      CHILDpid = rc;
      LogMsg(1000,LogLevel,LogFile, "fork_tcl (Parent): CHILDpid = %d", CHILDpid);
//      LWP_WaitProcess((char *)&CHILDpid);
  }
  else {                                  
    // child process 
      if (execve(script, (const char **) args, (const char **) environ)) {
	LogMsg(0,LogLevel,LogFile, "fork_tcl (Child): ERROR ==> during execl");
	LogMsg(0,LogLevel,EventFile, "fork_tcl (Child): ERROR ==> during execl");
	return(-1);
      }
  }

  LogMsg(100,LogLevel,LogFile,"L fork_tcl()");
  return(0);
}

/*************************************************************
 ******************  Incoming RPC Handlers  ******************
 *************************************************************/

long PseudoConnectedMiss(RPC2_Handle _cid, RPC2_String pathname, RPC2_Integer pid, RPC2_Integer *advice) 
{
  int rdfds = 0;
  int NFDS = 32;
  char message[1024];

  AwaitingUserResponse = TRUE;
  assert(pathname != NULL);
  sprintf(message, "PseudoConnectedMiss on %s by pid=%d", pathname, pid);

  IncrementCounter(&PCMcount, REQUESTED);

  if (AutoReply) {
    printf("%s ==> automatic Fetch in progress.\n", message);
    *advice = PseudoFetch;
  }
  else {
    *advice = GetAdvice(message);
    LogMsg(100,LogLevel,LogFile,"PseudoConnectedMiss: %s");
  }

  AwaitingUserResponse = FALSE;
  PrintCounters();
  return(RPC2_SUCCESS);
}


void InitDiscoFile(char *FileName, int venusmajor, int venusminor, int advice, int adsrv, int admon, DisconnectedMissQuestionnaire *questionnaire)
{
    FILE *DiscoFile;
    struct stat buf;

    // Ensure it does not exist
    stat(FileName, &buf);
    assert(errno == ENOENT);

    // Create and initialize it
    DiscoFile = fopen(FileName, "w+");
    assert(DiscoFile != NULL);

    fprintf(DiscoFile, "Disconnected Cache Miss Questionnaire\n");
    fprintf(DiscoFile, "hostid: 0x%o\n", gethostid());
    fprintf(DiscoFile, "user: %d\n", uid);
    fprintf(DiscoFile, "VenusVersion: %d.%d\n", venusmajor, venusminor);
    fprintf(DiscoFile, "AdviceMonitorVersion: %d\n", advice);
    fprintf(DiscoFile, "ADSRVversion: %d\n", adsrv);
    fprintf(DiscoFile, "ADMONversion: %d\n", admon);
    fprintf(DiscoFile, "Qversion: %d\n", questionnaire->DMQVersionNumber);
    fprintf(DiscoFile, "TimeOfDisconnection: %d\n", questionnaire->TimeOfDisconnection);
    fprintf(DiscoFile, "TimeOfCacheMiss: %d\n", questionnaire->TimeOfCacheMiss);
    fprintf(DiscoFile, "Fid: <%x.%x.%x>\n", questionnaire->Fid.Volume, questionnaire->Fid.Vnode, questionnaire->Fid.Unique);
    fprintf(DiscoFile, "Path: %s\n", questionnaire->Pathname);
    fprintf(DiscoFile, "RequestingProgram: %s\n", GetCommandName((int)questionnaire->pid));
    fflush(DiscoFile);
    fclose(DiscoFile);
}

long DisconnectedMiss(RPC2_Handle _cid, DisconnectedMissQuestionnaire *questionnaire, RPC2_Integer *Qrc)
{
    char tmpFileName[MAXPATHLEN];
    char FileName[MAXPATHLEN];
    miss *m;

    LogMsg(100,LogLevel,LogFile,"E DisconnectedMiss: %s", TimeString((int)questionnaire->TimeOfDisconnection));

    m = new miss((char*)questionnaire->Pathname,GetCommandName((int)questionnaire->pid));

    IncrementCounter(&DMcount, PRESENTED);

    // Determine the filename
    sprintf(tmpFileName, "%s/#DisconnectedMiss.%d", tmpDir, DMcount.requestedCount);
    sprintf(FileName, "%s/DisconnectedMiss.%d", tmpDir, DMcount.requestedCount);

    InitDiscoFile(tmpFileName, VenusMajorVersionNumber, VenusMinorVersionNumber, ADVICE_MONITOR_VERSION, ADSRV_VERSION, ADMON_VERSION, questionnaire);

    fflush(stdout);
    *Qrc = ADMON_SUCCESS;

    {
       char *args[5];

       args[0] = discomiss;
       args[1] = tmpFileName;
       args[2] = (char *)questionnaire->Pathname;
       args[3] = GetCommandName((int)questionnaire->pid);
       args[4] = NULL;

       int rc = execute_tcl(discomiss, args);
       if (rc == -1) {
         LogMsg(0,LogLevel,LogFile, "DisconnectedMiss: execute_tcl ERROR");
         LogMsg(0,LogLevel,EventFile, "DisconnectedMiss: execute_tcl ERROR");
         *Qrc = ADMON_FAIL;
         return(RPC2_SUCCESS);
       }
    }

    if (CHILDresult != 0) {
	LogMsg(0,LogLevel,LogFile, "DisconnectedMiss: ERROR ==> tcl script return %d", CHILDresult);
        LogMsg(0,LogLevel,EventFile, "tcl_childreturn: disco %d", CHILDresult);
    }
    else 
        // Move tmpFileName over to FileName (still in /tmp)
        rename(tmpFileName, FileName);    

    CHILDpid = 0;

    LogMsg(1000,LogLevel,LogFile, "L DisconnectedMiss()");
    PrintCounters();
    *Qrc = CHILDresult;
    return(RPC2_SUCCESS);
}

long WeaklyConnectedMiss(RPC2_Handle _cid, RPC2_String Pathname, RPC2_Integer pid, ViceFid *fid) 
{
    miss *m;

    LogMsg(100,LogLevel,LogFile,"E WeaklyConnectedMiss: %s %d", (char*)Pathname, (int)pid);

    m = new miss((char*)Pathname,GetCommandName((int)pid));

    IncrementCounter(&WCMcount, PRESENTED);

    return(RPC2_SUCCESS);
}

long DataFetchEvent(RPC2_Handle _cide, RPC2_String Pathname, RPC2_Integer Size, RPC2_String Vfile)
{
    printf("DataFetchEvent:  Path=%s; Size=%d; Vfile=%s\n", Pathname, Size, Vfile);
    return(RPC2_SUCCESS);
}

long HoardWalkAdvice(RPC2_Handle _cid, RPC2_String InputPathname, RPC2_String OutputPathname, RPC2_Integer *ReturnCode)
{
    LogMsg(0,LogLevel,LogFile,"E HoardWalkAdvice(%s,%s)", (char *)InputPathname, (char *)OutputPathname);

    IncrementCounter(&HWAcount, PRESENTED);

    *ReturnCode = ADMON_SUCCESS;

    {
       char *args[4];

       args[0] = HOARDLIST;
       args[1] = (char *)InputPathname;
       args[2] = (char *)OutputPathname;
       args[3] = NULL;

       int rc = execute_tcl(HOARDLIST, args);
       if (rc == -1) {
         LogMsg(0,LogLevel,LogFile, "HoardWalkAdvice: execute_tcl ERROR");
         LogMsg(0,LogLevel,EventFile, "HoardWalkAdvice: execute_tcl ERROR");
         *ReturnCode = ADMON_FAIL;
         return(RPC2_SUCCESS);
       }
    }

    if (CHILDresult != 0) {
	LogMsg(0,LogLevel,LogFile, "HoardWalkAdvice: ERROR ==> tcl script return %d", CHILDresult);
        LogMsg(0,LogLevel,EventFile, "tcl_childreturn: disco %d", CHILDresult);
    }

    CHILDpid = 0;

    LogMsg(0,LogLevel,LogFile, "L HoardWalkAdvice()");
    PrintCounters();
    *ReturnCode = CHILDresult;

    return(RPC2_SUCCESS);
}

void InitReconFile(char *FileName, int venusmajor, int venusminor, int advice, int adsrv, int admon, ReconnectionQuestionnaire *questionnaire)
{
    FILE *ReconFile;
    struct stat buf;

    // Ensure it does not exist
    stat(FileName, &buf);
    assert(errno == ENOENT);

    // Create and initialize it
    ReconFile = fopen(FileName, "w+");
    assert(ReconFile != NULL);

    fprintf(ReconFile, "Reconnection Questionnaire\n");
    fprintf(ReconFile, "hostid: %d\n", gethostid());
    fprintf(ReconFile, "user: %d\n", uid);
    fprintf(ReconFile, "VenusVersion: %d.%d\n", venusmajor, venusminor);
    fprintf(ReconFile, "AdviceMonitorVersion: %d\n", advice);
    fprintf(ReconFile, "ADSRVversion: %d\n", adsrv);
    fprintf(ReconFile, "ADMONversion: %d\n", admon);
    fprintf(ReconFile, "Qversion: %d\n", questionnaire->RQVersionNumber);
    fprintf(ReconFile, "VolumeName: %s\n", questionnaire->VolumeName);
    fprintf(ReconFile, "VID: 0x%x\n", questionnaire->VID);
    fprintf(ReconFile, "CMLCount: %d\n", questionnaire->CMLcount);
    fprintf(ReconFile, "TimeOfDisconnection: %d\n", questionnaire->TimeOfDisconnection);
    fprintf(ReconFile, "TimeOfReconnection: %d\n", questionnaire->TimeOfReconnection);
    fprintf(ReconFile, "TimeOfLastDemandHoardWalk: %d\n", questionnaire->TimeOfLastDemandHoardWalk);
    fprintf(ReconFile, "NumberOfReboots: %d\n", questionnaire->NumberOfReboots);
    fprintf(ReconFile, "NumberOfCacheHits: %d\n", questionnaire->NumberOfCacheHits);
    fprintf(ReconFile, "NumberOfCacheMisses: %d\n", questionnaire->NumberOfCacheMisses);
    fprintf(ReconFile, "NumberOfUniqueCacheHits: %d\n", questionnaire->NumberOfUniqueCacheHits);
//    fprintf(ReconFile, "NumberOfUniqueCacheMisses: %d\n", questionnaire->NumberOfUniqueCacheMisses);
    fprintf(ReconFile, "NumberOfObjectsNotReferenced: %d\n", questionnaire->NumberOfObjectsNotReferenced);

    fflush(ReconFile);
    fclose(ReconFile);
}

int PresentRQ(char *volumeName) {
    char userVolume[32];

    // Guess at the name of the user volume --> WEAK!
    sprintf(userVolume, "u.%s", UserName);

    if (strcmp(volumeName, userVolume) == 0)
	return(1);
    else
	return(0);
}

long Reconnection(RPC2_Handle _cid, ReconnectionQuestionnaire *questionnaire, RPC2_Integer *Qrc)
{
    char tmpFileName[MAXPATHLEN];
    char FileName[MAXPATHLEN];

    LogMsg(100,LogLevel,LogFile,"E Reconnection: %s", questionnaire->VolumeName);
    // Log params to this reconnection event
    LogMsg(1000,LogLevel,LogFile,"VenusVersionNumber = %d.%d", VenusMajorVersionNumber, VenusMinorVersionNumber);
    LogMsg(1000,LogLevel,LogFile,"RQVersionNumber = %d", (int)questionnaire->RQVersionNumber);

    LogMsg(1000,LogLevel,LogFile,"Volume Name = %s <%x>", questionnaire->VolumeName, questionnaire->VID);
    LogMsg(1000,LogLevel,LogFile,"CMLcount = %d", questionnaire->CMLcount);
    LogMsg(1000,LogLevel,LogFile,"Time of Disconnection = %s",TimeString((long)questionnaire->TimeOfDisconnection)); 
    LogMsg(1000,LogLevel,LogFile,"Time of Reconnection = %s",TimeString((long)questionnaire->TimeOfReconnection)); 
    LogMsg(1000,LogLevel,LogFile,"Time of last demand hoard walk = ");
    LogMsg(1000,LogLevel,LogFile,"Number of Reboots = %d", questionnaire->NumberOfReboots);
    LogMsg(1000,LogLevel,LogFile,"Number of Cache HITS = %d", questionnaire->NumberOfCacheHits);
    LogMsg(1000,LogLevel,LogFile,"Number of Cache MISSES = %d", questionnaire->NumberOfCacheMisses);
    LogMsg(1000,LogLevel,LogFile,"Number of Unique Cache HITS = %d", questionnaire->NumberOfUniqueCacheHits);
    LogMsg(1000,LogLevel,LogFile,"Number of Objects NOT Referenced = %d", questionnaire->NumberOfObjectsNotReferenced);

    *Qrc = ADMON_SUCCESS;

    // Decide whether or not to present the reconnection questionnaire
    if (!PresentRQ((char *)questionnaire->VolumeName)) {
    	LogMsg(100,LogLevel,LogFile, "L Reconnection()");
	IncrementCounter(&Rcount, REQUESTED);
	PrintCounters();
	return(RPC2_SUCCESS);
    }

    IncrementCounter(&Rcount, PRESENTED);

    // Determine the filename
    sprintf(tmpReconnectionFileName, "%s/#Reconnection.%d", tmpDir, Rcount.requestedCount);
    sprintf(ReconnectionFileName, "%s/Reconnection.%d", tmpDir, Rcount.requestedCount);

    InitReconFile(tmpReconnectionFileName, VenusMajorVersionNumber, VenusMinorVersionNumber, ADVICE_MONITOR_VERSION, ADSRV_VERSION, ADMON_VERSION, questionnaire);

    int rc = fork();
    if (rc == -1) {
      LogMsg(0,LogLevel,LogFile, "Reconnection: ERROR ==> during fork");
      LogMsg(0,LogLevel,EventFile, "tcl_fork: reconn");
      *Qrc = ADMON_FAIL;
      return(RPC2_SUCCESS);
    }
    else if (!rc) {      
      // child process

      char *args[6];
      args[0] = reconnection;
      args[1] = tmpReconnectionFileName;
      args[2] = GetDateFromLong((long)questionnaire->TimeOfDisconnection);
      args[3] = GetTimeFromLong((long)questionnaire->TimeOfDisconnection);
      args[4] = GetStringFromTimeDiff((long)questionnaire->TimeOfReconnection-(long)questionnaire->TimeOfDisconnection);
      args[5] = NULL;
//      if (execl(reconnection, reconnection, tmpReconnectionFileName, GetDateFromLong((long)questionnaire->TimeOfDisconnection), GetTimeFromLong((long)questionnaire->TimeOfDisconnection), GetStringFromTimeDiff((long)questionnaire->TimeOfReconnection-(long)questionnaire->TimeOfDisconnection), 0, environ)) {
      if (execve(reconnection, (const char **) args, (const char **) environ)) {
	LogMsg(0,LogLevel,LogFile, "Reconnection (Child): ERROR ==> during execl");
        LogMsg(0,LogLevel,EventFile, "tcl_execl: reconn");
        *Qrc = ADMON_FAIL;
        return(RPC2_SUCCESS);
      }
    }
    else {
        // parent process
        CHILDpid = rc;
	LogMsg(1000,LogLevel,LogFile, "Reconnection (Parent): CHILDpid = %d", CHILDpid);
	ReconnectionQuestionnaireInProgress = 1;

	// Can't wait for the questionnaire due to some sort of race condition in Venus!
	// LWP_WaitProcess((char *)&CHILDpid);
        // Sleep for a couple of seconds so that if the exec fails it finishes first
        sleep(2);
    }

    LogMsg(100,LogLevel,LogFile, "L Reconnection()");
    PrintCounters();
    return(RPC2_SUCCESS);
}


long ReintegratePending(RPC2_Handle _cid, RPC2_String VolumeName, Boolean PendingFlag)
{
  IncrementCounter(&RPcount, REQUESTED);
  if (PendingFlag)
    printf("Alert:  ReintegratePending for Volume %s\n", VolumeName);
  else
    printf("Cancel:  ReintegratePending for Volume %s\n", VolumeName);
  fflush(stdout);
  return(RPC2_SUCCESS);
}

long InvokeASR(RPC2_Handle _cid, RPC2_String pathname, RPC2_Integer uid, RPC2_Integer *ASRid, RPC2_Integer *ASRrc)
{
    IncrementCounter(&IASRcount, PRESENTED);

    *ASRrc = ADMON_SUCCESS;

    NumASRStarted ++;
    *ASRid = NumASRStarted;

    struct stat statbuf;
    if (!::stat(JUMPSTARTASR, &statbuf)) {
    int rc = fork();
    if (rc == -1) {
      LogMsg(100,LogLevel,LogFile,"InvokeASR: ERROR ==> during fork");
      LogMsg(0,LogLevel,EventFile, "tcl_fork: asr");
      *ASRrc = ADMON_FAIL;
      return(RPC2_SUCCESS);
    }
    else if (!rc) {
      // child process
      setuid((unsigned short)uid);
      char dnamebuf[MAXPATHLEN];
      char fnamebuf[MAXNAMLEN];
      path((char *)pathname, dnamebuf, fnamebuf);
      if (chdir(dnamebuf)) {
	LogMsg(100,LogLevel,LogFile,"InvokeASR: ERROR ==> ASR jump-starter couldn't change to directory %s", dnamebuf);
        LogMsg(0,LogLevel,EventFile, "jump-starter: cd");
	*ASRrc = ADMON_FAIL;
	return(ENOENT);
      }
      execl(JUMPSTARTASR, JUMPSTARTASR, pathname, 0);
    }
    else 
	// parent process
        ASRinProgress = 1;
	CHILDpid = rc;
  }
  else {
    LogMsg(0,LogLevel,LogFile, "InvokeASR: ERROR ==> JUMPSTARTASR (%s) not found", JUMPSTARTASR);
    LogMsg(0,LogLevel,EventFile, "jump-starter: not found");
    *ASRrc = ADMON_FAIL;
    return(RPC2_SUCCESS);
  }

  PrintCounters();
  return(RPC2_SUCCESS);
}

long LostConnection(RPC2_Handle _cid)
{
  LogMsg(100,LogLevel,LogFile,"LostConnection to \"venus\"");
  IncrementCounter(&LCcount, REQUESTED);
  WeLostTheConnection = TRUE;
  if (AwaitingUserResponse == TRUE)
    (void) RPC2_Unbind(rpc2_LocalPortal.Value.InetPortNumber);
  return(RPC2_SUCCESS);
}


/****************************************************
 *******************  Data Mover  *******************
 ****************************************************/

/*
 *  We cannot write the reconnection questionnaire results directly to Coda files
 *  or else Venus blocks on the advice monitor and the advice monitor blocks on
 *  Venus.  The result is that the connection to the advice monitor eventually
 *  times out, thus preventing the deadlock...  Venus blocks on the advice monitor 
 *  to return from the reconnection questionnaire on Volume #1.  In presenting the
 *  reconnection questionnaire for Volume #1 to the user, the advice monitor must
 *  open a file in /coda in Volume #2.  Venus will not allow this open to go through 
 *  because the volume has a transition pending.  
 *
 *  A nominal solution to this problem is to write the files to /tmp first and then
 *  move them over into /coda at another time.  I improve upon this solution by writing
 *  the data to a file named #Name.n first (the data gets written in two stages -- initial
 *  information is written by the advice monitor and the data provided by the user is
 *  written by the tcl script.  Once the tickle script has returned successfully, I
 *  move the file into Name.n.  When the RPC2 GetRequest routine times out, I trigger 
 *  the Data Mover routine.  This routine moves completed data files from /tmp into /coda.
 */

int MoveFile(char *filename, char *hereDir, char* thereDir) {
    FILE *hereFile, *thereFile;
    char hereFileName[MAXPATHLEN];
    char thereFileName[MAXPATHLEN];
    int code = 0;

    LogMsg(0,LogLevel,LogFile, "E MoveFile(%s, %s, %s)\n", filename, hereDir, thereDir);

    // Set up the file names
    sprintf(hereFileName, "%s/%s", hereDir, filename);
    sprintf(thereFileName, "%s/%s", thereDir, filename);

    // Open the file we will copy FROM
    hereFile = fopen(hereFileName, "r");
    if (hereFile == NULL) {
	LogMsg(0,LogLevel,LogFile, "ERROR: MoveFile(%s,%s,%s) ==> Cannot open file %s", filename, hereDir, thereDir, hereFileName);
        LogMsg(0,LogLevel,EventFile, "movefile: open %s",hereFileName);
    }

    // Open the file we will copy TO
    thereFile = fopen(thereFileName, "w+");
    if (thereFile == NULL) {
	LogMsg(0,LogLevel,LogFile, "ERROR: MoveFile(%s,%s,%s) ==> Cannot open file %s", filename, hereDir, thereDir, thereFileName);
        LogMsg(0,LogLevel,EventFile, "movefile: open %s",thereFileName);
    }
    
    // Copy the file
    LogMsg(0,LogLevel,LogFile, "Moving %s to %s", hereFileName, thereFileName);
    if ((hereFile != NULL) && (thereFile != NULL))
	code = ffilecopy(hereFile, thereFile);
    else
	code = 1;

    fclose(hereFile);
    fclose(thereFile);

    if (code == 0)
	unlink(hereFileName);
    else
	unlink(thereFileName);
    return(code);
}

char *GetDataFile(char *here) {
    DIR *hereDIR;
    struct direct *dirent;
    static char filename[MAXPATHLEN];

    hereDIR = opendir(here);
    for (dirent = readdir(hereDIR); dirent != NULL; dirent = readdir(hereDIR))
	if ((dirent->d_name[0] != '#') &&
	    (dirent->d_name[0] != '.')) {
		(void) strcpy(filename, dirent->d_name);
		closedir(hereDIR);
		return(filename);
		}
    closedir(hereDIR);
    return(NULL);
}

void DataMover() {
    char *component = NULL;

    while (component = GetDataFile(tmpDir)) 
	    (void) MoveFile(component, tmpDir, WorkingDir);
}

/*********************************************************
 *******************  Signal Handlers  *******************
 *********************************************************/

// Handler for SIGTERM
void Shutdown(int code) {
  LogMsg(100,LogLevel,LogFile,"Received a SIGTERM to Shutdown, code = %d", code);
  shutdownFlag = TRUE;
  LWP_NoYieldSignal((char *)&shutdownFlag);
}

void UserDrivenEvent(int code) {
  LogMsg(100,LogLevel,LogFile,"Received a SIGUSR1 event, code = %d", code);
  userdrivenFlag = TRUE;
  LWP_NoYieldSignal((char *)&userdrivenFlag);
}

// Handler for SIGCHLD (to collect result of ASRs)
void Child(int code) { 
  union wait status;            /* so we can explain what happend */
  int pid;                      /* process id */
  int doneone = 0;

  LogMsg(1000,LogLevel,LogFile,"Child: SIGCHLD event, code = %d", code);
  
  do {                          /* in case > 1 kid died  */
    pid = wait3(&status, WNOHANG,(struct rusage *)0);
    if (pid>0) {                        /* was a child to reap  */
      LogMsg(100,LogLevel,LogFile,"Child: Child %d died, rc=%d, coredump=%d, termsig=%d",
	     pid, status.w_retcode, status.w_coredump, status.w_termsig);

      doneone=1;
      CHILDresult = status.w_retcode;
      if (pid == CHILDpid) {
	if (ReconnectionQuestionnaireInProgress == 1) {
	  LogMsg(100,LogLevel,LogFile, "Child Signal Handler: Reconnection Questionnaire finished.\n");
	  ReconnectionQuestionnaireInProgress = 0;
	  CHILDpid = 0;
	  if (CHILDresult != 0) {
	      LogMsg(0,LogLevel,LogFile, "Reconnection: ERROR ==> tcl script returned %d", CHILDresult);
              LogMsg(0,LogLevel,EventFile, "childreturned: reconn %d",CHILDresult);
          }
	  else
	      // Move tmpFileName over to FileName (still in /tmp)
	      rename(tmpReconnectionFileName, ReconnectionFileName);
	}
	else {
	  LogMsg(1000,LogLevel,LogFile,"Child Signal Handler: Return result=%d of pid=%d to waiting process", CHILDresult, pid);
	  LWP_NoYieldSignal((char *)&CHILDpid);
	}
      }
      else {
	LogMsg(0,LogLevel,LogFile,"Child Signal Handler: Unrecognized child");
      }
    }
    else {
      if (!doneone) {
	LogMsg(0,LogLevel,LogFile,"Child Signal Handler: SIGCHLD event, but no child died");
      }
    }
  } while (pid > 0);
}

/*********************************************************
 *************  Procedure Bodies for Vprocs  *************
 *********************************************************/

// responsible for handling user-driven events
void UserEventHandler(char *c) {
  FILE *commandFile;
  int rc;
  char command[LINELENGTH];

  LogMsg(100,LogLevel,LogFile, "UserEventHandler: Initializing...");

  // Start the user control panel
  {
    char arg[8];
    char *args[3];

    args[0] = USERINITIATED;
    sprintf(arg, "%d", thisPID);
    args[1] = arg;
    args[2] = NULL;

    int rc = fork_tcl(USERINITIATED, args);
    if (rc == -1) {
      LogMsg(0,LogLevel,LogFile, "UserEventHandler: execute_tcl ERROR");
      LogMsg(0,LogLevel,EventFile, "UserEventHandler: execute_tcl ERROR");
      exit(-1);
    }
  }

  LogMsg(100,LogLevel,LogFile, "UserEventHandler: Waiting...");

  while (1) {
    // Wait for and handle user events 
    LWP_WaitProcess((char *)&userdrivenFlag);

    // Handle user event
    LogMsg(100,LogLevel,LogFile, "UserEventHandler:  User requested event...");

    /* Open the command file */
    commandFile = fopen(CommandFileName, "r");
    if (commandFile == NULL) {
      fprintf(stderr, "Command file (%s) initialization failed (errno=%d)\n\n", 
	      CommandFileName, errno);
      fflush(stderr);
      continue;
    }

    /* Read the command line */
    if (fgets(command, LINELENGTH, commandFile) == NULL) {
      fprintf(stderr, "Command file empty\n\n");
      fflush(stderr);
      continue;
    }

    /* Close the command file */
    fclose(commandFile);

    switch (command[0]) {
      case SolicitHoardAdvice:
        SolicitAdviceOnNextHoardWalk();
        break;
      case UnsolicitHoardAdvice:
        UnsolicitAdviceOnNextHoardWalk();
        break;
      case RequestMissList:
        HandleWeakAdvice(); 
        break;
      case RequestLongFetchQuery:
        break;
      default:
	break;
    }

   unlink(CommandFileName);
  }
}

// responsible for shutting down the AdviceMonitor after a SIGTERM.
void ShutdownHandler(char *c) {
  LogMsg(100,LogLevel,LogFile, "ShutdownHandler: Waiting...");

  LWP_WaitProcess((char *)&shutdownFlag);

  LogMsg(100,LogLevel,LogFile, "ShutdownHandler: Shutdown imminent...");

  if (!AwaitingUserResponse) 
    /* Inform Venus of our untimely demise. */
    (void) ImminentDeath(VenusCID, (RPC2_String) HostName, uid, rpc2_LocalPortal.Value.InetPortNumber);

  (void) RPC2_Unbind(VenusCID);
  (void) RPC2_Unbind(rpc2_LocalPortal.Value.InetPortNumber);

  exit(0);
}

// responsible for informing venus about the results of the ASR.
void EndASREventHandler(char *c) {
  // there might be some race condition in the code below with other
  // users/objects needing ASRs to execute.
  while (1) {
    if (CHILDpid && ASRinProgress) {
      long code = ResultOfASR(VenusCID, NumASRStarted, CHILDresult);
      CHILDresult = -1;
      CHILDpid = 0; 

      if (code != RPC2_SUCCESS) {
	LogMsg(0,LogLevel,LogFile,"EndASREventHandler:  ERROR ==> ResultOfASR failed (%s)", RPC2_ErrorMsg((int)code));
        LogMsg(0,LogLevel,EventFile, "EndASRevent: failed %s",RPC2_ErrorMsg((int)code));
      }
      else
	LogMsg(100,LogLevel,LogFile, "EndASREventHandler:  Venus knows the result of the ASR.");
    }
    else 
	LWP_WaitProcess((char *)&CHILDpid);
  }
}
