/**********************************************************************
Copyright (c) 2005, 2009 IBM Corporation and others.
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
$Id: launcher_md.c,v 1.2 2009/07/10 00:11:37 jwest Exp $
 
Contributors:
    IBM Corporation - initial implementation
**********************************************************************/
#ifdef MVS
#define _SHR_ENVIRON 1
#endif
#include "launcher_common.h"

#ifdef _DEBUG
extern FILE *trcFile;
#endif

/*
 * Generic launch function
 */
PID hc_launchProcess(char *exe, char *args, char *location, char *env, RA_HANDLE consoleChild[]) {
	int i; /* loop counter */
	PID pid; /* process ID of the launched process */

	if((exe == NULL) /*|| (location == NULL)*/)
		return -1;

#ifdef _WIN32
	pid = hc_launchProcess_WIN32(exe, args, location, env, consoleChild);
#elif MVS
	pid = hc_launchProcess_MVS(exe, args, location, env, consoleChild);
#elif __OS400__
	pid = hc_launchProcess_OS400(exe, args, location, env, consoleChild);
#else
	pid = hc_launchProcess_UNIX(exe, args, location, env, consoleChild);
#endif

	/* Close all the handles we no longer need in this address space */
	for(i = 0; i < 3; i++) {
		if(consoleChild[i]) {
			CLOSE_RA_HANDLE(consoleChild[i]);
		}
	}
	return pid;
}


/*
 * Windows launcher
 */
#ifdef _WIN32
PID hc_launchProcess_WIN32(char *exe, char *cmdline, char *location, char *env, RA_HANDLE consoleChild[]) {
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	DWORD error;

	/* Setup the STARTUP_INFO struct */
	ZeroMemory(&si,sizeof(si));
	si.cb=sizeof(si);
	if(consoleChild[0]) {
		si.dwFlags=STARTF_USESTDHANDLES;
		si.hStdInput=consoleChild[0];
	}
	if((consoleChild[1])) {
		si.dwFlags=STARTF_USESTDHANDLES;
		si.hStdOutput=consoleChild[1];
	}
	if(consoleChild[2]) {
		si.dwFlags=STARTF_USESTDHANDLES;
		si.hStdError=consoleChild[2];
	}

	//hc_logServiceMessage(__FILE__, __LINE__, RA_WARNING, "Attempting to launch process : %s  %s in location %s", exe->data, args->data, launchLocation);
	if(CreateProcess(exe,							/* Application to run */
					 cmdline,						/* The command line args */
					 NULL,							/* Default process security */
					 NULL,							/* Default thread security */
					 TRUE,							/* Inherit handles between children */
					 (consoleChild[0] || consoleChild[1] || consoleChild[2]) ? DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP|CREATE_SEPARATE_WOW_VDM : CREATE_NEW_CONSOLE,
					 env,							/* Environment */
					 location,						/* Where to run the child */
					 &si,							/* Startup info */
					 &pi)==0) {						/* Process info */

		error=GetLastError();
		//hc_logServiceMessage(__FILE__, __LINE__, RA_SEVERE, "Launching process failed, platform specific error is %d. Path=%s Process command line = %s", error, application->path.data,  cmdline);
		/* Set the process id to be zero as the process did not launch */
		pi.dwProcessId = -1;
		return pi.dwProcessId;
	}

	//hc_logServiceMessage(__FILE__, __LINE__, RA_WARNING, "Process launched: PID=%d", pi.dwProcessId);
	CloseHandle(pi.hThread);
	CloseHandle(pi.hProcess);
	ra_free(cmdline); /* Bug 103601 */

	return (PID)pi.dwProcessId;
}
#endif


/*
 * MVS launcher
 */
#ifdef MVS
PID hc_launchProcess_MVS(char *exe, char *cmdline, char *location, char *env, RA_HANDLE consoleChild[]) {
	PID childPID = 0;
	char *parsedArgs[MAX_ARGS];
	char *environment[MAX_ENV+1];
	int fd_map[3];
	int *fdmap;
	int fdcount = 3;
	char *current, *next;
	int i, count;
	char blank;

#ifdef MVS
#pragma convlit(suspend)
#endif
	blank = ' ';
#ifdef MVS
#pragma convlit(resume)
#endif

	if((cmdline == NULL) || (env == NULL)) {
		return -1;
	}

	current = cmdline;

	/* 178427 begin */
	/* Move past any leading blanks */
	while (*current == blank) {
		current++;
	}

	/* Parse out the args */
	for(i = 0; (next = (char*)strchr(current, blank)) != NULL; i++) {
		/* If this is a quoted arg we need to remove the quote and skip to the next quote */
		if(current[0]=='\"') {
			current++;
			next=(char*)strchr(current, '\"');
			if(next) {
				*next='\0';
			}
		}
		else {
			*next='\0';
		}

		#ifdef _DEBUG                   
		#pragma convlit(suspend)
		fprintf(trcFile, "hc_launchProcess: command arg=<%s>\n", current); 
		fflush(trcFile);        
		#pragma convlit(resume)
		#endif

		parsedArgs[i]=current;

		/* move past blanks */
		do {
			next++;
		} while (*next == blank);
		current=next;
	}

	/* check if there is another argument */
	if (strlen(current) > 0) {  /* 178427 - handles case of blank at end of cmd line */
		if(current[0]=='\"') { /* If this is a quoted arg we need to remove the quote and skip to the next quote */
			current++;
			next=(char*)strchr(current, '\"');
			if(next) {
				*next='\0';
			}
		}

		#ifdef _DEBUG                    
		#pragma convlit(suspend)
		fprintf(trcFile, "hc_launchProcess: command arg=<%s>\n", current); 
		fflush(trcFile);        
		#pragma convlit(resume)
		#endif

		parsedArgs[i]=current;
		/*	End the list of arguments */
		parsedArgs[i+1]=NULL;
	}
	else {
		/* else end the list of arguments */
		parsedArgs[i]=NULL;
	}

	/* 178427 end */
	/* Load the environment array */
	i=count=0;
	do {
		#ifdef _DEBUG                    
		#pragma convlit(suspend)
		fprintf(trcFile, "hc_launchProcess: i=%d  count=%d  env var=<%s>\n", i, count, &env[i]); 
		fflush(trcFile);        
		#pragma convlit(resume)
		#endif

		environment[count]=&env[i];
		i+=strlen(&env[i])+1;
		count++;
	} while(env[i]!='\0' && count<MAX_ENV);

	/* We have imposed a limit on the number of environment variables */
	if(count==MAX_ENV) {

		#ifdef _DEBUG                    
		#pragma convlit(suspend)
		fprintf(trcFile, "hc_launchProcess: Launching process failed, too many environment variables.  count=%d  but Maximum=%d\n", count, MAX_ENV); 
		fflush(trcFile);        
		#pragma convlit(resume)
		#endif

		exit(-1);
	}
	else {
		/* End the list of environment variables */
		environment[count]=NULL;
	}

   /* Set up file descriptor map for child process */
	if (consoleChild) {
		if ((fd_map[0]=dup(consoleChild[0])) < 0) {  /* child stdin is read end of pipe */
			//hc_logServiceMessage(__FILE__, __LINE__, RA_SEVERE, "Creating stdin for process failed.  Platform specific error is %d", errno);
		}
		if ((fd_map[1]=dup(consoleChild[1])) < 0) {  /* child stdout is write end of pipe */
			//hc_logServiceMessage(__FILE__, __LINE__, RA_SEVERE, "Creating stdout for process failed.  Platform specific error is %d", errno);
		}
		if ((fd_map[2]=dup(consoleChild[2])) < 0) {  /* child stderr is write end of pipe */
			//hc_logServiceMessage(__FILE__, __LINE__, RA_SEVERE, "Creating stderr for process failed.  Platform specific error is %d", errno);
		}
		fdmap = fd_map;
	}
	else {
		fdcount = 0;
		fdmap = NULL;
	}

	/* Change directory if one was specified */
	if (location != NULL) {
		#ifdef _DEBUG                    
		#pragma convlit(suspend)
		fprintf(trcFile, "hc_launchProcess: location=<%s>\n", location); 
		fflush(trcFile);        
		#pragma convlit(resume)
		#endif	

		if(chdir(location)) {

			#ifdef _DEBUG
			#pragma convlit(suspend)
			fprintf(trcFile, "hc_launchProcess: chdir to %s failed with error %d %s - returning 0 pid\n", location, errno, strerror(errno)); 
			fflush(trcFile);        
			#pragma convlit(resume)
			#endif

			childPID = 0;
			return childPID;
		}
	}

	/* Spawn the new process */
	if((childPID = spawnp(parsedArgs[0], fdcount, fdmap, NULL, parsedArgs, environment))<0) {
		#ifdef _DEBUG
		#pragma convlit(suspend)
		fprintf(trcFile, "hc_launchProcess: spawn failed with error %d %s - path=<%s>\n", errno, strerror(errno), parsedArgs[0]); 
		fflush(trcFile);        
		#pragma convlit(resume)
		#endif

		childPID = -1;  /* 179338 */
	}

	return childPID;
}
#endif


/*
 * OS/400 launcher
 */
#ifdef __OS400__
 /* Please refer to launcher_os400.c */
#endif


/*
 * UNIX launcher
 */
#if !defined (_WIN32) && !defined (MVS) && !defined (__OS400__)
PID hc_launchProcess_UNIX(char *exe, char *cmdline, char *location, char *env, RA_HANDLE consoleChild[]) {

	PID childPID = 0;

	childPID = fork();

	if(childPID == 0) {   /* Child process */
		char *parsedArgs[MAX_ARGS];
		char *environment[MAX_ENV+1];
		char *current, *next;
		int i, count;
		current=cmdline;

	     /* Move past any leading blanks */
		while (*current == ' ') {
			current++;
		}

		/* Parse out the args */
		for(i=0; (next=(char*)strchr(current, ' '))!=NULL; i++) {
			if(current[0]=='\"') { /* If this is a quoted arg we need to remove the quote and skip to the next quote */
				current++;
				next=(char*)strchr(current, '\"');
				if(next) {
					*next='\0';
				}
			}
			else {
				*next='\0';
			}

			parsedArgs[i]=current;

			do { /* move past blanks */
				next++;
			} while (*next == ' ');
			current=next;
		}

		/* check if there is another argument */
		if (strlen(current) > 0) { /* handles case of blank at end of cmd line */
			if(current[0]=='\"') { /* If this is a quoted arg we need to remove the quote and skip to the next quote */
				current++;
				next=(char*)strchr(current, '\"');
				if(next) {
					*next='\0';
				}
			}
			parsedArgs[i]=current;
			parsedArgs[i+1]='\0';
		}

		else { /* Else end the list of arguments */
			parsedArgs[i]='\0';
		}

		/* Load the environment array */
		i=count=0;
		do{
			environment[count]=&env[i];
			i+=strlen(&env[i])+1;
			count++;
		} while(env[i]!='\0' && count<MAX_ENV);

		if(count==MAX_ENV) {
			printf("Launching process failed, too many environment variables.  Maximum=%d\n", MAX_ENV);
			return (-1);
		}
		else {
			environment[count]='\0';
		}

		/* Change directory to the specified location if it exists */
        if(location) {
		    if(chdir(location)) {
			    printf("Launching process failed, cannot set location.  Platform specific error is %d. Location=%s\n", errno, location);
			    return (-1);
		    }
        }

		/* Setup the console */
		if(dup2(consoleChild[0], STDIN_FILENO) < 0) {
			printf("Creating stdin for process failed.  Platform specific error is %d\n", errno);
		}
		if(dup2(STDOUT_FILENO, consoleChild[1]) < 0) {
			printf("Creating stdout for process failed.  Platform specific error is %d\n", errno);
		}
		if(dup2(STDERR_FILENO, consoleChild[2]) < 0) {
			printf("Creating stderr for process failed.  Platform specific error is %d\n", errno);
		}

		/* Exec the target process */
		if(execve(parsedArgs[0], parsedArgs, environment)<0) {
//			printf("Launching process failed, platform specific error is %d. Process command line = %s ... %s\n", errno, cmdline, strerror(errno) );
			printf("Launching process failed, platform specific error is %d. Process command line = %s\n", errno, cmdline);
			return (-1);	/* exec failed */
		}
	}
	else { /* parent process */
		return childPID;
	}
}
#endif


/*******************************************************************************
 *                                                                             *
 * Environment                                                                 *
 *                                                                             *
 ******************************************************************************/


/*
 * Get the current system environment
 */
void* hc_getEnvironment() {

#ifdef _WIN32
	return GetEnvironmentStrings(); /* Return a string of environment variables */
#else

	#ifdef _DEBUG
	#ifdef MVS                    
	#pragma convlit(suspend)
	#endif
	fprintf(trcFile, "hc_getEnvironment: environ=%p\n", environ); 
	fflush(trcFile);
	#ifdef MVS        
	#pragma convlit(resume)
	#endif
	#endif

	return (void*)environ;
#endif
}

/*
 * Get the environment variable at the specified offset in the specified environment 
 * and returns the offset of the next environment variable.
 * Input:
 *    env    - environment  
 *    offset - offset of variable in the environment
 *
 * Output:
 *    envvarName  - ptr to environment variable name
 *    envvarValue - ptr to environment variable value
 *
 * Returns - offset of next environment variable in the specified environment 
 *         - -1 if no more environment variables
 */

int hc_getEnvironmentVariable(void *env, int offset, char **envvarName, char **envvarValue) {
	int nextOffset, nameLen, valueLen;
	char *tmp;
	char *envvar;

#ifdef _WIN32
	char *sysEnv = (char *)env;

	#ifdef _DEBUG
	fprintf(trcFile, "hc_getEnvironmentVariable: offset=%d  sysEnv=%p\n", offset, sysEnv); 
	fflush(trcFile);
	#endif

	envvar = sysEnv+offset;

	#ifdef _DEBUG
	fprintf(trcFile, "hc_getEnvironmentVariable: envvar = <%s>\n", envvar); 
	fflush(trcFile);
	#endif

	/* Get the name-value pairs - look for = */
	tmp = strchr(envvar, '=');

	/* If we don't have an = then return the whole string for the name and an empty string for the value */
	if (tmp == NULL) {
		nameLen = strlen(envvar);
		valueLen = 0;
	}

	/* Check if the first character is = (on Windows this is the case for some variables) */
	else if (tmp-envvar == 0) {
		/* check if there is another =                              
		 * If there is another = Then use that one                    
		 * If there isn't another = then use the whole string for the name and make the value an empty string 
		 */
		tmp = strchr(envvar+1, '=');

		if (tmp == NULL) {
			nameLen = strlen(envvar);
			valueLen = 0;
		}
		else {
			nameLen = tmp-envvar;
			valueLen = strlen(tmp)-1;
		}

	}
	else {
		nameLen = tmp-envvar;
		valueLen = strlen(tmp)-1;
	}

	*envvarName = (char *)malloc(nameLen+1);
	*envvarValue = (char *)malloc(valueLen+1);
	memcpy(*envvarName, envvar, nameLen);
	*(*envvarName+nameLen) = '\0';
	if ((tmp == NULL) || (valueLen == 0)) { /* Bug 68899 */
		**envvarValue = '\0';
	}
	else {
		strcpy(*envvarValue, tmp+1);
	}


	#ifdef _DEBUG
	fprintf(trcFile, "hc_getEnvironmentVariable: envvarName=%p <%s> envvarValue=%p <%s>\n", *envvarName, *envvarName, *envvarValue, *envvarValue); 
	fflush(trcFile);
	#endif

	/* Get offset to next environment variable */
	nextOffset = offset + strlen(envvar) + 1;

	if (sysEnv[nextOffset] == '\0') {
		nextOffset = -1;
	}

#else  /* Non-Windows Platforms */
	char *n_envvarName; /* native encoded name */
	char *n_envvarValue; /* native encoded value */
	char **sysEnv = (char **)env;
	size_t rc_n;
	size_t rc_v;

#ifdef MVS                    
	#pragma convlit(suspend)
#endif

	#ifdef _DEBUG
	fprintf(trcFile, "hc_getEnvironmentVariable: offset=%d  sysEnv=%p\n", offset, sysEnv); 
	fflush(trcFile);
	#endif

	envvar = sysEnv[offset];

	#ifdef _DEBUG
	fprintf(trcFile, "hc_getEnvironmentVariable: envvar = <%s>\n", envvar); 
	fflush(trcFile);
	#endif

	/* Get the name-value pairs - look for = */
	tmp = strchr(envvar, '=');

	/* If we don't have an = then return the whole string for the name and an empty string for the value */
	if (tmp == NULL) {
		nameLen = strlen(envvar);
		valueLen = 0;
	}
	else {
		nameLen = tmp-envvar;
		valueLen = strlen(tmp)-1;
	}

	n_envvarName = (char *)malloc(nameLen+1);
	n_envvarValue = (char *)malloc(valueLen+1);
	memcpy(n_envvarName, envvar, nameLen);
	*(n_envvarName+nameLen) = '\0';
	if ((tmp == NULL) || (valueLen == 0)) { /* Bug 68899 */
		*n_envvarValue = '\0';
	}
	else {
		strcpy(n_envvarValue, tmp+1);
	}

	#ifdef _DEBUG
	fprintf(trcFile, "hc_getEnvironmentVariable: envvarName=%p <%s> envvarValue=%p <%s>\n", n_envvarName, n_envvarName, n_envvarValue, n_envvarValue); 
	fflush(trcFile);
	#endif

	/* Convert the string from native to UTF-8 */
	rc_n = native2unicode(envvarName, n_envvarName, nameLen + 1);
	rc_v = native2unicode(envvarValue, n_envvarValue, valueLen + 1);

	free(n_envvarName);
	free(n_envvarValue);

	/* Get offset to next environment variable */
	nextOffset = offset + 1;

	/* If we are at the end of the environment variable array then signal it by returning -1 */
	if (sysEnv[nextOffset] == NULL) {
		nextOffset = -1;
	}

	if((rc_n == 0) || (rc_v == 0)) {
		*envvarName = NULL;
		*envvarValue = NULL;
	}
#endif

	return nextOffset;

}

/*******************************************************************************
 *                                                                             *
 * Process handling                                                            *
 *                                                                             *
 ******************************************************************************/

/*
 * Get the process state
 * Please refer to IExecutionComponent definition
 */
int hc_getProcessStatus(PID pid) {
#ifdef _WIN32
	HANDLE hProcess;
	DWORD exitCode;

	hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
	if (hProcess) {
		GetExitCodeProcess(hProcess, &exitCode);
		CloseHandle(hProcess); 
	}
	else {
		return org_eclipse_hyades_execution_core_IExecutionComponent_DEAD;
	}

	if(exitCode == STILL_ACTIVE) {
		return org_eclipse_hyades_execution_core_IExecutionComponent_READY;
	}
	else {
		return org_eclipse_hyades_execution_core_IExecutionComponent_DEAD;
	}
#else
	int status = 0;

	pid = waitpid(pid, &status, WNOHANG | WUNTRACED);
	if(pid == 0) {
		return org_eclipse_hyades_execution_core_IExecutionComponent_READY;
	}
	else {
		return org_eclipse_hyades_execution_core_IExecutionComponent_DEAD;
	}
#endif
}

/*
 * Kill the process specified by the process id
 */
void hc_killProcess(int pid) {
#ifdef _WIN32
	HANDLE hProcess;

	hProcess=OpenProcess(PROCESS_TERMINATE, FALSE, pid);
	if(hProcess!=NULL) {
		TerminateProcess(hProcess, 0);
	}
#else
	kill(pid, SIGKILL);
#endif

	return;
}


/*******************************************************************************
 *                                                                             *
 * File I/O                                                                    *
 *                                                                             *
 ******************************************************************************/

/*
 * Read data from file descriptor
 */
int hc_readFile(RA_HANDLE fd, char* buffer, int count) {
	int rc;
#ifdef _WIN32
	ReadFile(fd, buffer, count, &rc, NULL);
#else
	rc = read(fd, buffer, count);
#endif
	return rc;
}


/*
 * Write a single character to the file descriptor
 */
int hc_writeFileChar(RA_HANDLE fd, char c) {
	int rc;
#ifdef _WIN32
	WriteFile(fd, &c, 1, &rc, NULL);
#elif defined (__OS400__)
	rc = write(fd, as400_atoe(&c), 1);
#elif defined (MVS)
	char c2[2];
	c2[0] = c;
	c2[1] = '\0';
	__atoe(c2);
	rc = write(fd, c2, 1);
#else
	rc = write(fd, &c, 1);
#endif
	return rc;
}
