/* Agent Tcl
   Bob Gray
   10 January 1996

   genAgent.cc

   This file implements the supporting agent routines.

   Copyright (c) 1995-2000, Bob Gray, Dartmouth College

   See the file "agent.terms" for information on usage and redistribution
   of this file and for a DISCLAIMER OF ALL WARRANTIES.
*/

#ifndef NO_PRAGMAS
#pragma implementation
#endif

#include "platPorting.h"	// platform-specific definitions
#include "platDelete.h"		// DELETE_OBJECT and DELETE_ARRAY_OBJECT
#include "genAgentIO.h"		// AgentIO
#include "genFactory.h"		// AgentFactory
#include "platSystem.h"		// SystemUtil

#include "platMath.h"
#include "platSigterm.h"
#include "platSystem.h"
#include "agentAgentId.h"
#include "agentVersion.h"
#include "genAgent.h"
#include "genAgentConn.h"
#include "suppDBuffer.h"
#include "genEncrypt.h"
#include "genLocation.h"
#include "genManager.h"
#include "genMasks.h"
#include "genServerInterface.h"
#include "genTransmit.h"
#include "randpool.h"

/* RandomInitializer

   This class initializes the random number generator inside the standard
   C library.  The static variable, randomInitializerControl, is set to
   MonitorOnce::e_DONE if we have already done the initialization.
*/

static MonitorOnce::OnceControl s_randomInitializerControl
    = MonitorOnce::e_NEVER;

class RandomInitializer: public OnceTrap
{
public:

    RandomInitializer (AGENT &agent):
	m_agent (agent)
    {
	// empty
    }

    virtual ~RandomInitializer () {
	// empty
    }

    void initialize (void) {
#ifndef NO_SRAND_FUNCTION
	srand (m_agent.agentRandom());
#endif
#ifndef NO_SRANDOM_FUNCTION
	srandom (m_agent.agentRandom());
#endif
    }

private:

    AGENT &m_agent;		// an agent
};

/* AgentSelectParameters::AgentSelectParameters, etc.

   Purpose: These procedures are the constructor, etc. for class
            AgentSelectParameters.
*/

AgentSelectParameters::AgentSelectParameters (void):
    m_numHandles (0),
    m_numHandleSlots (0),
    m_handlesIn ((UINT_32 *) NULL),
    m_handlesOut ((UINT_8 *) NULL),
    m_numFds (0),
    m_numFdSlots (0),
    m_fdsIn ((int *) NULL),
    m_fdsOut ((BOOLEAN *) NULL)    
{
    for (int i = 0; i < NUM_ITEMS; ++i) {
	m_itemsIn [i] = e_FALSE;
        m_itemsOut[i] = e_FALSE;
    }
}

void AgentSelectParameters::copyArrays 
	(const AgentSelectParameters &params)
{
    UINT_32 i;

    for (i = 0; i < (UINT_32) NUM_ITEMS; ++i) {
	m_itemsIn [i] = params.m_itemsIn [i];
 	m_itemsOut[i] = params.m_itemsOut[i];
    }

    if (m_numHandleSlots != 0) {

	m_handlesIn  = new UINT_32 [m_numHandleSlots];
	m_handlesOut = new UINT_8  [m_numHandleSlots];

	for (i = 0; i < m_numHandles; ++i) {
	    m_handlesIn [i] = params.m_handlesIn [i];
	    m_handlesOut[i] = params.m_handlesOut[i];
  	}
    }

    if (m_numFdSlots != 0) {

	m_fdsIn  = new int     [m_numFdSlots];
 	m_fdsOut = new BOOLEAN [m_numFdSlots];

	for (i = 0; i < m_numFds; ++i) {
	    m_fdsIn [i] = params.m_fdsIn [i];
	    m_fdsOut[i] = params.m_fdsOut[i];
	}
    }
}

AgentSelectParameters::AgentSelectParameters 
	(const AgentSelectParameters &params):
    m_numHandles (params.m_numHandles),
    m_numHandleSlots (params.m_numHandleSlots),
    m_handlesIn ((UINT_32 *) NULL),
    m_handlesOut ((UINT_8 *) NULL),
    m_numFds (params.m_numFds),
    m_numFdSlots (params.m_numFdSlots),
    m_fdsIn ((int *) NULL),
    m_fdsOut ((BOOLEAN *) NULL)
{
    copyArrays (params);
}

AgentSelectParameters& AgentSelectParameters::operator=
	(const AgentSelectParameters &params)
{
    if (this != &params) {
  
	    // delete the arrays
 
	DELETE_ARRAY_OBJECT(m_fdsIn);
	DELETE_ARRAY_OBJECT(m_fdsOut);
	DELETE_ARRAY_OBJECT(m_handlesIn);
	DELETE_ARRAY_OBJECT(m_handlesOut);

	    // copy the counts

	m_numHandles     = params.m_numHandles;
	m_numHandleSlots = params.m_numHandleSlots;
	m_numFds         = params.m_numFds;
 	m_numFdSlots	 = params.m_numFdSlots;

	    // copy the arrays

	copyArrays (params);
    }

    return (*this);
}

AgentSelectParameters::~AgentSelectParameters()
{
    DELETE_ARRAY_OBJECT(m_fdsIn);
    DELETE_ARRAY_OBJECT(m_fdsOut);
    DELETE_ARRAY_OBJECT(m_handlesIn);
    DELETE_ARRAY_OBJECT(m_handlesOut);
}
   
void AgentSelectParameters::setMaximumHandles (UINT_32 slots)
{
    if (slots > m_numHandleSlots) {

	UINT_32 *handlesIn  = new UINT_32 [slots];
	UINT_8  *handlesOut = new UINT_8  [slots];

	for (UINT_32 i = 0; i < m_numHandles; ++i) {
	    handlesIn [i] = m_handlesIn [i];
	    handlesOut[i] = m_handlesOut[i];
	}

	DELETE_ARRAY_OBJECT(m_handlesIn);
	DELETE_ARRAY_OBJECT(m_handlesOut);

	m_handlesIn      = handlesIn;
	m_handlesOut     = handlesOut; 
	m_numHandleSlots = slots;
    }
}

void AgentSelectParameters::setMaximumFds (UINT_32 slots)
{
    if (slots > m_numFdSlots) {

	int     *fdsIn  = new int     [slots];
	BOOLEAN *fdsOut = new BOOLEAN [slots];

	for (UINT_32 i = 0; i < m_numFds; ++i) {
	    fdsIn [i] = m_fdsIn [i];
	    fdsOut[i] = m_fdsOut[i];
	}

	DELETE_ARRAY_OBJECT(m_fdsIn);
	DELETE_ARRAY_OBJECT(m_fdsOut);

	m_fdsIn      = fdsIn;
	m_fdsOut     = fdsOut; 
	m_numFdSlots = slots;
    }
}

void AgentSelectParameters::resetOutputFlags (void)
{
    UINT_32 i;

    for (i = 0; i < (UINT_32) NUM_ITEMS; ++i) {
	m_itemsOut[i] = e_FALSE;
    }

    for (i = 0; i < m_numHandles; ++i) {
	m_handlesOut[i] = MEET_NO_CHANGE;
    }

    for (i = 0; i < m_numFds; ++i) {
	m_fdsOut[i] = e_FALSE;
    }
}
 
/* IncomingCallback::IncomingCallback and IncomingCallback::~IncomingCallback

   Purpose: These procedures are the constructor and destructor for class
	    IncomingCallback.
*/

IncomingCallback::IncomingCallback (void) {
    SigioHandler::setGeneralTrap (this);
}

IncomingCallback::~IncomingCallback () {
    /* empty */
}

/* AGENT::serverCodeToAgentCode

   Purpose: Convert a server error code to an agent error code

     Input: code = server error code -- e.g., ServerInterface::e_SERV_ERR_UNKNOWN
		   (int)

    Output: The procedure returns the appropriate error code.
*/

struct saMapEntry 
{
    ServerInterface::ServerErrorcodes serverCode;  // error code from server
    UINT_8 			      agentCode;   // corresponding agent error code
};

static saMapEntry saMap[] = {
    { ServerInterface::e_SERV_ERR_SOURCE		, AGENT_SERVER_SOURCE		},
    { ServerInterface::e_SERV_ERR_INTERP		, AGENT_SERVER_INTERP		},
    { ServerInterface::e_SERV_ERR_SYSTEM		, AGENT_SERVER_SYSTEM		},
    { ServerInterface::e_SERV_ERR_INVALID      		, AGENT_SERVER_INVALID		},
    { ServerInterface::e_SERV_ERR_LOST			, AGENT_SERVER_LOST		},
    { ServerInterface::e_SERV_ERR_NONEXISTENT  		, AGENT_NONEXISTENT		},
    { ServerInterface::e_SERV_ERR_NO_EVENT		, AGENT_NO_EVENT		},
    { ServerInterface::e_SERV_ERR_NAME_USED		, AGENT_NAME_USED		},
    { ServerInterface::e_SERV_ERR_NAME_RESERVED		, AGENT_NAME_RESERVED		},
    { ServerInterface::e_SERV_ERR_MAX_AGENTS   		, AGENT_SERVER_MAX_AGENTS	},
    { ServerInterface::e_SERV_ERR_MAX_ANON		, AGENT_SERVER_MAX_ANON		},
    { ServerInterface::e_SERV_ERR_LIAR	   		, AGENT_SERVER_LIAR		},
    { ServerInterface::e_SERV_ERR_REMOTE_BEGIN 		, AGENT_SERVER_RBEGIN		},
    { ServerInterface::e_SERV_ERR_ANON_REMOTE_BEGIN	, AGENT_SERVER_ANON_RBEGIN	},
    { ServerInterface::e_SERV_ERR_REMOTE_COMM 		, AGENT_SERVER_RCOMM		},
    { ServerInterface::e_SERV_ERR_REMOTE_INFO		, AGENT_SERVER_RINFO		},
    { ServerInterface::e_SERV_ERR_ANON_REMOTE_INFO	, AGENT_SERVER_ANON_RINFO	},
    { ServerInterface::e_SERV_ERR_LOAD			, AGENT_SERVER_LOAD		},
    { ServerInterface::e_SERV_ERR_TOO_MANY_EVENTS	, AGENT_SERVER_TOO_MANY_EVENTS	},
    { ServerInterface::e_SERV_ERR_INVALID_FORCE		, AGENT_CANT_FORCE		}
};
  
int AGENT::serverCodeToAgentCode (ServerInterface::ServerErrorcodes code)
{
    int mapSize = sizeof(saMap) / sizeof(saMapEntry);

    for (int i = 0; i < mapSize; i++) {
	if (saMap[i].serverCode == code) {
	    return (saMap[i].agentCode);
	}
    }

    return (AGENT_SERVER_ERROR);
}

/* AGENT::messageCodeToAgentCode

   Purpose: Convert a message error code to an agent error code

     Input: code       = message error code -- e.g., MESSAGE_TIMEOUT 
		         (int)

	    connection = connection that generated the error
			 (AgentConnectionProxy *)

    Output: The procedure returns the appropriate agent error code.
*/

int AGENT::messageCodeToAgentCode (int code, AgentConnectionProxy *connection)
{
    int rc;

	/* MESSAGE_LOST_CONNECTION error has different meanings depending */
	/* on whether the connection is a background connection or not    */

    if (code == MESSAGE_LOST_CONNECTION) {

	if (connection == NULL) { 

	    code = MESSAGE_SYS_ERROR;

	} else if (!((*connection)->isBackground())) {

	    code = MESSAGE_NO_SERVER;
	}
    }

	/* convert the error code to an agent code */

    switch (code) {

	case MESSAGE_OK			: rc = AGENT_OK;
					  break;

	case MESSAGE_TIMEOUT		: rc = AGENT_TIMEOUT;
				 	  break;

	case MESSAGE_SYS_ERROR		: rc = AGENT_FAILED_MESSAGE;
					  break;

	case MESSAGE_INVALID		: rc = AGENT_BAD_RESPONSE;
					  break;

	case MESSAGE_ENCRYPT		: rc = AGENT_ENCRYPT;
					  break;
	
	case MESSAGE_DECRYPT		: rc = AGENT_DECRYPT;
					  break;

	case MESSAGE_CANT_READ		: rc = AGENT_CANT_READ;
					  break;

	case MESSAGE_CANT_WRITE		: rc = AGENT_CANT_WRITE;
					  break;

	case MESSAGE_NO_SERVER		:
	case MESSAGE_NOT_FOUND		: rc = AGENT_NO_SERVER;
					  break;

	case MESSAGE_LOST_CONNECTION	: rc = AGENT_LOST_BACKGROUND;
				    	  break;

	default			    	: rc = AGENT_UNKNOWN;
				  	  break;
    } 

    return (rc);
}

/* AGENT::AGENT and AGENT::~AGENT

   Purpose: These procedures are the constructor and destructor for class 
	    AGENT.
*/

AGENT::AGENT (const Version &version, const AgentFactory &factory, AGENT::AgentSource agentSource):
    coreVersion (c_CORE_NAME, c_CORE_MAJOR_NUMBER, c_CORE_MINOR_NUMBER, c_CORE_QUALITY),
    interpreterVersion (version)
{
	/*
	 * SIGTERM handler
	 */

    SigtermHandler::setExitCode (ServerInterface::e_RECEIVED_SIGTERM);
    sigtermHandler = new SigtermHandler (this);

	/*
	 * subsystems
	 *
	 * security, permits, network location, meetings, and io
	 *
	 * These subsystems MUST be constructed in the given order since MASKS 
	 * depends on LOCATION which in turn depends on RESTRICT and SECURITY.
	 */

    security = factory.createSecurityInstance (this);
    restrict = factory.createRestrictInstance (this);
    location = factory.createLocationInstance (this);
    masks    = factory.createMasksInstance    (this);
    meetings = factory.createMeetingsInstance (this);
    io       = new AgentIO		      (this);

	/*
	 * further initialization if we are a local agent -- must initializae
	 * LOCATION first since MASKS gets IP addresses out of location
	 */

    if (agentSource == AGENT::e_LOCAL_AGENT) {
	(void) security -> startingBootstrap ();
	(void) location -> startingBootstrap ();
	(void) masks    -> startingBootstrap ();
    }
}

AGENT::~AGENT () 
{
    DELETE_OBJECT(sigtermHandler);
    DELETE_OBJECT(io);
    DELETE_OBJECT(security);
    DELETE_OBJECT(restrict);
    DELETE_OBJECT(location);
    DELETE_OBJECT(masks);
    DELETE_OBJECT(meetings);
}

/* AGENT::sigtermAlarm

   Purpose: This method is called when the agent receives a SIGTERM
*/

void AGENT::sigtermAlarm (void)
{
    printf ("got a SIGTERM!\n");

    if (io != NULL) {
	io -> turnBackgroundOff();
    }
}

/* AGENT::agentRestore

   Purpose: Restore an incoming agent

     Input: None 

	 	OR

	    fd = file descriptor of the socket that connects the agent to
		 the local agent server	
		 (int)

	    In the no argument case, the file descriptor is obtained from 
	    the CONNECTION environment variable, which the server set before
	    forking the agent interpreter.

    Output: The procedure returns one of the following codes.

	    AGENT_OK        = success
            AGENT_NO_SERVER = unable to receive the incoming agent
            AGENT_BAD_AGENT = invalid incoming agent 
*/

int AGENT::agentRestore (int fd)
{
    int code;
    UINT_32 randSize;
    UINT_32 masksSize;
    UINT_32 scriptSize;
    UINT_32 restrictSize;
    MESSAGE *message;
    AgentConnection *connection;

#ifdef REUSE_INTERPRETER

	/* set up the connection structure */

    connection = new AgentConnection 
	(fd, this, location -> machine, *security, AgentConnection::e_NoClose);

#else

	/* set up the connection structure */

    connection = new AgentConnection 
	(fd, this, location -> machine, *security);

#endif

	/* receive the incoming agent -- don't close the fd! */

    if ((message = connection -> unsecureReceiveAny (messagesToInterpreter)) == (MESSAGE *) NULL) {	
	DELETE_OBJECT(connection);
	return AGENT_NO_SERVER;
    }

	/* reset the agent's starting wall time to the current time  */
	/* and the agent's starting CPU time to 0 (If we are reusing */ 
        /* interpreters, we might have waited quite a while to       */
	/* receive a new agent, and if we do not reset the starting  */
	/* wall time, it will appear as if the agent has already     */
	/* been running for a long time.)                            */

#ifdef REUSE_INTERPRETER
    restrict->resetWallTime();
    SystemUtil::resetCpuTime();
#endif

	/* break out the message components */

    ServerInterface::SubmissionTypes type = 
	(ServerInterface::SubmissionTypes) message -> getByte (0);

    SmartIdPtr root      (message -> getId (1));		// ensure deletion
    SmartIdPtr rootHome  (message -> getId (2));		// ensure deletion
    SmartIdPtr local     (message -> getId (3));		// ensure deletion
    SmartIdPtr localHome (message -> getId (4));		// ensure deletion

    char *script = message -> getBinary (5, scriptSize);	// don't delete

    if ((scriptSize == 0) || ServerInterface::isSubmissionTypeOutOfRange (type)) {
	DELETE_OBJECT(message);
	DELETE_OBJECT(connection);
	return (AGENT_BAD_STATE);
    }

    DynamicBuffer stateBuffer (scriptSize, script, BufferOwnership::e_TAKE);

    BASE_SECURITY *sender        = message -> getSecurity (6);
    SmartCharPtr  randBuffer     (message -> getBinary (7, randSize));
    SmartCharPtr  restrictBuffer (message -> getBinary (8, restrictSize));
    SmartCharPtr  masksBuffer    (message -> getBinary (9, masksSize));
    UINT_8 trustedFlag           = message -> getByte (10);
    Permit *permit               = message -> getPermit (11);

    char *directory = message -> getString (12);
    DynamicString tempDirectory (directory, BufferOwnership::e_TAKE);

	/* stir in the 64 random bytes (and, if we have not done so already, */
	/* initialize the random-number generators inside the standard C     */
	/* library)                                                          */

    randPoolAddBytes ((unsigned char *) ((char *) randBuffer), randSize);

    if (s_randomInitializerControl != MonitorOnce::e_DONE) {
	RandomInitializer randomInitializer (*this);
	MonitorOnce::doOnce (s_randomInitializerControl, randomInitializer);	
    }

	/* bootstrap the security, location and masks subsystems -- must */
	/* initializate LOCATION first since MASKS gets IP addresses out */
	/* of LOCATION                                                   */

    (void) security -> arrivingBootstrap (type, *sender);
    (void) location -> arrivingBootstrap (type, *root, *rootHome, *local, *localHome, tempDirectory);
    (void) masks    -> arrivingBootstrap (type);
    DELETE_OBJECT(sender);

	/* restore the masks */

    if ((type == ServerInterface::e_SUB_JUMP) || 
	(type == ServerInterface::e_SUB_FORK) ||
	(type == ServerInterface::e_SUB_AGLET_JUMP)) {

	AgentMasksUnpacker masksUnpacker (*masks);

        if (masksUnpacker.unpack (masksBuffer, ((char *) masksBuffer) + masksSize) == NULL) {
	    DELETE_OBJECT(connection);
	    DELETE_OBJECT(message);
	    DELETE_OBJECT(permit);
	    return (AGENT_BAD_STATE);
        }
    }

	/* restore the restrictions */

    RestrictUnpacker restrictUnpacker (*restrict, type);

    if (restrictUnpacker.unpack (restrictBuffer, ((char *) restrictBuffer) + restrictSize) == NULL) {
	DELETE_OBJECT(connection);
	DELETE_OBJECT(message);
	DELETE_OBJECT(permit);
	return (AGENT_BAD_STATE);
    }

	/* increment the number of jumps if we have just jumped; */
	/* otherwise increment the depth                         */


    if ((type == ServerInterface::e_SUB_JUMP) || 
	(type == ServerInterface::e_SUB_AGLET_JUMP)) {

	restrict -> incrementTotalJumps ();

    } else {

	restrict -> incrementChildDepth ();
    }

	/* restore the agent interpreter's state */

    if ((code = restoreState (type, stateBuffer)) != AGENT_OK) {
	DELETE_OBJECT(connection);
	DELETE_OBJECT(message);
	DELETE_OBJECT(permit);
	return (code);
    }

	/* finish restoring the overall permit */

    if ((type != ServerInterface::e_SUB_JUMP) && 
	(type != ServerInterface::e_SUB_AGLET_JUMP)) {

	    /*
	     * At this point we should stop and consider what we have --      
	     * since we are forking or submitting, we have already restored  
	     * the group start depth, the current depth and the current depth
	     * limit (in RestrictPacker::unpack above).  This depth information,
	     * however, was packaged up by the sending *AGENT*, so our task 
	     * here is to decide whether or not to believe the depth limit,
	     * and if we do not believe it, to change it.
	     *                                                               
	     * Also we need to fill in the overall restriction for the other   
	     * four resources (jumps, children, wall-time and cpu-time).  Note
	     * that in all cases the group-start permit and the current usage
	     * values for these four other resources have already been set 
	     * correctly in the RESTRICT constructors.
	     *                                                               
	     * Note that we have three separate if clauses (rather than      
	     * if-else) so that the level of trust in the sending machine can
	     * be downgraded just by resetting trustedFlag.
	     */

	assert ((trustedFlag == e_MACHINE_TRUSTED) || 
		(trustedFlag == e_MACHINE_TRUSTED_BUT_UNVERIFIED) ||
		(trustedFlag == e_MACHINE_UNTRUSTED));

	if (trustedFlag == e_MACHINE_TRUSTED) {

 		/*
 		 * If the sending machine is trusted and we were able to 
 		 * adequately verify its identity, then we must have a 
		 * depth limit and that depth limit is correct, so simply
 		 * set the limits for the other four resources to the local 
		 * machine's limits (since the other four resources start at 
		 * zero usage for any new child agent).
 		 */
 
 	    const Permit &base = restrict -> getBase();
 	    permit -> setDepthLimit (base.getDepthLimit());
 	    restrict -> addBase (*permit);
	}
    
	if (trustedFlag == e_MACHINE_TRUSTED_BUT_UNVERIFIED) {

		/*
		 * If the sending machine is trusted but we were unable to
		 * adequately verify its identity, we first decide whether or 
		 * not the reported depth limit falls within the range allowed 
		 * by the local machine's limit.  If it does, then we keep the 
		 * reported depth limit and just set the limits for the other 
		 * four resources.  If it does not, then we reset it.
		 */

	    const Permit &base = restrict -> getBase();

	    if (!base.haveDepthLimit()) {

		    /*
		     * If the parent agent started on the sending machine,
		     * we do not have a depth limit, so we downgrade to 
		     * e_MACHINE_UNTRUSTED and end up using the local
		     * machine's limit below.
		     */

		trustedFlag = e_MACHINE_UNTRUSTED;	// downgrade

	    } else {

		    /*
		     * Otherwise we see if the depth limit falls at or below
		     * the maximum-allowable depth limit.
		     */

		UINT_32 groupStartDepth = restrict -> getGroupStartDepth();
		UINT_32 currentDepth    = restrict -> getChildDepth();
		UINT_32 depthLimit      = base.getDepthLimit();
		UINT_32 localDepthLimit = 
		    groupStartDepth + permit -> getDepthLimit();

		if (!((groupStartDepth <= currentDepth) && (currentDepth <= depthLimit))) {
		    DELETE_OBJECT(connection);
		    DELETE_OBJECT(permit);
		    DELETE_OBJECT(message);
		    return (AGENT_BAD_STATE);
		}

		    /* Here's the point that must be grasped -- If we make it to  */
		    /* here and assign a depth limit, this depth limit can be NO  */
		    /* HIGHER than the one that would have been assigned if we    */
		    /* had actually KNOWN whether or not the sending machine was  */
		    /* trusted.  A careful examination of the TRUSTED case above  */
		    /* and the UNTRUSTED case below should hopefully convince     */
		    /* you of this.  Note also that an agent that actually came   */
		    /* from a trusted machine (and was not modified in transit)   */
		    /* must make it to this point, simply because the agent,      */
		    /* can not affect the relative order of groupStartDepth, etc. */
		    /* while on the trusted machine).  A similar comment holds    */
		    /* for all of the checks in the TRUSTED_BUT_UNVERIFIED        */
		    /* section for JUMPS below.                                   */

		if (depthLimit <= localDepthLimit) {
		    permit -> setDepthLimit (depthLimit);
		} else if (localDepthLimit <= currentDepth) {
		    permit -> setDepthLimit (currentDepth);
		} else {
		    permit -> setDepthLimit (localDepthLimit);
		}
 
		restrict -> addBase (*permit);
	    }
	}

	if (trustedFlag == e_MACHINE_UNTRUSTED) {

		/*
		 * If the sending machine is untrusted, then we do not  
		 * believe the reported depth limit at all.  We reset the
		 * the group-start depth to the current depth, set the 
		 * depth limit to the current depth plus the local machine's 
		 * limit, and set the other four limits to the local machine's 
		 * limits as before.
		 */

	    restrict -> setGroupStartDepth (restrict -> getChildDepth());
	    UINT_32 dsum = MathUtil::addWithCeiling (permit -> getDepthLimit(), restrict -> getChildDepth());
	    permit -> setDepthLimit (dsum);
	    restrict -> addBase (*permit);
	}

    } else {

	    /*
	     * At this point we should stop and consider what we have --      
	     * since we are jumping, we have already restored the group-start
	     * permit, the current usage values and the current limits for all
	     * five resources (in RestrictPacker::unpack above).  This 
	     * information, however, was packaged up by the sending *AGENT*, so 
	     * our task here is to decide whether or not to believe the limits, 
	     * and if we do not believe them, to change them.
	     *                                                               
	     * Note that we have three separate if clauses (rather than      
	     * if-else) so that the level of trust in the sending machine can
	     * be downgraded just by resetting trustedFlag.
	     */

	assert ((trustedFlag == e_MACHINE_TRUSTED) || 
		(trustedFlag == e_MACHINE_TRUSTED_BUT_UNVERIFIED) ||
		(trustedFlag == e_MACHINE_UNTRUSTED));

	if (trustedFlag == e_MACHINE_TRUSTED) {

		/* 
		 * If the sending machine is trusted and we were able to
		 * adequately verify it's identity, then we have nothing to do,
		 * since we can believe all of the limits.
		 */
	}

 	if (trustedFlag == e_MACHINE_TRUSTED_BUT_UNVERIFIED) {

		/*
		 * If the sending machine is trusted but we were unable to
		 * adequately verify its identity, then we first decide 
	  	 * whether or not the reported limits fall within the range
		 * allowed by the local machine's limits.  If they do, we
		 * just keep the reported limits.  If any single limit does not,
		 * we reset that limit to the current usage PLUS the local
		 * machine's limit.
		 */

	    Permit base = restrict -> getBase();

	    if (!base.haveAll()) {

		trustedFlag = e_MACHINE_UNTRUSTED;	// downgrade

	    } else {

		    /* check the WALL time limit */

		struct timeval startWall      = restrict -> getStartTimeval();
		struct timeval groupStartWall = restrict -> getGroupStartTimeval();
		struct timeval currentWall    = SystemUtil::getCurrentWall();

		struct timeval wallLimit      =
		    TimevalUtil::addTimevals
			(startWall, base.getWallLimit());

		struct timeval localWallLimit =
		    TimevalUtil::addTimevals
			(groupStartWall, permit -> getWallLimit());

		if (!((TimevalUtil::compareTimevals(startWall, groupStartWall) <= 0) &&
		      (TimevalUtil::compareTimevals(groupStartWall,currentWall) <= 0) &&
		      (TimevalUtil::compareTimevals(currentWall,wallLimit) <= 0))) {
		    exit (ServerInterface::e_GLOBAL_WALL_VIOLATION);
		}

		struct timeval newLimit;

		if (TimevalUtil::compareTimevals (wallLimit,localWallLimit) <= 0) {
		    newLimit = wallLimit;
		} else if (TimevalUtil::compareTimevals (localWallLimit, currentWall) <= 0) {
		    newLimit = currentWall;
		} else {
		    newLimit = localWallLimit;
		}

		base.setWallLimit (TimevalUtil::subTimevals(newLimit,startWall));

		    /* check the CPU time limit */

		struct timeval groupStartCpu = restrict -> getGroupStartCpu();
		struct timeval currentCpu    = SystemUtil::getCurrentCpu();
		struct timeval cpuLimit	     = base.getCpuLimit();

		struct timeval localCpuLimit =
		    TimevalUtil::addTimevals
			(groupStartCpu, permit -> getCpuLimit());

		if (!((TimevalUtil::compareTimevals(groupStartCpu,currentCpu) <= 0) &&
		      (TimevalUtil::compareTimevals(currentCpu,cpuLimit) <= 0))) {
		    exit (ServerInterface::e_GLOBAL_CPU_VIOLATION);
		}

		if (TimevalUtil::compareTimevals(cpuLimit,localCpuLimit) <= 0) {
		    base.setCpuLimit (cpuLimit);
		} else if (TimevalUtil::compareTimevals(localCpuLimit,currentCpu) <= 0) {
		    base.setCpuLimit (currentCpu);
		} else {
		    base.setCpuLimit (localCpuLimit);
		}

		    /* check the children limit */

		UINT_32 groupStartChildren = restrict -> getGroupStartChildren();
		UINT_32 currentChildren    = restrict -> getTotalChildren();
		UINT_32 childrenLimit      = base.getChildrenLimit();
		UINT_32 localChildrenLimit = 
		    groupStartChildren + permit -> getChildrenLimit();

		if (!((groupStartChildren <= currentChildren) && (currentChildren <= childrenLimit))) {
		    DELETE_OBJECT(connection);
		    DELETE_OBJECT(permit);
		    DELETE_OBJECT(message);
		    return (AGENT_BAD_STATE);
		}

		if (childrenLimit <= localChildrenLimit) {
		    base.setChildrenLimit (childrenLimit);
		} else if (localChildrenLimit <= currentChildren) {
		    base.setChildrenLimit (currentChildren);
		} else {
		    base.setChildrenLimit (localChildrenLimit);
		}

		    /* check the jumps limit */

		UINT_32 groupStartJumps = restrict -> getGroupStartJumps();
		UINT_32 currentJumps    = restrict -> getTotalJumps();
		UINT_32 jumpsLimit      = base.getJumpLimit();
		UINT_32 localJumpsLimit = 
		    groupStartJumps + permit -> getJumpLimit();

		if (!((groupStartJumps <= currentJumps) && (currentJumps <= jumpsLimit))) {
		    DELETE_OBJECT(connection);
		    DELETE_OBJECT(permit);
		    DELETE_OBJECT(message);
		    return (AGENT_BAD_STATE);
		}

		if (jumpsLimit <= localJumpsLimit) {
		    base.setJumpLimit (jumpsLimit);
		} else if (localJumpsLimit <= currentJumps) {
		    base.setJumpLimit (currentJumps);
		} else {
		    base.setJumpLimit (localJumpsLimit);
		}

		    /* check the depth limit */

		UINT_32 groupStartDepth = restrict -> getGroupStartDepth();
		UINT_32 currentDepth    = restrict -> getChildDepth();
		UINT_32 depthLimit      = base.getDepthLimit();
		UINT_32 localDepthLimit = 
		    groupStartDepth + permit -> getDepthLimit();

		if (!((groupStartDepth <= currentDepth) && (currentDepth <= depthLimit))) {
		    DELETE_OBJECT(connection);
		    DELETE_OBJECT(permit);
		    DELETE_OBJECT(message);
		    return (AGENT_BAD_STATE);
		}

		if (depthLimit <= localDepthLimit) {
		    base.setDepthLimit (depthLimit);
		} else if (localDepthLimit <= currentDepth) {
		    base.setDepthLimit (currentDepth);
		} else {
		    base.setDepthLimit (localDepthLimit);
		}

		    /* if we make it here, then we have a good set of limits, */
		    /* so we add those limits as the base restriction         */
 
		restrict -> addBase (base);
	    }
	}

	if (trustedFlag == e_MACHINE_UNTRUSTED) {

		/*
		 * If the sending machine is untrusted, then we do not believe
		 * any of the reported items.  We reset the group-start permit
		 * to the current usage and set the overall limits to the 
		 * current usage PLUS the local machine's limits. 
		 */

	    Permit scratch;
	    scratch.setWallLimit (SystemUtil::getCurrentWall());
	    scratch.setCpuLimit (SystemUtil::getCurrentCpu());
	    scratch.setChildrenLimit (restrict -> getTotalChildren());
	    scratch.setJumpLimit (restrict -> getTotalJumps());
	    scratch.setDepthLimit (restrict -> getChildDepth());

	    restrict -> setGroupStartPermit (scratch);
	    scratch.setWallLimit (TimevalUtil::subTimevals (scratch.getWallLimit(), restrict -> getStartTimeval()));
	    permit -> add (scratch);
	    restrict -> addBase (*permit);
	}
    }

	/* turn on the background handler and cleanup --           */
	/* do not delete the connection since it is the background */
	/* connection for all further communication!               */

    io -> turnBackgroundOn (connection);
    DELETE_OBJECT(permit);
    DELETE_OBJECT(message);
    return AGENT_OK;
}

int AGENT::agentRestore (void)
{
    int fd;
    char *value;

	/* get the fd over which the agent is arriving */

    if ((value = getenv ("CONNECTION")) == NULL) { 
	return AGENT_NO_SERVER;
    }

    fd = atoi (value);

	// initialize the file descriptor (e.g., in Java, create an entry in 
	// the fd lock table)

    SystemUtil::initializeRawFd (fd);

	/* hand control to the other agentRestore method */

    return (agentRestore(fd));
}

/* AGENT::agentCodeToShortString

   Purpose: Convert an agent error code into a short string

     Input: code = the error code
		   (int)

    Output: The procedure returns a DynamicString that contains the short
            string. 
*/

static char *error[] = {
	"OK",				/* AGENT_OK		        */
	"REGISTERED",			/* AGENT_ALREADY_REG	        */
	"NO_SERVER",			/* AGENT_NO_SERVER	        */
	"BAD_RESPONSE",			/* AGENT_BAD_RESPONSE	        */
	"SERVER_ERROR",			/* AGENT_SERVER_ERROR	        */
	"NOT_REGISTERED",		/* AGENT_NOT_REG	        */
	"PROGRAM_ERROR",		/* AGENT_PROGRAM_ERROR          */
	"TIMEOUT",			/* AGENT_TIMEOUT	        */
	"LOST_BACKGROUND",		/* AGENT_LOST_BACKGROUND        */
	"NONEXISTENT",			/* AGENT_NONEXISTENT	        */
	"SELF",				/* AGENT_SELF		        */
	"UNKNOWN",			/* AGENT_UNKNOWN	        */
	"BAD_STATE",			/* AGENT_BAD_STATE	        */
	"SAME_MACHINE",			/* AGENT_SAME		        */
	"PENDING_EVENT",		/* AGENT_PENDING_EVENT	        */
	"NO_EVENT",			/* AGENT_NO_EVENT	        */
	"MESSAGE_SUBSYSTEM",		/* AGENT_FAILED_MESSAGE         */
	"ENCRYPTION",			/* AGENT_ENCRYPT                */
	"DECRYPTION",			/* AGENT_DECRYPT	        */
	"SERVER_SOURCE",	        /* AGENT_SERVER_SOURCE          */
	"SERVER_INTERP",		/* AGENT_SERVER_INTERP          */
	"SERVER_SYSTEM",		/* AGENT_SERVER_SYSTEM          */
	"SERVER_INVALID",		/* AGENT_SERVER_INVALID         */
	"TOO_MANY_MEETINGS",		/* AGENT_TOO_MANY_MEETINGS      */
	"SERVER_LOST",			/* AGENT_SERVER_LOST            */
	"NAME_USED",			/* AGENT_NAME_USED              */
	"NAME_RESERVED",		/* AGENT_NAME_RESERVED          */
	"NONEXISTENT_MEETING",          /* AGENT_BAD_MEETING            */
	"CANT_ACCEPT",			/* AGENT_CANT_ACCEPT	        */
	"UNKNOWN_MEETING",		/* AGENT_UNKNOWN_MEETING        */
	"CANT_REJECT",			/* AGENT_CANT_REJECT	        */
	"CANT_SEND",			/* AGENT_CANT_SEND              */
	"MEET_BROKEN",			/* AGENT_MEET_BROKEN	        */
	"CANT_RECEIVE",			/* AGENT_CAN_RECEIVE            */
	"NEXT_FILE",			/* AGENT_NEXT_FILE              */
 	"CANT_READ",			/* AGENT_CANT_READ              */
	"NEXT_MESSAGE",		  	/* AGENT_NEXT_MESSAGE           */
	"CANT_WRITE",			/* AGENT_CANT_WRITE             */
	"MEETING_SELF",			/* AGENT_MEET_SELF	        */
	"NO_JUMPS",			/* AGENT_NO_JUMPS               */
	"SERVER_MAX_AGENTS",		/* AGENT_SERVER_MAX_AGENTS      */
	"SERVER_MAX_ANON_AGENTS",	/* AGENT_SERVER_MAX_ANON        */
	"SERVER_LIAR",		 	/* AGENT_SERVER_LIAR            */
	"SERVER_NO_REMOTE_BEGIN",	/* AGENT_SERVER_RBEGIN          */
	"SERVER_NO_ANON_REMOTE_BEGIN",  /* AGENT_SERVER_ANON_RBEGIN     */
	"SERVER_NO_REMOTE_COMM",	/* AGENT_SERVER_RCOMM           */	
	"SERVER_NO_REMOTE_INFO",	/* AGENT_SERVER_RINFO           */
	"SERVER_NO_ANON_REMOTE_INFO",   /* AGENT_SERVER_ANON_RINFO      */
	"AGENT_NO_CHILDREN",		/* AGENT_NO_CHILDREN	        */
	"SERVER_MAX_LOAD",		/* AGENT_SERVER_LOAD	        */
	"SERVER_TOO_MANY_EVENTS",	/* AGENT_SERVER_TOO_MANY_EVENTS */
	"CANT_FORCE", 			/* AGENT_CANT_FORCE		*/
	"MEETING_REJECTED"		/* AGENT_MEET_REJECTED          */
	"AGENT_EXIT"			/* AGENT_EXIT                   */
};

DynamicString AGENT::agentCodeToShortString (int code)
{
    DynamicString string;

    if ((code >= AGENT_BEG_GENERAL) && (code <= AGENT_END_GENERAL)) {
	string.append (error[code - AGENT_BEG_GENERAL]);
    } else {
	string.append ("UNKNOWN");
    }

    return (string);
}

/* AGENT::agentCodeToLongString

   Purpose: Construct an agent error code into a long human-readable string

     Input: code   = the error code
		     (int)

	    name   = the name of the destination server
		     (const DynamicString &)

   Output: The procedure returns a DynamicString that contains the long string.
*/
 
DynamicString AGENT::agentCodeToLongString (int code, const DynamicString& name)
{
    DynamicString string;

	/* put the error message into the string */

    switch (code) {

	case AGENT_OK:
	    break;

	case AGENT_MEET_REJECTED:

	    string.append ("meeting was rejected");
	    break;

	case AGENT_SERVER_RCOMM:

	    string.append ("permission denied: server does not allow remote communication");
	    break;

	case AGENT_SERVER_RINFO:

	    string.append ("permission denied: server does not allow remote information requests");
	    break;

	case AGENT_SERVER_ANON_RINFO:

	    string.append ("permission denied: server does not allow remote information requests from anonymous agents");
	    break;

	case AGENT_SERVER_RBEGIN:

	    string.append ("permission denied: server does not allow remote registration");
	    break;

	case AGENT_SERVER_ANON_RBEGIN:

	    string.append ("permission denied: server does not allow remote registration by anonymous agents");
	    break;

	case AGENT_SERVER_LIAR:
	
	    string.append ("permission denied: agent is lieing about its identity");
	    break;

	case AGENT_SERVER_MAX_ANON:

	    string.append ("permission denied: server is running the maximum number of ANONYMOUS agents");
	    break;

	case AGENT_SERVER_MAX_AGENTS:

	    string.append ("permission denied: server is running the maximum number of agents");
	    break;

	case AGENT_NO_JUMPS:

	    string.append ("permission denied: current permit does not allow any jumps");
	    break;

	case AGENT_NO_CHILDREN:

	    string.append ("permission denied: current permit does not allow any children");
	    break;

	case AGENT_MEET_SELF:

	    string.append ("an agent can not meet with itself");
	    break;

	case AGENT_NEXT_FILE:

	    string.append ("next thing on the meeting is a file");
	    break;

	case AGENT_NEXT_MESSAGE:

	    string.append ("next thing on the meeting is a message");
	    break;

	case AGENT_CANT_RECEIVE:
	    string.append ("meeting is not connected");
	    break;

	case AGENT_MEET_BROKEN:
	    string.append ("meeting has broken");
	    break;

	case AGENT_CANT_SEND:
	    string.append ("meeting is not connected");
	    break;

	case AGENT_CANT_REJECT:
	    string.append ("meeting is not in the \"requested\" state");
	    break;
	
	case AGENT_UNKNOWN_MEETING:
	    string.append ("unknown error while attempting to complete meeting");
	    break;

	case AGENT_CANT_ACCEPT:
	    string.append ("meeting is not in the \"requesting\" state"); 
	    break;
	
	case AGENT_ALREADY_REG:
	    string.append ("agent is already registered");
	    break;

	case AGENT_NO_SERVER:
	    string.append ("unable to send to \"");
	    string.append (name);
	    string.append ("\"");
	    break;

	case AGENT_BAD_RESPONSE:
	    string.append ("bad response from \"");
	    string.append (name);
	    string.append ("\"");
	    break;

	case AGENT_SERVER_ERROR:
	    string.append ("unknown server error on \"");
	    string.append (name);
	    string.append ("\"");
	    break;

	case AGENT_NOT_REG:
	    string.append ("agent is not registered");
	    break;

	case AGENT_TIMEOUT:

	    if (name.isEmpty()) {
		string.append ("timeout");
	    } else {
	        string.append ("timeout while attempting to contact \"");
	        string.append (name);
	        string.append ("\"");
	    }

	    break;

	case AGENT_LOST_BACKGROUND:
	    string.append ("background connection has been lost");
	    break;

	case AGENT_NONEXISTENT:
	    string.append ("specified agent does not exist");
	    break;

        case AGENT_SELF:
	    string.append ("agent can not force itself");
	    break;

	case AGENT_BAD_STATE:
	    string.append ("unable to capture interpreter state");
	    break;

	case AGENT_SAME:
	    string.append ("jumping to the same machine");
	    break;

	case AGENT_PENDING_EVENT:
	    string.append ("pending notification");
	    break;

	case AGENT_NO_EVENT:
	    string.append ("notification is impossible since there was no symbolic name");
	    break;

	case AGENT_FAILED_MESSAGE:
	    string.append ("system error in the messaging subsystem");
	    break;

	case AGENT_ENCRYPT:
	    string.append ("unable to encrypt the outgoing request\n\n");
	    string.append (getEncryptionError());
	    break;

	case AGENT_DECRYPT:
	    string.append ("unable to decrypt the incoming response\n\n");
	    string.append (getEncryptionError());
	    break;

	case AGENT_SERVER_SOURCE:
	    string.append ("permission denied: server \"");
	    string.append (name);
	    string.append ("\" does not accept requests from the current machine");
	    break;

	case AGENT_SERVER_INTERP:
	    string.append ("necessary interpreter is not available at server \"");
	    string.append (name);
	    string.append ("\"");
	    break;

	case AGENT_SERVER_SYSTEM:
	    string.append ("system error on server \"");
	    string.append (name);
	    string.append ("\"");
	    break;

	case AGENT_SERVER_INVALID:
	    string.append ("server \"");
	    string.append (name);
	    string.append ("\" does not implement this request");
	    break;

	case AGENT_TOO_MANY_MEETINGS:
	    string.append ("maximum number of meetings has been reached");
	    break;

	case AGENT_SERVER_LOST:
	    string.append ("server \"");
	    string.append (name);
	    string.append ("\" has lost the agent from its tables - probable server crash and restart");
	    break;

	case AGENT_SERVER_LOAD:
	    string.append ("server \"");
	    string.append (name);
	    string.append ("\" has reached its maximum load");
	    break;
	
	case AGENT_SERVER_TOO_MANY_EVENTS:
	    string.append ("permission denied: agent has reached the maximum number of outstanding status events");
	    break;

	case AGENT_NAME_USED:
	    string.append ("permission denied: requested symbolic name is in use");
	    break;

	case AGENT_NAME_RESERVED:
	    string.append ("permission denied: requested symbolic name was empty or reserved");
	    break;

	case AGENT_BAD_MEETING:
	    string.append ("no meeting with the given handle");
	    break;

	case AGENT_CANT_READ:
	    string.append ("unable to read from file: POSIX ");
	    string.append (SystemUtil::errnoToShortString(errno));
	    string.append (" ");
	    string.append (SystemUtil::errnoToLongString(errno));
	    break;

	case AGENT_CANT_WRITE:
	    string.append ("unable to write to file: POSIX ");
	    string.append (SystemUtil::errnoToShortString(errno));
	    string.append (" ");
	    string.append (SystemUtil::errnoToLongString(errno));
	    break;

	case AGENT_CANT_FORCE:
	    string.append ("agent does not have sufficient authority to force the target agent");
	    break;

	case AGENT_PROGRAM_ERROR:
	    string.append ("runtime error in the agent program");
	    break;

	case AGENT_EXIT: 
	    string.append ("agent should exit");
	    break;

	default:
	    string.append ("unknown error");
	    break;
    }

    return (string);
}
