| /*------------------------------------------------------------------------- |
| * drawElements Utility Library |
| * ---------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Process abstraction. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "deProcess.h" |
| #include "deMemory.h" |
| #include "deString.h" |
| |
| #if (DE_OS == DE_OS_UNIX) || (DE_OS == DE_OS_OSX) || (DE_OS == DE_OS_IOS) || (DE_OS == DE_OS_ANDROID) || (DE_OS == DE_OS_SYMBIAN) || (DE_OS == DE_OS_QNX) |
| |
| #include "deCommandLine.h" |
| |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| |
| typedef enum ProcessState_e |
| { |
| PROCESSSTATE_NOT_STARTED = 0, |
| PROCESSSTATE_RUNNING, |
| PROCESSSTATE_FINISHED, |
| |
| PROCESSSTATE_LAST |
| } ProcessState; |
| |
| struct deProcess_s |
| { |
| ProcessState state; |
| int exitCode; |
| char* lastError; |
| |
| pid_t pid; |
| deFile* standardIn; |
| deFile* standardOut; |
| deFile* standardErr; |
| }; |
| |
| static void die (int statusPipe, const char* message) |
| { |
| size_t msgLen = strlen(message); |
| int res = 0; |
| |
| printf("Process launch failed: %s\n", message); |
| res = (int)write(statusPipe, message, msgLen+1); |
| DE_UNREF(res); /* No need to check result. */ |
| exit(-1); |
| } |
| |
| static void dieLastError (int statusPipe, const char* message) |
| { |
| char msgBuf[256]; |
| int lastErr = errno; |
| deSprintf(msgBuf, sizeof(msgBuf), "%s, error %d: %s", message, lastErr, strerror(lastErr)); |
| die(statusPipe, msgBuf); |
| } |
| |
| DE_INLINE deBool beginsWithPath (const char* fileName, const char* pathPrefix) |
| { |
| size_t pathLen = strlen(pathPrefix); |
| |
| /* Strip trailing / */ |
| while (pathLen > 0 && pathPrefix[pathLen-1] == '/') |
| pathLen -= 1; |
| |
| return pathLen > 0 && deMemoryEqual(fileName, pathPrefix, pathLen) && fileName[pathLen] == '/'; |
| } |
| |
| static void stripLeadingPath (char* fileName, const char* pathPrefix) |
| { |
| size_t pathLen = strlen(pathPrefix); |
| size_t fileNameLen = strlen(fileName); |
| |
| DE_ASSERT(beginsWithPath(fileName, pathPrefix)); |
| |
| /* Strip trailing / */ |
| while (pathLen > 0 && pathPrefix[pathLen-1] == '/') |
| pathLen -= 1; |
| |
| DE_ASSERT(pathLen > 0); |
| DE_ASSERT(fileName[pathLen] == '/'); |
| |
| memmove(&fileName[0], &fileName[0]+pathLen+1, fileNameLen-pathLen); |
| } |
| |
| /* Doesn't return on success. */ |
| static void execProcess (const char* commandLine, const char* workingDirectory, int statusPipe) |
| { |
| deCommandLine* cmdLine = deCommandLine_parse(commandLine); |
| char** argList = cmdLine ? (char**)deCalloc(sizeof(char*)*((size_t)cmdLine->numArgs+1)) : DE_NULL; |
| |
| if (!cmdLine || !argList) |
| die(statusPipe, "Command line parsing failed (out of memory)"); |
| |
| if (workingDirectory && chdir(workingDirectory) != 0) |
| dieLastError(statusPipe, "chdir() failed"); |
| |
| { |
| int argNdx; |
| for (argNdx = 0; argNdx < cmdLine->numArgs; argNdx++) |
| argList[argNdx] = cmdLine->args[argNdx]; |
| argList[argNdx] = DE_NULL; /* Terminate with 0. */ |
| } |
| |
| if (workingDirectory && beginsWithPath(argList[0], workingDirectory)) |
| stripLeadingPath(argList[0], workingDirectory); |
| |
| execv(argList[0], argList); |
| |
| /* Failed. */ |
| dieLastError(statusPipe, "execv() failed"); |
| } |
| |
| deProcess* deProcess_create (void) |
| { |
| deProcess* process = (deProcess*)deCalloc(sizeof(deProcess)); |
| if (!process) |
| return DE_FALSE; |
| |
| process->state = PROCESSSTATE_NOT_STARTED; |
| |
| return process; |
| } |
| |
| static void deProcess_cleanupHandles (deProcess* process) |
| { |
| if (process->standardIn) |
| deFile_destroy(process->standardIn); |
| |
| if (process->standardOut) |
| deFile_destroy(process->standardOut); |
| |
| if (process->standardErr) |
| deFile_destroy(process->standardErr); |
| |
| process->pid = 0; |
| process->standardIn = DE_NULL; |
| process->standardOut = DE_NULL; |
| process->standardErr = DE_NULL; |
| } |
| |
| void deProcess_destroy (deProcess* process) |
| { |
| /* Never leave child processes running. Otherwise we'll have zombies. */ |
| if (deProcess_isRunning(process)) |
| { |
| deProcess_kill(process); |
| deProcess_waitForFinish(process); |
| } |
| |
| deProcess_cleanupHandles(process); |
| deFree(process->lastError); |
| deFree(process); |
| } |
| |
| const char* deProcess_getLastError (const deProcess* process) |
| { |
| return process->lastError ? process->lastError : "No error"; |
| } |
| |
| int deProcess_getExitCode (const deProcess* process) |
| { |
| return process->exitCode; |
| } |
| |
| static deBool deProcess_setError (deProcess* process, const char* error) |
| { |
| if (process->lastError) |
| { |
| deFree(process->lastError); |
| process->lastError = DE_NULL; |
| } |
| |
| process->lastError = deStrdup(error); |
| return process->lastError != DE_NULL; |
| } |
| |
| static deBool deProcess_setErrorFromErrno (deProcess* process, const char* message) |
| { |
| char msgBuf[256]; |
| int lastErr = errno; |
| deSprintf(msgBuf, sizeof(msgBuf), "%s, error %d: %s", message, lastErr, strerror(lastErr)); |
| return deProcess_setError(process, message); |
| } |
| |
| static void closePipe (int p[2]) |
| { |
| if (p[0] >= 0) |
| close(p[0]); |
| if (p[1] >= 0) |
| close(p[1]); |
| } |
| |
| deBool deProcess_start (deProcess* process, const char* commandLine, const char* workingDirectory) |
| { |
| pid_t pid = 0; |
| int pipeIn[2] = { -1, -1 }; |
| int pipeOut[2] = { -1, -1 }; |
| int pipeErr[2] = { -1, -1 }; |
| int statusPipe[2] = { -1, -1 }; |
| |
| if (process->state == PROCESSSTATE_RUNNING) |
| { |
| deProcess_setError(process, "Process already running"); |
| return DE_FALSE; |
| } |
| else if (process->state == PROCESSSTATE_FINISHED) |
| { |
| deProcess_cleanupHandles(process); |
| process->state = PROCESSSTATE_NOT_STARTED; |
| } |
| |
| if (pipe(pipeIn) < 0 || pipe(pipeOut) < 0 || pipe(pipeErr) < 0 || pipe(statusPipe) < 0) |
| { |
| deProcess_setErrorFromErrno(process, "pipe() failed"); |
| |
| closePipe(pipeIn); |
| closePipe(pipeOut); |
| closePipe(pipeErr); |
| closePipe(statusPipe); |
| |
| return DE_FALSE; |
| } |
| |
| pid = fork(); |
| |
| if (pid < 0) |
| { |
| deProcess_setErrorFromErrno(process, "fork() failed"); |
| |
| closePipe(pipeIn); |
| closePipe(pipeOut); |
| closePipe(pipeErr); |
| closePipe(statusPipe); |
| |
| return DE_FALSE; |
| } |
| |
| if (pid == 0) |
| { |
| /* Child process. */ |
| |
| /* Close unused endpoints. */ |
| close(pipeIn[1]); |
| close(pipeOut[0]); |
| close(pipeErr[0]); |
| close(statusPipe[0]); |
| |
| /* Set status pipe to close on exec(). That way parent will know that exec() succeeded. */ |
| if (fcntl(statusPipe[1], F_SETFD, FD_CLOEXEC) != 0) |
| dieLastError(statusPipe[1], "Failed to set FD_CLOEXEC"); |
| |
| /* Map stdin. */ |
| if (pipeIn[0] != STDIN_FILENO && |
| dup2(pipeIn[0], STDIN_FILENO) != STDIN_FILENO) |
| dieLastError(statusPipe[1], "dup2() failed"); |
| close(pipeIn[0]); |
| |
| /* Stdout. */ |
| if (pipeOut[1] != STDOUT_FILENO && |
| dup2(pipeOut[1], STDOUT_FILENO) != STDOUT_FILENO) |
| dieLastError(statusPipe[1], "dup2() failed"); |
| close(pipeOut[1]); |
| |
| /* Stderr. */ |
| if (pipeErr[1] != STDERR_FILENO && |
| dup2(pipeErr[1], STDERR_FILENO) != STDERR_FILENO) |
| dieLastError(statusPipe[1], "dup2() failed"); |
| close(pipeErr[1]); |
| |
| /* Doesn't return. */ |
| execProcess(commandLine, workingDirectory, statusPipe[1]); |
| } |
| else |
| { |
| /* Parent process. */ |
| |
| /* Check status. */ |
| { |
| char errBuf[256]; |
| ssize_t result = 0; |
| |
| close(statusPipe[1]); |
| while ((result = read(statusPipe[0], errBuf, 1)) == -1) |
| if (errno != EAGAIN && errno != EINTR) break; |
| |
| if (result > 0) |
| { |
| int procStatus = 0; |
| |
| /* Read full error msg. */ |
| int errPos = 1; |
| while (errPos < DE_LENGTH_OF_ARRAY(errBuf)) |
| { |
| result = read(statusPipe[0], errBuf+errPos, 1); |
| if (result == -1) |
| break; /* Done. */ |
| |
| errPos += 1; |
| } |
| |
| /* Make sure str is null-terminated. */ |
| errBuf[errPos] = 0; |
| |
| /* Close handles. */ |
| close(statusPipe[0]); |
| closePipe(pipeIn); |
| closePipe(pipeOut); |
| closePipe(pipeErr); |
| |
| /* Run waitpid to clean up zombie. */ |
| waitpid(pid, &procStatus, 0); |
| |
| deProcess_setError(process, errBuf); |
| |
| return DE_FALSE; |
| } |
| |
| /* Status pipe is not needed. */ |
| close(statusPipe[0]); |
| } |
| |
| /* Set running state. */ |
| process->pid = pid; |
| process->state = PROCESSSTATE_RUNNING; |
| |
| /* Stdin, stdout. */ |
| close(pipeIn[0]); |
| close(pipeOut[1]); |
| close(pipeErr[1]); |
| |
| process->standardIn = deFile_createFromHandle((deUintptr)pipeIn[1]); |
| process->standardOut = deFile_createFromHandle((deUintptr)pipeOut[0]); |
| process->standardErr = deFile_createFromHandle((deUintptr)pipeErr[0]); |
| |
| if (!process->standardIn) |
| close(pipeIn[1]); |
| |
| if (!process->standardOut) |
| close(pipeOut[0]); |
| |
| if (!process->standardErr) |
| close(pipeErr[0]); |
| } |
| |
| return DE_TRUE; |
| } |
| |
| deBool deProcess_isRunning (deProcess* process) |
| { |
| if (process->state == PROCESSSTATE_RUNNING) |
| { |
| int status = 0; |
| |
| if (waitpid(process->pid, &status, WNOHANG) == 0) |
| return DE_TRUE; /* No status available. */ |
| |
| if (WIFEXITED(status) || WIFSIGNALED(status)) |
| { |
| /* Child has finished. */ |
| process->state = PROCESSSTATE_FINISHED; |
| return DE_FALSE; |
| } |
| else |
| return DE_TRUE; |
| } |
| else |
| return DE_FALSE; |
| } |
| |
| deBool deProcess_waitForFinish (deProcess* process) |
| { |
| int status = 0; |
| pid_t waitResult; |
| |
| if (process->state != PROCESSSTATE_RUNNING) |
| { |
| deProcess_setError(process, "Process is not running"); |
| return DE_FALSE; |
| } |
| |
| /* \note [pyry] HACK, apparently needed by some versions of OS X. */ |
| while ((waitResult = waitpid(process->pid, &status, 0)) != process->pid) |
| if (errno != ENOENT) break; |
| |
| if (waitResult != process->pid) |
| { |
| deProcess_setErrorFromErrno(process, "waitpid() failed"); |
| return DE_FALSE; /* waitpid() failed. */ |
| } |
| |
| if (!WIFEXITED(status) && !WIFSIGNALED(status)) |
| { |
| deProcess_setErrorFromErrno(process, "waitpid() failed"); |
| return DE_FALSE; /* Something strange happened. */ |
| } |
| |
| process->exitCode = WEXITSTATUS(status); |
| process->state = PROCESSSTATE_FINISHED; |
| return DE_TRUE; |
| } |
| |
| static deBool deProcess_sendSignal (deProcess* process, int sigNum) |
| { |
| if (process->state != PROCESSSTATE_RUNNING) |
| { |
| deProcess_setError(process, "Process is not running"); |
| return DE_FALSE; |
| } |
| |
| if (kill(process->pid, sigNum) == 0) |
| return DE_TRUE; |
| else |
| { |
| deProcess_setErrorFromErrno(process, "kill() failed"); |
| return DE_FALSE; |
| } |
| } |
| |
| deBool deProcess_terminate (deProcess* process) |
| { |
| return deProcess_sendSignal(process, SIGTERM); |
| } |
| |
| deBool deProcess_kill (deProcess* process) |
| { |
| return deProcess_sendSignal(process, SIGKILL); |
| } |
| |
| deFile* deProcess_getStdIn (deProcess* process) |
| { |
| return process->standardIn; |
| } |
| |
| deFile* deProcess_getStdOut (deProcess* process) |
| { |
| return process->standardOut; |
| } |
| |
| deFile* deProcess_getStdErr (deProcess* process) |
| { |
| return process->standardErr; |
| } |
| |
| deBool deProcess_closeStdIn (deProcess* process) |
| { |
| if (process->standardIn) |
| { |
| deFile_destroy(process->standardIn); |
| process->standardIn = DE_NULL; |
| return DE_TRUE; |
| } |
| else |
| return DE_FALSE; |
| } |
| |
| deBool deProcess_closeStdOut (deProcess* process) |
| { |
| if (process->standardOut) |
| { |
| deFile_destroy(process->standardOut); |
| process->standardOut = DE_NULL; |
| return DE_TRUE; |
| } |
| else |
| return DE_FALSE; |
| } |
| |
| deBool deProcess_closeStdErr (deProcess* process) |
| { |
| if (process->standardErr) |
| { |
| deFile_destroy(process->standardErr); |
| process->standardErr = DE_NULL; |
| return DE_TRUE; |
| } |
| else |
| return DE_FALSE; |
| } |
| |
| #elif (DE_OS == DE_OS_WIN32) |
| |
| #define VC_EXTRALEAN |
| #define WIN32_LEAN_AND_MEAN |
| #include <windows.h> |
| #include <strsafe.h> |
| |
| typedef enum ProcessState_e |
| { |
| PROCESSSTATE_NOT_STARTED = 0, |
| PROCESSSTATE_RUNNING, |
| PROCESSSTATE_FINISHED, |
| |
| PROCESSSTATE_LAST |
| } ProcessState; |
| |
| struct deProcess_s |
| { |
| ProcessState state; |
| char* lastError; |
| int exitCode; |
| |
| PROCESS_INFORMATION procInfo; |
| deFile* standardIn; |
| deFile* standardOut; |
| deFile* standardErr; |
| }; |
| |
| static deBool deProcess_setError (deProcess* process, const char* error) |
| { |
| if (process->lastError) |
| { |
| deFree(process->lastError); |
| process->lastError = DE_NULL; |
| } |
| |
| process->lastError = deStrdup(error); |
| return process->lastError != DE_NULL; |
| } |
| |
| static deBool deProcess_setErrorFromWin32 (deProcess* process, const char* msg) |
| { |
| DWORD error = GetLastError(); |
| LPSTR msgBuf; |
| char errBuf[256]; |
| |
| #if defined(UNICODE) |
| # error Unicode not supported. |
| #endif |
| |
| if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, |
| NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msgBuf, 0, DE_NULL) > 0) |
| { |
| deSprintf(errBuf, sizeof(errBuf), "%s, error %d: %s", msg, error, msgBuf); |
| LocalFree(msgBuf); |
| return deProcess_setError(process, errBuf); |
| } |
| else |
| { |
| /* Failed to get error str. */ |
| deSprintf(errBuf, sizeof(errBuf), "%s, error %d", msg, error); |
| return deProcess_setError(process, errBuf); |
| } |
| } |
| |
| deProcess* deProcess_create (void) |
| { |
| deProcess* process = (deProcess*)deCalloc(sizeof(deProcess)); |
| if (!process) |
| return DE_NULL; |
| |
| process->state = PROCESSSTATE_NOT_STARTED; |
| |
| return process; |
| } |
| |
| void deProcess_cleanupHandles (deProcess* process) |
| { |
| DE_ASSERT(!deProcess_isRunning(process)); |
| |
| if (process->standardErr) |
| deFile_destroy(process->standardErr); |
| |
| if (process->standardOut) |
| deFile_destroy(process->standardOut); |
| |
| if (process->standardIn) |
| deFile_destroy(process->standardIn); |
| |
| if (process->procInfo.hProcess) |
| CloseHandle(process->procInfo.hProcess); |
| |
| if (process->procInfo.hThread) |
| CloseHandle(process->procInfo.hThread); |
| |
| process->standardErr = DE_NULL; |
| process->standardOut = DE_NULL; |
| process->standardIn = DE_NULL; |
| process->procInfo.hProcess = DE_NULL; |
| process->procInfo.hThread = DE_NULL; |
| } |
| |
| void deProcess_destroy (deProcess* process) |
| { |
| if (deProcess_isRunning(process)) |
| { |
| deProcess_kill(process); |
| deProcess_waitForFinish(process); |
| } |
| |
| deProcess_cleanupHandles(process); |
| deFree(process->lastError); |
| deFree(process); |
| } |
| |
| const char* deProcess_getLastError (const deProcess* process) |
| { |
| return process->lastError ? process->lastError : "No error"; |
| } |
| |
| int deProcess_getExitCode (const deProcess* process) |
| { |
| return process->exitCode; |
| } |
| |
| deBool deProcess_start (deProcess* process, const char* commandLine, const char* workingDirectory) |
| { |
| SECURITY_ATTRIBUTES securityAttr; |
| STARTUPINFO startInfo; |
| |
| /* Pipes. */ |
| HANDLE stdInRead = DE_NULL; |
| HANDLE stdInWrite = DE_NULL; |
| HANDLE stdOutRead = DE_NULL; |
| HANDLE stdOutWrite = DE_NULL; |
| HANDLE stdErrRead = DE_NULL; |
| HANDLE stdErrWrite = DE_NULL; |
| |
| if (process->state == PROCESSSTATE_RUNNING) |
| { |
| deProcess_setError(process, "Process already running"); |
| return DE_FALSE; |
| } |
| else if (process->state == PROCESSSTATE_FINISHED) |
| { |
| /* Process finished, clean up old cruft. */ |
| deProcess_cleanupHandles(process); |
| process->state = PROCESSSTATE_NOT_STARTED; |
| } |
| |
| deMemset(&startInfo, 0, sizeof(startInfo)); |
| deMemset(&securityAttr, 0, sizeof(securityAttr)); |
| |
| /* Security attributes for inheriting handle. */ |
| securityAttr.nLength = sizeof(SECURITY_ATTRIBUTES); |
| securityAttr.bInheritHandle = TRUE; |
| securityAttr.lpSecurityDescriptor = DE_NULL; |
| |
| /* Create pipes. \todo [2011-10-03 pyry] Clean up handles on error! */ |
| if (!CreatePipe(&stdInRead, &stdInWrite, &securityAttr, 0) || |
| !SetHandleInformation(stdInWrite, HANDLE_FLAG_INHERIT, 0)) |
| { |
| deProcess_setErrorFromWin32(process, "CreatePipe() failed"); |
| CloseHandle(stdInRead); |
| CloseHandle(stdInWrite); |
| return DE_FALSE; |
| } |
| |
| if (!CreatePipe(&stdOutRead, &stdOutWrite, &securityAttr, 0) || |
| !SetHandleInformation(stdOutRead, HANDLE_FLAG_INHERIT, 0)) |
| { |
| deProcess_setErrorFromWin32(process, "CreatePipe() failed"); |
| CloseHandle(stdInRead); |
| CloseHandle(stdInWrite); |
| CloseHandle(stdOutRead); |
| CloseHandle(stdOutWrite); |
| return DE_FALSE; |
| } |
| |
| if (!CreatePipe(&stdErrRead, &stdErrWrite, &securityAttr, 0) || |
| !SetHandleInformation(stdErrRead, HANDLE_FLAG_INHERIT, 0)) |
| { |
| deProcess_setErrorFromWin32(process, "CreatePipe() failed"); |
| CloseHandle(stdInRead); |
| CloseHandle(stdInWrite); |
| CloseHandle(stdOutRead); |
| CloseHandle(stdOutWrite); |
| CloseHandle(stdErrRead); |
| CloseHandle(stdErrWrite); |
| return DE_FALSE; |
| } |
| |
| /* Setup startup info. */ |
| startInfo.cb = sizeof(startInfo); |
| startInfo.hStdError = stdErrWrite; |
| startInfo.hStdOutput = stdOutWrite; |
| startInfo.hStdInput = stdInRead; |
| startInfo.dwFlags |= STARTF_USESTDHANDLES; |
| |
| if (!CreateProcess(DE_NULL, (LPTSTR)commandLine, DE_NULL, DE_NULL, TRUE /* inherit handles */, 0, DE_NULL, workingDirectory, &startInfo, &process->procInfo)) |
| { |
| /* Store error info. */ |
| deProcess_setErrorFromWin32(process, "CreateProcess() failed"); |
| |
| /* Close all handles. */ |
| CloseHandle(stdInRead); |
| CloseHandle(stdInWrite); |
| CloseHandle(stdOutRead); |
| CloseHandle(stdOutWrite); |
| CloseHandle(stdErrRead); |
| CloseHandle(stdErrWrite); |
| |
| return DE_FALSE; |
| } |
| |
| process->state = PROCESSSTATE_RUNNING; |
| |
| /* Close our ends of handles.*/ |
| CloseHandle(stdErrWrite); |
| CloseHandle(stdOutWrite); |
| CloseHandle(stdInRead); |
| |
| /* Construct stdio file objects \note May fail, not detected. */ |
| process->standardIn = deFile_createFromHandle((deUintptr)stdInWrite); |
| process->standardOut = deFile_createFromHandle((deUintptr)stdOutRead); |
| process->standardErr = deFile_createFromHandle((deUintptr)stdErrRead); |
| |
| return DE_TRUE; |
| } |
| |
| deBool deProcess_isRunning (deProcess* process) |
| { |
| if (process->state == PROCESSSTATE_RUNNING) |
| { |
| int exitCode; |
| BOOL result = GetExitCodeProcess(process->procInfo.hProcess, (LPDWORD)&exitCode); |
| |
| if (result != TRUE) |
| { |
| deProcess_setErrorFromWin32(process, "GetExitCodeProcess() failed"); |
| return DE_FALSE; |
| } |
| |
| if (exitCode == STILL_ACTIVE) |
| return DE_TRUE; |
| else |
| { |
| /* Done. */ |
| process->exitCode = exitCode; |
| process->state = PROCESSSTATE_FINISHED; |
| return DE_FALSE; |
| } |
| } |
| else |
| return DE_FALSE; |
| } |
| |
| deBool deProcess_waitForFinish (deProcess* process) |
| { |
| if (process->state == PROCESSSTATE_RUNNING) |
| { |
| if (WaitForSingleObject(process->procInfo.hProcess, INFINITE) != WAIT_OBJECT_0) |
| { |
| deProcess_setErrorFromWin32(process, "WaitForSingleObject() failed"); |
| return DE_FALSE; |
| } |
| return !deProcess_isRunning(process); |
| } |
| else |
| { |
| deProcess_setError(process, "Process is not running"); |
| return DE_FALSE; |
| } |
| } |
| |
| static deBool stopProcess (deProcess* process, deBool kill) |
| { |
| if (process->state == PROCESSSTATE_RUNNING) |
| { |
| if (!TerminateProcess(process->procInfo.hProcess, kill ? -1 : 0)) |
| { |
| deProcess_setErrorFromWin32(process, "TerminateProcess() failed"); |
| return DE_FALSE; |
| } |
| else |
| return DE_TRUE; |
| } |
| else |
| { |
| deProcess_setError(process, "Process is not running"); |
| return DE_FALSE; |
| } |
| } |
| |
| deBool deProcess_terminate (deProcess* process) |
| { |
| return stopProcess(process, DE_FALSE); |
| } |
| |
| deBool deProcess_kill (deProcess* process) |
| { |
| return stopProcess(process, DE_TRUE); |
| } |
| |
| deFile* deProcess_getStdIn (deProcess* process) |
| { |
| return process->standardIn; |
| } |
| |
| deFile* deProcess_getStdOut (deProcess* process) |
| { |
| return process->standardOut; |
| } |
| |
| deFile* deProcess_getStdErr (deProcess* process) |
| { |
| return process->standardErr; |
| } |
| |
| deBool deProcess_closeStdIn (deProcess* process) |
| { |
| if (process->standardIn) |
| { |
| deFile_destroy(process->standardIn); |
| process->standardIn = DE_NULL; |
| return DE_TRUE; |
| } |
| else |
| return DE_FALSE; |
| } |
| |
| deBool deProcess_closeStdOut (deProcess* process) |
| { |
| if (process->standardOut) |
| { |
| deFile_destroy(process->standardOut); |
| process->standardOut = DE_NULL; |
| return DE_TRUE; |
| } |
| else |
| return DE_FALSE; |
| } |
| |
| deBool deProcess_closeStdErr (deProcess* process) |
| { |
| if (process->standardErr) |
| { |
| deFile_destroy(process->standardErr); |
| process->standardErr = DE_NULL; |
| return DE_TRUE; |
| } |
| else |
| return DE_FALSE; |
| } |
| |
| #else |
| # error Implement deProcess for your OS. |
| #endif |