sbsv2/raptor/util/talon/process_win.c
changeset 0 044383f39525
child 3 e1eecf4d390d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sbsv2/raptor/util/talon/process_win.c	Tue Oct 27 16:36:35 2009 +0000
@@ -0,0 +1,406 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+*
+*/
+
+
+
+#include <unistd.h>
+
+#include "talon_process.h"
+#include "buffer.h"
+#include <errno.h>
+
+#include <windows.h> 
+#include <tchar.h>
+#include <stdio.h> 
+//#include <strsafe.h>
+
+
+#include "log.h"
+
+#define RETURN(x) { retval=x; goto cleanup; }
+#define CLEANUP() cleanup:
+
+#define READSIZE 4096
+
+typedef struct ReadOpStruct {
+	HANDLE semaphore;
+	HANDLE thread;
+	DWORD timeout;
+	DWORD error;
+	HANDLE file;
+	BOOL success;
+	char *space;
+	DWORD nbytes;
+	int id;
+	struct ReadOpStruct *next;
+} ReadOp;
+
+typedef struct {
+	ReadOp *first;
+	ReadOp *last;
+	HANDLE semaphore;
+} ReadOpQ;
+
+proc *process_new(void)
+{
+	proc *p = malloc(sizeof(proc));
+	p->output = buffer_new();
+	if (!p->output)
+	{
+		free(p);
+		return  NULL;
+	}
+	p->starttime = 0;
+	p->endtime = 0;
+	p->returncode = 1;
+	p->pid = 0;
+	p->causeofdeath = PROC_NORMALDEATH;
+
+	return p;
+}
+
+#define TALONMAXERRSTR 1024
+
+void printlasterror(void)
+{
+	LPTSTR msg;
+	DWORD err = GetLastError();
+	char buf[1024];
+
+	msg=buf;
+	
+	DEBUG(("error %d\n",err));
+	FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
+	  FORMAT_MESSAGE_FROM_SYSTEM |
+	  FORMAT_MESSAGE_IGNORE_INSERTS, 
+	  NULL, 		// lpSource
+	  err, 	// dwMessageId,
+	  0,
+	  //MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), 			// dwLanguageId,
+	  msg,
+	  0,
+	  NULL
+	);
+
+	DEBUG(("%s\n",msg));
+	//LocalFree(msg);
+}
+
+typedef struct 
+{
+	HANDLE read, write;
+} tl_stream;
+
+
+/* Because windows is d**b, there is no way to avoid blocking on an anonymous
+ * pipe.  We can't use CancelIO  to stop reads since that's only in newer
+ * versions of Win***ws.  So what we are left with is putting the read operation
+ * into a thread, timing out in the main body and ignoring this thread if we
+ * feel we have to.
+ * */
+
+
+DWORD readpipe_thread(void *param)
+{
+	ReadOpQ *io_ops = (ReadOpQ *)param;
+	ReadOp *iopipe_op;
+	/* have our own buffer since we don't want to risk that the
+	 * caller's buffer might have disappeared by the time
+	 * our readfile unblocks.
+	 */
+
+	while (1)
+	{
+		DWORD  waitres = WaitForSingleObject(io_ops->semaphore, INFINITE);
+		iopipe_op = io_ops->last;
+
+		DEBUG(("readpipe_thread: pre-ReadFile%d: %d \n", iopipe_op->id, iopipe_op->nbytes));
+		iopipe_op->success = ReadFile(iopipe_op->file, iopipe_op->space, iopipe_op->nbytes, &iopipe_op->nbytes, NULL);
+		iopipe_op->error = GetLastError();
+		
+		DEBUG(("readpipe_thread: post-ReadFile%d: %d read, err %d\n", iopipe_op->id, iopipe_op->nbytes,iopipe_op->error));
+		ReleaseSemaphore(iopipe_op->semaphore, 1, NULL);
+	}
+}
+
+proc *process_run(char executable[], char *args[], int timeout)
+{
+	proc *retval = NULL;
+	char *text;
+	int status;
+	tl_stream stdout_p;
+	tl_stream stdin_p;
+	SECURITY_ATTRIBUTES saAttr; 
+	PROCESS_INFORMATION pi;
+	STARTUPINFO si;
+	BOOL createproc_success = FALSE; 
+	BOOL timedout = FALSE; 
+	TCHAR *commandline = NULL;
+
+	proc *p = process_new();
+
+	if (p == NULL)
+		return NULL;
+
+	/* Make sure pipe handles are inherited */
+	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
+	saAttr.bInheritHandle = TRUE; 
+	saAttr.lpSecurityDescriptor = NULL; 
+	
+	p->causeofdeath = PROC_PIPECREATE;
+
+	DEBUG(("making pipes \n"));
+	/* Child's Stdout */
+	if ( ! CreatePipe(&stdout_p.read, &stdout_p.write, &saAttr, 1) ) 
+	{
+		printlasterror();
+		RETURN(p);
+	}
+	DEBUG(("stdout done \n"));
+	
+	/* read handle to the pipe for STDOUT is not inherited */
+	if ( ! SetHandleInformation(stdout_p.read, HANDLE_FLAG_INHERIT, 0) )
+	{
+		printlasterror();
+		RETURN(p); 
+	}
+	DEBUG(("stdout noinherit \n"));
+	
+	/* a pipe for the child process's STDIN */
+	if ( ! CreatePipe(&stdin_p.read, &stdin_p.write, &saAttr, 0) ) 
+	{
+		printlasterror();
+		RETURN(p); 
+	}
+	DEBUG(("stdin done \n"));
+	
+	/*  write handle to the pipe for STDIN not inherited */
+	if ( ! SetHandleInformation(stdin_p.read, HANDLE_FLAG_INHERIT, 0) )
+	{
+		printlasterror();
+		RETURN(p); 
+	}
+	DEBUG(("pipes done \n"));
+	
+	
+	p->causeofdeath = PROC_START;
+
+	ZeroMemory( &si, sizeof(STARTUPINFO) );
+	ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
+	
+	si.cb = sizeof(STARTUPINFO); 
+	si.hStdError = stdout_p.write;
+	si.hStdOutput = stdout_p.write;
+	/*
+	  Rather than use the stdin pipe, which would be
+       		  si.hStdInput = stdin_p.read;
+	  Pass on talon's own standard input to the child process
+	  This helps with programs like xcopy which demand that
+	  they are attached to a console and not just any type of
+	  input file.
+	 */
+	si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+	si.dwFlags |= STARTF_USESTDHANDLES;
+
+	
+	DEBUG(("pre commandline \n"));
+	/* create the commandline string */
+	int len = 0;
+	int i = 0;
+	while (args[i] != NULL)
+	{
+		len += strlen(args[i++]) + 1;
+	}
+	len+=2;
+
+	commandline = malloc(len*2);
+	if (! commandline) 
+		RETURN(p);
+	commandline[0] = '\0';
+
+	i = 0;
+	while (args[i] != NULL)
+	{
+		strcat(commandline, args[i]);
+		strcat(commandline, " ");
+		i++;
+	}
+
+
+	/* Get the read thread ready to go before creating
+	 * the process.
+	 */
+	ReadOpQ *ropq  = malloc(sizeof(ReadOpQ));
+
+	ropq->first=NULL;
+	ropq->last=NULL;
+	ropq->semaphore = CreateSemaphore(NULL, 0, 1, NULL);
+	DEBUG(("Creating read thread. \n"));
+
+	DWORD readpipe_threadid;
+	HANDLE h_readpipe_thread = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE) readpipe_thread, (void*)ropq, 0, &readpipe_threadid);
+
+	/* ready to run the process */
+ 
+	DEBUG(("process commandline:\n%s \n", commandline));
+	DEBUG(("\n"));
+	createproc_success = CreateProcess(executable, 
+	   commandline,     // command line 
+	   NULL,          // process security attributes 
+	   NULL,          // primary thread security attributes 
+	   TRUE,          // handles are inherited 
+	   0,             // creation flags 
+	   NULL,          // use parent's environment 
+	   NULL,          // use parent's current directory 
+	   &si,  // STARTUPINFO pointer 
+	   &pi);  // receives PROCESS_INFORMATION 
+
+	if (! createproc_success)
+	{
+		DEBUG(("Createprocess failed. \n"));
+		p->causeofdeath = PROC_SOMEODDDEATH;
+		RETURN(p);
+	}
+
+	int have_status = 0;
+
+
+	DEBUG(("Closing Handles. \n"));
+	if (!CloseHandle(stdout_p.write)) 
+		RETURN(p);
+	if (!CloseHandle(stdin_p.read)) 
+		RETURN(p);
+
+	DEBUG(("Closed Handles. \n"));
+
+
+	static int id=0;
+	do
+	{
+		char *space = buffer_makespace(p->output, READSIZE);
+
+		DWORD waitres;
+		ReadOp *iopipe_op = malloc(sizeof(ReadOp));
+		iopipe_op->semaphore = CreateSemaphore(NULL, 0, 1, NULL);
+		iopipe_op->thread =  h_readpipe_thread;
+		iopipe_op->timeout = timeout;
+		iopipe_op->file = stdout_p.read;
+		iopipe_op->space = malloc(READSIZE);
+		iopipe_op->id = id++;
+		iopipe_op->nbytes = READSIZE;
+		iopipe_op->next = NULL;
+
+		if (!ropq->first)
+		{
+			ropq->first = iopipe_op;
+			ropq->last = iopipe_op;
+		} else {
+			ropq->last->next = iopipe_op;
+			ropq->last = iopipe_op;
+		}
+
+		ReleaseSemaphore(ropq->semaphore, 1, NULL);
+
+		DEBUG(("waiting for read %d\n", timeout));
+		waitres = WaitForSingleObject(iopipe_op->semaphore, timeout);
+		DEBUG(("read wait finished result= %d\n", waitres));
+
+		if (waitres != WAIT_OBJECT_0)
+		{
+			DEBUG(("timeout \n"));
+			timedout = TRUE;
+			break;
+		}
+		else
+		{
+			DEBUG(("read signalled: nbytes: %d \n", iopipe_op->nbytes));
+			if (iopipe_op->nbytes <= 0)
+			{
+				break;
+			}
+			memcpy(space, iopipe_op->space, iopipe_op->nbytes);	
+			buffer_usespace(p->output, iopipe_op->nbytes);	
+			DEBUG(("buffer took on nbytes: %d \n", iopipe_op->nbytes));
+		}
+	}
+	while (1);
+
+	if (timedout == FALSE) 
+	{
+		DEBUG(("Wait for process exit\n"));
+		// Wait until child process exits.
+		WaitForSingleObject(pi.hProcess, INFINITE);
+		DEBUG(("Process exited\n"));
+
+		DWORD exitcode;
+
+		if (GetExitCodeProcess(pi.hProcess, &exitcode))
+		{
+			p->causeofdeath = PROC_NORMALDEATH;
+			p->returncode = exitcode;
+			DEBUG(("process exited normally = %d:\n", p->returncode));
+			RETURN(p);	
+		} else {
+			p->causeofdeath = PROC_SOMEODDDEATH;
+			p->returncode = 128;
+			DEBUG(("process terminated \n"));
+			RETURN(p);	
+		}
+	} else {
+		TerminateProcess(pi.hProcess,1);
+		p->causeofdeath = PROC_TIMEOUTDEATH;
+		p->returncode = 128;
+		DEBUG(("process timedout \n"));
+		RETURN(p);	
+	}
+
+	/* Clean up the read operation queue 
+	ReadOp *r = ropq.first;
+	do
+	{
+		CloseHandle(r->semaphore);
+		free(r->space);
+		free(r);
+		r = r->next;
+	} while (r != NULL); */
+
+	CLEANUP();
+	if (retval == NULL)
+	{
+		if (p)
+			process_free(&p);
+	}
+	if (commandline)
+		free(commandline);
+	
+	return retval;
+}
+
+void process_free(proc **pp)
+{
+	if (!pp)
+		return;
+	if (! *pp)
+		return;
+
+	if ((*pp)->output)
+		buffer_free(&((*pp)->output));
+	
+	free(*pp);
+
+	*pp = NULL;
+}