123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /*
- * Copyright (c) 2009-2011 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)
- {
- 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;
- 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 = strlen(executable) + 1;
- int i = 1;
- while (args[i] != NULL)
- {
- len += strlen(args[i++]) + 1;
- }
- len+=2;
- commandline = malloc(len*2);
- if (! commandline)
- RETURN(p);
- commandline[0] = '\0';
- strcat(commandline, executable);
- strcat(commandline, " ");
- i = 1;
- 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 executable:\n%s \n", executable));
- DEBUG(("process commandline:\n%s \n", commandline));
- DEBUG(("\n"));
- createproc_success = CreateProcess(NULL,
- commandline, // command line, starts with executable, possibly fully-pathed
- 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)
- {
- DWORD last_error = GetLastError();
- LPVOID msg_buff;
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- last_error,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &msg_buff,
- 0, NULL );
- DEBUG(("CreateProcess failed (error code %d):\n%s\n",
- last_error, msg_buff));
- LocalFree(msg_buff);
- p->causeofdeath = PROC_SOMEODDDEATH;
- RETURN(p);
- }
- 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;
- }
|