diff -r 000000000000 -r 044383f39525 sbsv2/raptor/util/talon/process_win.c --- /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 + +#include "talon_process.h" +#include "buffer.h" +#include + +#include +#include +#include +//#include + + +#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; +}