/*******************************************************************************
 * Copyright (c) 2003, 2009 IBM, Intel Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Andy Kaylor, Intel - Initial re-implementation of Agent Controller
 *    IBM - Non-Windows startup code moved from older RAC (by Andy Kaylor)
 *
 * $Id: AgentController.c,v 1.47 2009/09/24 21:02:04 jwest Exp $ 
 *******************************************************************************/ 


#include "ConnectionManager.h"
#include "AgentManager.h"
#include "ConfigurationManager.h"
#include "LoggingService.h"
#include "ACLog.h"
#include "tptp/TPTPCommon.h"
#include "tptp/version.h"

#ifdef _WIN32
  #include <windows.h>
  #include <direct.h>
  #include <shlwapi.h> /* Bug 80367 */
#else
  #include <signal.h>
  #include <unistd.h>
  #include <string.h>
  #include <strings.h>
  #include <pthread.h>
  #include <locale.h>

/** Required for file searching functionality, which is used by the findConfigJarUnix(...) function. */
	#include <sys/types.h>
	#include <dirent.h>

#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define ARG_SHUTDOWN "-shutdown"
#define ARG_VERSION "-v"
#define ARG_VERSION_LONG "-version"
#define ARG_FULL_VERSION "-fullversion"
#define ARG_PARSE_PEER_ATTACH_ADDRESS "-peerAttachAddress="
#define ARG_PARSE_PEER_ATTACH_PORT "-peerAttachPort="
#define ARG_CONFIG_DIR "-d"

#define MAXFD 1024

/* The home directory of the data server */
#ifdef __OS400__
    #define CONFIGURATION_HOME			"/opt/hyadesdc"
    #define CONFIGURATION_HOME_LENGTH	11
#else
    #define CONFIGURATION_HOME			"TPTP_AC_HOME"
#endif


/* Forward declarations */
int parseAndProcessParameters( int argc, char* argv[], BOOL* invokeShutdown, ConnectionManager_t* cm );
void shutdownServer(ConfigurationManager_t* cfgMgr, ConnectionManager_t* cnctMgr);
tptp_int32 initializeProgram();
void handleMainComplete();
void cleanup();
tptp_int32 getConfigurationPath(unsigned char *buffer, tptp_int32 length);

int generateDefaultServiceConfigIfNecessary();


#ifndef _WIN32
  void cleanupOnSignal(tptp_int32 sig);
  void cleanupChild(tptp_int32 sig);
#endif

/* Avoid copying these as globals into other code modules */
/*    They get passed as parameters where they are needed */
AgentManager_t         agentManager;
ConnectionManager_t    connectionManager;
ConfigurationManager_t configurationManager;
LoggingService_t       loggingService;
char				   *iacConfigDirPath = NULL;

/* Help for termination */
#ifdef _WIN32
#define TPTP_STOP_EVENT_NAME "TPTPACStopEvent"
#elif defined _OS400
/* I don't know what OS400 is going to use */
#else  /* Neither Windows nor OS400 */
pthread_mutex_t  endMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t   endCondition = PTHREAD_COND_INITIALIZER;
tptp_int32       okToEnd = 0;
#endif

/**
 *********************************************************************
 *                                                       
 * @brief                                  
 *    Agent Controller entry point
 *                                                       
 *********************************************************************/
tptp_int32 main(tptp_int32 argc, char* argv[])
{
    char  configBuffer[1024];
	int   initRet = 0;
	int   rc =0;
	BOOL  invokeShutdown = FALSE;
	int   generateReturnVal = 0;

	if (argc > 1)
	{
		if ( !parseAndProcessParameters( argc, argv, &invokeShutdown, &connectionManager ) )
		{
			/* If parseAndProcessParameters returns zero, that indicates we should abort */
			return 0;
		}
	}

	/* Bug 175696 starts */
	if(!isEnvironmentValid()) {
		printf("The TEMP environment variable does not point to a valid directory.\n");
		printf("Agent Controller will not start.\n");
		TPTP_LOG_ERROR_MSG(am, "The TEMP environment variable does not point to a valid directory.");
		TPTP_LOG_ERROR_MSG(am, "Agent Controller will not start.");
		return 0;
	}
	/* Bug 175696 ends */

	#ifdef MVS
		tptp_zosInitializeSemaphoreLock();
	#endif

	//bug  242899
	#ifndef _WIN32
		setlocale(LC_ALL, "");
	#endif
	//bug  242899 ends
        
	initializeXMLPlatformUtils();
	loggingService_init( &loggingService );

	if ( invokeShutdown )
	{
		loggingService_disableLogging( &loggingService );
	}
	else
	{
		/* The initializeProgram function forks on some OSs and a zero return value
		 * indicates that main() should exit. 
		 */
		if ( !initializeProgram() )
		{
			TPTP_LOG_DEBUG_MSG( am, "Exiting main" );
			terminateXMLPlatformUtils();

			return 0;
		}
	}
	
	/* Tell ourcomponents about each other */
	agentManager.connectionManager = &connectionManager;
	agentManager.configurationManager = &configurationManager;
	agentManager.loggingService = &loggingService;
	connectionManager.agentManager = &agentManager;
	connectionManager.loggingService = &loggingService;
	configurationManager.connectionManager = &connectionManager;
	configurationManager.agentManager = &agentManager;
	configurationManager.loggingService = &loggingService;

	/* Initialize */
	connectionManager_init( &connectionManager );
	agentManager_init( &agentManager );
	configurationManager_init( &configurationManager );

	/* Search for the serviceconfig file and call SetConfig with default option if the serviceconfig has not been generated */
	generateReturnVal = generateDefaultServiceConfigIfNecessary();

	if(generateReturnVal != 0) {
		TPTP_LOG_ERROR_MSG1(am, "generateDefaultServiceConfigIfNecessary() function returned a non-zero error code, indicating an error. retval: %d", generateReturnVal);
	}
	

    /* Locate our configuration file */
	strcpy( configBuffer, argv[0] ); /* Bug 68906 */
	if ( getConfigurationPath(configBuffer, 1024) < 0 ) 
	{
		/* TODO: handleServiceStartError(); */
		terminateXMLPlatformUtils();
		return -1;
	}
	else
	{
		configurationManager_setConfigDirectory( &configurationManager, configBuffer );
	}
	/* Read our configuration settings */
	initRet = configurationManager_readConfigFile( &configurationManager );

	if (initRet < 0)
	{
		TPTP_LOG_DEBUG_MSG( am, "Error reading the Agent Controller configuration file. Please check for valid configuration." );
		printf("Error reading the Agent Controller configuration file. Please check for valid configuration.");
		printf("Could not start Agent Controller.");
		fflush(stdout);
	}
	else
	{
		if ( invokeShutdown )
		{
			shutdownServer(&configurationManager, &connectionManager);
		}
		else
		{
			TPTP_LOG_DEBUG_MSG( am, "Ready to start servers" );

			/* start the transport layers */

			rc = connectionManager_startServers( &connectionManager );
			if (rc == -1)
			{
				TPTP_LOG_DEBUG_MSG( am, "Error starting transport layers, Agent controller exiting.");
				printf( "Error starting transport layers, Agent controller exiting. \n");
				printf( "See servicelog.log for error report. \n");
				return -1;
			}
			agentManager_startAutoloadAgents( &agentManager );

			TPTP_LOG_DEBUG_MSG( am, "Everything is running" );	
			
			handleMainComplete();
		}
	}

	TPTP_LOG_DEBUG_MSG( am, "Exiting main" );
	terminateXMLPlatformUtils();


	#ifdef MVS
		/* Free any undeleted semaphores (ACTL leaves one laying around) */
		tptp_zosDeleteUndeletedSemaphores();
	#endif

	return 0;
}

/*
 * Parse out the address value from the command line argument
 * The address should be numerical (nnn.nnn.nnn.nnn) IPv4 address
 */
unsigned int parsePeerAttachAddress(char* str) {
	unsigned int addr = -1;
	int argLen;
	int prefixLen;
	int valueLen;
	int dotCount = 0;

	argLen = strlen(str);
	prefixLen = strlen(ARG_PARSE_PEER_ATTACH_ADDRESS);
	valueLen = argLen - prefixLen;

	if(valueLen > 0) {
		int i;
		BOOL isValid = TRUE;

		for(i = prefixLen; i < argLen; i++) {
			/* The address should be in the form nnn.nnn.nnn.nnn */
			if(!isdigit(str[i])) {
				if((str[i] != '.') && (dotCount < 3)){ /* should have max 3 dots in the IPv4 address */
					isValid = FALSE;
					break;
				}
				else {
					dotCount++;
				}
			}
		}

		if(isValid && (dotCount == 3)) {
			int addrByte[4];
			int addrByteCount = 0;
			char *tok;
			char *valueStr = (char*)tptp_malloc(sizeof(char) * valueLen + 1); /* should be null terminate */
			BZERO(valueStr, valueLen + 1);
			strncpy(valueStr, str + prefixLen, valueLen);

			tok = strtok(valueStr, ".");
			while(tok != NULL) {
				addrByte[addrByteCount++] = atoi(tok);
				tok = strtok(NULL, ".");
			}

			addr = ((addrByte[0] << 24) & 0xff000000) |
					((addrByte[1] << 16) & 0x00ff0000) |
					((addrByte[2] << 8) & 0x0000ff00) |
					(addrByte[3] & 0x000000ff);

			tptp_free(valueStr);
#if _DEBUG
			printf("DEBUG: Peer attach address override is %d.%d.%d.%d\n", addrByte[0], addrByte[1], addrByte[2], addrByte[3]);
#endif
		}
		else {
			printf("ERROR: Peer attach address should be a valid IPv4 value (nnn.nnn.nnn.nnn)\n");
			exit(0);
		}
	}

	return addr;
}

/*
 * Parse out the port value from the command line argument
 */
int parsePeerAttachPort(char* str) {
	int port = -1;
	int argLen;
	int prefixLen;
	int valueLen;

	argLen = strlen(str);
	prefixLen = strlen(ARG_PARSE_PEER_ATTACH_PORT);
	valueLen = argLen - prefixLen;

	if(valueLen > 0) {
		int i;
		BOOL isValid = TRUE;

		for(i = prefixLen; i < argLen; i++) {
			if(!isdigit(str[i])) {
				isValid = FALSE;
				break;
			}
		}

		if(isValid) {
			char *valueStr = (char*)tptp_malloc(sizeof(char) * valueLen + 1); /* should be null terminate */
			BZERO(valueStr, valueLen + 1);
			strncpy(valueStr, str + prefixLen, valueLen);
			port = atoi(valueStr);
			tptp_free(valueStr);
#if _DEBUG
			printf("DEBUG: Peer attach port override is %d\n", port);
#endif
		}
		else {
			printf("ERROR: Peer attach port should be a valid integer value\n");
			exit(0);
		}
	}

	return port;
}

/*
 * Process the command line parameters
 * -v : display the version of the Agent Controller
 * -version: same as '-v'
 * -fullversion: display a verbose version message
 * -shutdown: terminate the Agent Controller
 * -peerAttachAddress: the overriding IP address used when requesting peer monitor
 * -peerAttachPort: the overriding port used when requesting peer monitor
 */
int parseAndProcessParameters( int argc, char* argv[], BOOL* invokeShutdown, ConnectionManager_t* cm )
{
	int i; /* For loop counter */
	FILE *serviceConfigFile;
	char * FILESEP = NULL, iacConfigFilePath[2048];
	char *temConfigDir;

	for(i = 1; i < argc; i++) {
		char *arg;

		arg = argv[i];
	
		if (isEqualString( arg, ARG_FULL_VERSION))
		{
			printf("TPTP Agent Controller Version %s\n", _VERSION);
			printf("%s\n", _COPYRIGHT);
			printf("All rights reserved. This program and the accompanying materials\n");
			printf("are made available under the terms of the Eclipse Public License v1.0\n");
			printf("which accompanies this distribution, and is available at\n");
			printf("http://www.eclipse.org/legal/epl-v10.html\n");			
			return 0;
		}
		else if (isEqualString( arg, ARG_VERSION) || isEqualString( arg, ARG_VERSION_LONG))
		{
			printf("%s\n", _VERSION);			
			return 0;
		}
		else if(0 == strcmp(arg, ARG_SHUTDOWN)) { /* Bug 71607 */
			/* The actual shutdown is performed below.  We need to do some initialization first. */
			*invokeShutdown = TRUE;
		}
		else if(0 == strncmp(arg, ARG_PARSE_PEER_ATTACH_ADDRESS, strlen(ARG_PARSE_PEER_ATTACH_ADDRESS))) {
			cm->peerInfo.usePeerAttachAddress = TRUE;
			cm->peerInfo.peerAttachAddress = parsePeerAttachAddress(arg);
		}
		else if(0 == strncmp(arg, ARG_PARSE_PEER_ATTACH_PORT, strlen(ARG_PARSE_PEER_ATTACH_PORT))) {
			cm->peerInfo.usePeerAttachPort = TRUE;
			cm->peerInfo.peerAttachPort = parsePeerAttachPort(arg);
		}else if (isEqualString( arg, ARG_CONFIG_DIR)){
			if(argc <= i+1 || argv[i+1] == NULL){
				printf("You need to specify the configuration directory to use.\n" );
				return 0;
			}  
			#ifdef _WIN32
				FILESEP = "\\";
			#else
				FILESEP = "/";
			#endif
				
			//bug 243431, remove the "" around the argument
			if(argv[i+1][0] == '\"' && argv[i+1][strlen(argv[i+1])-1] == '\"'){
				temConfigDir = argv[i+1];
				temConfigDir++;
				temConfigDir[strlen(temConfigDir)-1] = '\0';
			}else{
				temConfigDir = argv[i+1];
			}
			sprintf(iacConfigFilePath, "%s%sserviceconfig.xml", temConfigDir, FILESEP);
			
			serviceConfigFile = fopen(iacConfigFilePath,"r");
			
			if(serviceConfigFile == 0) {  
				printf("Can't open configuration file %s.\n",iacConfigFilePath);
				return 0;
			}else{
				iacConfigDirPath = (char*)tptp_malloc(sizeof(char) * 2048);
				
				sprintf(iacConfigDirPath, "%s", temConfigDir);
				fclose(serviceConfigFile);
			}
		}
	}

	return 1;
}

/**
 *********************************************************************
 *                                                       
 * @brief                                  
 *    This function sends a message to another instance of the AC
 *    requesting it to shutdown.  This instance will also go away
 *    when we return from this function.
 *
 *    This function reports errors using printf since shutdown implies
 *    a short-lived console application.
 *
 *    Use agentController_shutdown to shutdown THIS instance of the AC
 *
 *********************************************************************/
void shutdownServer(ConfigurationManager_t* cfgMgr, ConnectionManager_t* cnctMgr)
{
	tptp_string* pmt;
	tptp_string* ci;
	tptp_int32   rc;
	tptp_uint32  connectionID;
	char         shutdownFmt[] = "<Cmd src=\"%u\" dest=\"%u\" ctxt=\"-1\"><shutdown iid=\"org.eclipse.tptp.agentManager\" /></Cmd>";
	char         shutdownCmd[256];

	printf("Shutting down the Agent Controller...\n");

	/* Get the peer monitoring transport layer */
	rc = configurationManager_getPeerConnectionTransport( cfgMgr, &pmt );
	if ( ( rc != 0 ) || (pmt == NULL) )
	{
		printf( "The configuration information could not be found for contacting the Agent Controller to be shutdown.\n" );
		return;
	}

	/* Get the requested information */
	rc = connectionManager_getPeerConnectionInfo( cnctMgr, pmt, "shutdownConnectionInfo", &ci );
	if ( ( rc != 0 ) || (ci == NULL) )
	{
		printf( "The connection information could not be found for contacting the Agent Controller to be shutdown.\n" );
		tptp_free( pmt );
		return;
	}

	/* Establish a connection to the AC described -- it will be the local AC we want to shutdown */
    rc = connectionManager_connectToPeer( cnctMgr, ci, &connectionID );
	if ( rc != 0 )
	{
		printf( "Unable to connect to the Agent Controller to be shutdown.\n" );

		if ( rc == TPTP_REQUEST_IS_LOCAL )
		{
			/* This is a potential pitfall.  Currently our TL doesn't do this, but we're expecting it 
			      to be local, so it could be.  I used a new "connection type" above (shutdownConnectionInfo), 
				  which the TLs should be able to use to recognize that local connection is desired.  */
			tptp_free( pmt );
			tptp_free( ci );
			return;
		}

		tptp_free( pmt );
		tptp_free( ci );
		return;
	}

	/* Build the shutdown message */
	sprintf( shutdownCmd, shutdownFmt, connectionID, AGENT_MANAGER );

	/* Send the shutdown message */
	connectionManager_sendMessage( cnctMgr, connectionID, shutdownCmd );

	/* Drop the connection */
	connectionManager_dropPeerConnection( cnctMgr, connectionID );

	/* Clean up */
	tptp_free( pmt );
	tptp_free( ci );
}


/**
 *********************************************************************
 *                                                       
 * @brief                                  
 *    Unlike shutdownServer above, this function causes THIS instance
 *    of the AC to be shutdown.  shutdownServer shutsdown a peer AC.
 *                                                       
 *********************************************************************/
tptp_int32 agentController_shutdown()
{
#ifdef _WIN32
    HANDLE hStopEvent;
	hStopEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, TPTP_STOP_EVENT_NAME);
	SetEvent( hStopEvent );
#elif defined _OS400
	/* I don't know what OS400 is going to use */
#else  /* Neither Windows nor OS400 */
	pthread_mutex_lock( &endMutex );
	okToEnd = 1;
	pthread_cond_signal( &endCondition );
	pthread_mutex_unlock( &endMutex );
#endif

	return 0;
}

/**
 *********************************************************************
 *                                                       
 * @brief                                  
 *    Initialize resources used by the Agent Manager and perform any
 *    necessary startup tasks
 *                                                       
 * @return
 *    nonzero if program execution should continue
 *    0       if program execution should not continue (i.e. we forked)
 *
 *********************************************************************/
tptp_int32 initializeProgram() 
{
#ifdef _WIN32
	/* Nothing to do for Windows */
	return 1;

#elif defined _OS400
/* 237349 begins */
	struct sigaction sigActSIGCHLD;

	/* Handling write to broken pipe/socket exception from stdio (defect 232262) */
	signal(SIGPIPE, SIG_IGN);

	/* Handling SIGSEGV */
	signal(SIGSEGV, cleanupOnSignal);

	/* Handling SIGTERM */
	signal(SIGTERM, cleanupOnSignal);

	/* Cleanup our children when they exit */
	BZERO(&sigActSIGCHLD, sizeof(struct sigaction));
	sigActSIGCHLD.sa_handler=&cleanupChild;
	sigaction(SIGCHLD, &sigActSIGCHLD, NULL);
/* 237349 ends */

	/* Fork not supported on OS400, continue executing */
	return 1;

#else

#ifdef _TPTP_DIAGNOSTIC_
	/* The SIGPIPE handler is required, even if we don't fork */
	signal(SIGPIPE, SIG_IGN);

	/* For diagnostic/debugging purposes, we may want to hold the console and not trap signals */
	return 1;
#else
	/* First we fork so that the parent ends and the child continues. */
	if ( fork() == 0 ) 
	{
		/* Become the session leader and hense a new process group, and become the leader of that process group */
		setsid();

		/* Ignore the SIGHUP signal so that we don't get a SIG_HUP when we fork again */
		signal(SIGHUP, SIG_IGN);

		/* Fork again so that this process cannot become the controlling terminal */
		if ( fork() == 0 ) 
		{
			tptp_int32 i;
			struct sigaction sigActSIGCHLD;

			/* Close any file descriptors that we have inherited */
         /* DNS:  Don't close stdin, stdout, stderr because if we  */
         /*       do they will be reused by the server and mess up */
         /*       stdin, stdout, stderr of the launched processes  */
         /*       and the usage of the log file - see defect 217346*/
         /*       Need to investigate the consequences of this     */
         /*       further.  The console code in launcher.c may be  */
         /*       incorrect.                                       */

			for(i=3; i<MAXFD; i++) 
			{
				close(i);
			}

			/* Handling Ctrl-C exit */
			signal(SIGINT, cleanupOnSignal);

			/* Handling write to broken pipe/socket exception from stdio (defect 232262) */
			signal(SIGPIPE, SIG_IGN);

			/* Handling SIGSEGV */
			signal(SIGSEGV, cleanupOnSignal);

			/* Handling SIGTERM */
			signal(SIGTERM, cleanupOnSignal);

			/* Cleanup our children when they exit */
			bzero(&sigActSIGCHLD, sizeof(struct sigaction));
			sigActSIGCHLD.sa_handler=&cleanupChild;

			/* RKD:  On all of the platforms except solaris we can
			         successfully listen for our children to exit so
					 that we can clean up there zombie state manually.
					 On Solaris we are going to specify not to allow
					 zombies to occur and to not tell us when the process
					 exits.  THIS SHOULD BE REVISITED.
			*/
#if defined _SOLARIS
			sigActSIGCHLD.sa_flags=SA_NOCLDSTOP | SA_NOCLDWAIT;
#endif
			sigaction(SIGCHLD, &sigActSIGCHLD, NULL);

			/* This is the part of the process we want to continue executing */
			return 1;
		}
	}

	/* We forked.  Tell the calling process to return */
	return 0;

#endif /* diagnostic */

#endif /* neither windows nor OS400 */
}


/**
 *********************************************************************
 *                                                       
 * @brief                                  
 *    Retrieves the path to the configuarion directory that was
 *    created during the install.
 *                                                       
 * @return
 *    nonzero if program execution should continue
 *    0       if program execution should not continue (i.e. we forked)
 *
 *********************************************************************/
tptp_int32 getConfigurationPath(unsigned char *buffer, tptp_int32 length) 
{
#ifdef __OS400__
	/* Don't use the RASERVER_HOME environment variable on iSeries */
	strcpy((char*)buffer, CONFIGURATION_HOME);
	strcat((char*)buffer, RELATIVE_CONFIGURATION_DIR);
#else

	char *acHome;
	tptp_int32 len;

	if(iacConfigDirPath != NULL){
		strcpy(buffer, 	iacConfigDirPath);
		return 0;	
	}

	acHome = getenv(CONFIGURATION_HOME); /* Check TPTP_AC_HOME environment first */

	/* Bug 68906 */
	if(!acHome) 
	{ /* If no TPTP_AC_HOME defined, try to resolve using the current working directory */
		if(buffer) 
		{
			char *pos;

			len = strlen((char*)buffer);
#ifdef _WIN32
			pos = StrStrI((char*)buffer, "ACServer"); /* Bug 80367 */
			if (pos == NULL)
			{
				pos = StrStrI((char*)buffer, "RAServer");
			}
#else
			pos = strstr((char*)buffer, "ACServer");
			if (pos == NULL)
			{
				pos = strstr((char*)buffer, "RAServer");
			}
#endif
			if (pos == NULL)
			{
				return -1;
			}

			/* Replace the "tptpAgentCtlr" string with null */
			*pos = '\0';

			/* Check buffer size */
			if(strlen((char*)buffer) > 1024 - strlen(PARENT_CONFIG_DIR_PATH)) 
			{
				/* TODO: tptp_setLastError(INSUFFICIENT_BUFFER_SIZE, 0); */
				return -1;
			}

#ifdef _WIN32
			/* Case 1: Absolute path on Windows with a drive letter */
			if((buffer != NULL) && ((buffer[1] == ':') && (buffer[2] == FILE_SEPARATOR))) {
				strcat(buffer, "..");
			}
#else
			/* Case 1: Absolute path on unix */
			if((buffer != NULL) && (buffer[0] == FILE_SEPARATOR)) 
			{
				strcat((char*)buffer, "..");
			}
#endif /* _WIN32 */
			/* Case 3: Relative path */
			else 
			{
				char *cwd = (char*)tptp_malloc(sizeof(char) * 1024);
				BZERO(cwd, 1024);
				getcwd(cwd, 1024);

#ifdef _WIN32
				/* Special case: absolute path on Windows without a driver letter */
				if((buffer != NULL) && (buffer[0] == FILE_SEPARATOR)) 
				{
					acHome = (char*)tptp_malloc(sizeof(char) * 1024);
					BZERO(acHome, 1024);
					strncpy(acHome, cwd, 2); /* Prepend with the drive letter */
					strcat(acHome, buffer);
					strcat(acHome, "..");
					strcpy(buffer, acHome);
					tptp_free(cwd);
					tptp_free(acHome);
				}
				else 
				{
#endif /* _WIN32 */
					if(strlen((char*)cwd) + 1 + strlen((char*)buffer) + strlen(PARENT_CONFIG_DIR_PATH) > 1024) 
					{ /* Reserve 1 for FILE_SEPARATOR */
						/* TODO: tptp_setLastError(INSUFFICIENT_BUFFER_SIZE, 0); */
						tptp_free(cwd);
						return -1;
					}
					else 
					{
						acHome = (char*)tptp_malloc(sizeof(char) * 1024);
						BZERO(acHome, 1024);
						sprintf(acHome, "%s%c%s..", cwd, FILE_SEPARATOR, buffer);
						strcpy((char*)buffer, acHome);
						tptp_free(cwd);
						tptp_free(acHome);
					}
#if _WIN32
				}
#endif /* _WIN32 */
			}
		}
		else 
		{ /* Both env and argv[0] do not exist */
			/* TODO: tptp_setLastError(RASERVER_HOME_NOT_SET, 0); */
			return -1;
		}
	}
	else 
	{
		/* Check the size of the buffer we were supplied for the data */
		len = strlen(acHome);
		if(len + (tptp_int32) strlen(RELATIVE_CONFIGURATION_DIR) + 1 > length) 
		{
			/* TODO: tptp_setLastError(INSUFFICIENT_BUFFER_SIZE, 0); */
			return -1;
		}
		memcpy(buffer, acHome, len+1);
	}
	/* Bug 68906 */

	strcat((char*)buffer, RELATIVE_CONFIGURATION_DIR);

#endif /* _OS400_ */

	return 0;
}


void handleMainComplete()
{
#ifdef _WIN32
	/* wait */
    HANDLE hStopEvent;
	hStopEvent = CreateEvent(NULL, TRUE, FALSE, TPTP_STOP_EVENT_NAME);
	WaitForSingleObject(hStopEvent, INFINITE);

	cleanup();

#elif defined _OS400
	/* TODO: Figure out what this maps to with the pluggable transport model */

	/* Detach the internal server handle and wait for the external message pump to exit */
	pthread_detach(_server_info->internalPumpTid);
	pthread_join(_server_info->externalTid, &status);

#else  /* Neither Windows nor OS400 */

	/* flush any error messages */
	fflush(stderr);

	/* Wait for the okToEnd flag to be set */
	pthread_mutex_lock( &endMutex );
	while ( TRUE )
	{
		pthread_cond_wait( &endCondition, &endMutex );
		if ( okToEnd )
			break;
	}
	pthread_mutex_unlock( &endMutex );

	cleanup();

	/* Detach the internal server handle and wait for the external message pump to exit */
/*#ifdef MVS
	pthread_detach(&_server_info->internalPumpTid);
#else
	pthread_detach(_server_info->internalPumpTid);
#endif
	pthread_join(_server_info->externalTid, &status);
*/

	TPTP_LOG_DEBUG_MSG( am, "Going away" );
#endif
}

void cleanup()
{
	TPTP_LOG_DEBUG_MSG( am, "Cleaning up" );
	
	agentManager_cleanup( &agentManager );
	connectionManager_cleanup( &connectionManager );
	configurationManager_cleanup( &configurationManager );
}

#ifndef _WIN32

void cleanupOnSignal(tptp_int32 sig) 
{
	/* TODO: restore the following line when we have an equivalent to ra_logServiceMessage */
/*	ra_logServiceMessage(__FILE__, __LINE__,RA_DEBUG, "Recieving signal %d", sig); */
	cleanup();
}

void cleanupChild(tptp_int32 sig) 
{
	/* TODO: restore the following line when we have an equivalent to ra_childExited */
/*	ra_childExited(); */
}

#endif /* NOT _WIN32 */







#define CONFIG_JAR_PLUGIN_STRING "org.eclipse.tptp.platform.agentcontroller_"


#ifdef _WIN32 


char * findConfigJarWin() {
	char *configJarLoct;
    FILE *fp = NULL;
	char * val;

	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;
	
	configJarLoct = (char*)tptp_malloc(sizeof(char) * 4096);
    
	// Check whether config.jar file exists (this is the case in standalone AC)
    sprintf(configJarLoct, "..\\lib\\config.jar");

    fp = fopen(configJarLoct, "r");
    
	// If it exists, return the location and close
    if(fp != 0) {
            fclose(fp);
            return configJarLoct;
    }  

	// Doesn't exist......

	configJarLoct[0] = 0;

	// Search through the plugin directory, looking for the agent controller plugin
	hFind = FindFirstFile("..\\..\\..\\*", &FindFileData);
	if (hFind == INVALID_HANDLE_VALUE)  { } 
	else 
	{
	   val = FindFileData.cFileName;
	   
	   // If the plugin string is in the file list, construct the full path
	   if(val != NULL && strstr(val, CONFIG_JAR_PLUGIN_STRING) != NULL) {
		   sprintf(configJarLoct, "..\\..\\..\\%s\\config.jar", val);
	   } else {
			
			while( FindNextFile(hFind, &FindFileData) ) {
					val = FindFileData.cFileName;
					   
					if(val != NULL && strstr(val, CONFIG_JAR_PLUGIN_STRING) != NULL) {
						sprintf(configJarLoct, "..\\..\\..\\%s\\config.jar", val);
					}	   
			}
	   }
		
      FindClose(hFind);
   }

	if(configJarLoct[0] == 0) {
		tptp_free(configJarLoct);
		configJarLoct = NULL;
	}

    return configJarLoct;
    
}
#endif

#ifndef _WIN32

char * findConfigJarUnix() {
	char *configJarLoct;
    FILE *fp = NULL;
    DIR *dp;
    struct dirent *ep;
     
	configJarLoct = (char*)tptp_malloc(sizeof(char) * 4096);
    
	// Check whether config.jar file exists (this is the case in standalone AC)
    sprintf(configJarLoct, "../lib/config.jar");
    
    fp = fopen(configJarLoct, "r");
    
	// If it exists, return the location and close
    if(fp != NULL) {
            fclose(fp);
            return configJarLoct;
    }  
 		    
    configJarLoct[0] = 0;
    
	// Search through the plugin directory, looking for the agent controller plugin
    dp = opendir ("../../..");
    if(dp != NULL) {
          while (ep = readdir (dp)) {
                if(ep->d_name != NULL && strstr(ep->d_name, CONFIG_JAR_PLUGIN_STRING) != NULL) {
                      sprintf(configJarLoct, "../../../%s/config.jar", ep->d_name);
                      break;
                }
           }
          closedir (dp);
    }

	// If no results were found, return NULL
	if(configJarLoct[0] == 0) {
		tptp_free(configJarLoct);
		configJarLoct = NULL;
	}
    
    return configJarLoct;
    
}
#endif

char * findConfigJar() {
#ifdef _WIN32
	return findConfigJarWin();
#else
	return findConfigJarUnix();
#endif
}


int generateDefaultServiceConfigIfNecessary() {
	char * FILESEP = NULL;
	int isGenerationNeeded = 1;
	char serviceConfigFilename[2048];
	char javaCmdLineCall[4096];
	char * javaSetConfigFilename;
	char fileLine[2048];
	char *retval;
	char configDir[1024];
	int x = 0;
    FILE *fp = NULL;
	
	#ifdef _WIN32
           FILESEP = "\\";
	#else
           FILESEP = "/";
	#endif
	
	//242569
	
	if ( getConfigurationPath(configDir, 1024) >= 0 ) 
		sprintf(serviceConfigFilename, "%s%sserviceconfig.xml",configDir, FILESEP );
	else
		sprintf(serviceConfigFilename, "..%sconfig%sserviceconfig.xml", FILESEP, FILESEP);
	
	isGenerationNeeded = 1;
	
	// Check whether the serviceconfig.xml exists
    fp = fopen(serviceConfigFilename,"r");
    if( !fp ) {  isGenerationNeeded = 1; /* File doesn't exist */} else { 
        while(!feof(fp) && !ferror(fp)) {
			// File exists.... scan through the file looking for AgentControllerConfiguration
            retval = fgets(fileLine, 2040, fp);
            if(retval == NULL) break;
			fileLine[2041] = 0; // Null terminate the string (again) to prevent overflow
            
            if(fileLine != NULL && strstr(fileLine,"AgentControllerConfiguration") != NULL) {
				// If AgentControllerConfiguration XML entry is found, we don't need to generate the file
                 isGenerationNeeded = 0;
            }
        }
		fclose(fp);
    }
	
    if(isGenerationNeeded) {

		/*
		javaSetConfigFilename  = findConfigJar();
		if(javaSetConfigFilename == NULL) {
			return -19;
		}
		sprintf(javaCmdLineCall, "java -classpath %s org.eclipse.tptp.platform.agentcontroller.config.SetConfig -default", javaSetConfigFilename);
		*/

		// We can call the script file directly, in order to run SetConfig. (bug 263543)
#ifdef _WIN32
		sprintf(javaCmdLineCall, ".\\SetConfig.bat -default");
#else
		sprintf(javaCmdLineCall, "./SetConfig.sh -default");
#endif

		/* tptp_free(javaSetConfigFilename); */

		x = system(javaCmdLineCall);
        
        return x;
    }
    
    return 0;
}

