Artifact 486d495193bf421f840db844ce0533419a474deb62dd5b57678fd4ee04bdfe72:
- File
src/OFProcess.m
— part of check-in
[13ee56edf3]
at
2014-06-21 21:43:43
on branch trunk
— Move all macros from OFObject.h to macros.h
This means that OFObject.h imports macros.h now, making it unnecessary
to manually import macros.h in almost every file. And while at it, also
import autorelease.h in OFObject.h, so that this doesn't need to be
manually imported in almost every file as well. (user: js, size: 11946) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 * Jonathan Schleifer <js@webkeks.org> * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" #include <string.h> /* Work around __block being used by glibc */ #ifdef __GLIBC__ # undef __USE_XOPEN #endif #ifndef _WIN32 # include <unistd.h> # include <signal.h> # include <sys/wait.h> #endif #ifdef __MACH__ # include <crt_externs.h> #endif #import "OFProcess.h" #import "OFString.h" #import "OFArray.h" #import "OFDictionary.h" #import "OFDataArray.h" #import "OFInitializationFailedException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFWriteFailedException.h" #ifdef _WIN32 # include <windows.h> #endif #ifndef __MACH__ extern char **environ; #endif @interface OFProcess (OF_PRIVATE_CATEGORY) #ifndef _WIN32 - (char**)OF_environmentForDictionary: (OFDictionary*)dictionary; #else - (of_char16_t*)OF_environmentForDictionary: (OFDictionary*)dictionary; #endif @end @implementation OFProcess + (instancetype)processWithProgram: (OFString*)program { return [[[self alloc] initWithProgram: program] autorelease]; } + (instancetype)processWithProgram: (OFString*)program arguments: (OFArray*)arguments { return [[[self alloc] initWithProgram: program arguments: arguments] autorelease]; } + (instancetype)processWithProgram: (OFString*)program programName: (OFString*)programName arguments: (OFArray*)arguments { return [[[self alloc] initWithProgram: program programName: programName arguments: arguments] autorelease]; } + (instancetype)processWithProgram: (OFString*)program programName: (OFString*)programName arguments: (OFArray*)arguments environment: (OFDictionary*)environment { return [[[self alloc] initWithProgram: program programName: programName arguments: arguments environment: environment] autorelease]; } - init { OF_INVALID_INIT_METHOD } - initWithProgram: (OFString*)program { return [self initWithProgram: program programName: program arguments: nil environment: nil]; } - initWithProgram: (OFString*)program arguments: (OFArray*)arguments { return [self initWithProgram: program programName: program arguments: arguments environment: nil]; } - initWithProgram: (OFString*)program programName: (OFString*)programName arguments: (OFArray*)arguments { return [self initWithProgram: program programName: program arguments: arguments environment: nil]; } - initWithProgram: (OFString*)program programName: (OFString*)programName arguments: (OFArray*)arguments environment: (OFDictionary*)environment { self = [super init]; @try { #ifndef _WIN32 if (pipe(_readPipe) != 0 || pipe(_writePipe) != 0) @throw [OFInitializationFailedException exceptionWithClass: [self class]]; switch ((_pid = fork())) { case 0:; OFString *const *objects = [arguments objects]; size_t i, count = [arguments count]; char **argv; of_string_encoding_t encoding; argv = [self allocMemoryWithSize: sizeof(char*) count: count + 2]; encoding = [OFString nativeOSEncoding]; argv[0] = (char*)[programName cStringWithEncoding: encoding]; for (i = 0; i < count; i++) argv[i + 1] = (char*)[objects[i] cStringWithEncoding: encoding]; argv[i + 1] = NULL; if (environment != nil) { #ifdef __MACH__ *_NSGetEnviron() = [self OF_environmentForDictionary: environment]; #else environ = [self OF_environmentForDictionary: environment]; #endif } close(_readPipe[0]); close(_writePipe[1]); dup2(_writePipe[0], 0); dup2(_readPipe[1], 1); execvp([program cStringWithEncoding: encoding], argv); @throw [OFInitializationFailedException exceptionWithClass: [self class]]; case -1: @throw [OFInitializationFailedException exceptionWithClass: [self class]]; default: close(_readPipe[1]); close(_writePipe[0]); break; } #else SECURITY_ATTRIBUTES sa; PROCESS_INFORMATION pi; STARTUPINFOW si; void *pool; OFMutableString *argumentsString; OFEnumerator *enumerator; OFString *argument; of_char16_t *argumentsCopy; size_t length; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; if (!CreatePipe(&_readPipe[0], &_readPipe[1], &sa, 0)) @throw [OFInitializationFailedException exceptionWithClass: [self class]]; if (!SetHandleInformation(_readPipe[0], HANDLE_FLAG_INHERIT, 0)) @throw [OFInitializationFailedException exceptionWithClass: [self class]]; if (!CreatePipe(&_writePipe[0], &_writePipe[1], &sa, 0)) @throw [OFInitializationFailedException exceptionWithClass: [self class]]; if (!SetHandleInformation(_writePipe[1], HANDLE_FLAG_INHERIT, 0)) @throw [OFInitializationFailedException exceptionWithClass: [self class]]; memset(&pi, 0, sizeof(pi)); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.hStdInput = _writePipe[0]; si.hStdOutput = _readPipe[1]; si.hStdError = GetStdHandle(STD_ERROR_HANDLE); si.dwFlags |= STARTF_USESTDHANDLES; pool = objc_autoreleasePoolPush(); argumentsString = [OFMutableString stringWithString: programName]; [argumentsString replaceOccurrencesOfString: @"\\\"" withString: @"\\\\\""]; [argumentsString replaceOccurrencesOfString: @"\"" withString: @"\\\""]; if ([argumentsString containsString: @" "]) { [argumentsString prependString: @"\""]; [argumentsString appendString: @"\""]; } enumerator = [arguments objectEnumerator]; while ((argument = [enumerator nextObject]) != nil) { OFMutableString *tmp = [[argument mutableCopy] autorelease]; bool containsSpaces = [tmp containsString: @" "]; [argumentsString appendString: @" "]; if (containsSpaces) [argumentsString appendString: @"\""]; [tmp replaceOccurrencesOfString: @"\\\"" withString: @"\\\\\""]; [tmp replaceOccurrencesOfString: @"\"" withString: @"\\\""]; [argumentsString appendString: tmp]; if (containsSpaces) [argumentsString appendString: @"\""]; } length = [argumentsString UTF16StringLength]; argumentsCopy = [self allocMemoryWithSize: sizeof(of_char16_t) count: length + 1]; memcpy(argumentsCopy, [argumentsString UTF16String], ([argumentsString UTF16StringLength] + 1) * 2); @try { if (!CreateProcessW([program UTF16String], argumentsCopy, NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, [self OF_environmentForDictionary: environment], NULL, &si, &pi)) @throw [OFInitializationFailedException exceptionWithClass: [self class]]; } @finally { [self freeMemory: argumentsCopy]; } objc_autoreleasePoolPop(pool); _process = pi.hProcess; CloseHandle(pi.hThread); CloseHandle(_readPipe[1]); CloseHandle(_writePipe[0]); #endif } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [self close]; [super dealloc]; } #ifndef _WIN32 - (char**)OF_environmentForDictionary: (OFDictionary*)environment { OFEnumerator *keyEnumerator, *objectEnumerator; char **envp; size_t i, count; of_string_encoding_t encoding; if (environment == nil) return NULL; encoding = [OFString nativeOSEncoding]; count = [environment count]; envp = [self allocMemoryWithSize: sizeof(char*) count: count + 1]; keyEnumerator = [environment keyEnumerator]; objectEnumerator = [environment objectEnumerator]; for (i = 0; i < count; i++) { OFString *key; OFString *object; size_t keyLen, objectLen; key = [keyEnumerator nextObject]; object = [objectEnumerator nextObject]; keyLen = [key cStringLengthWithEncoding: encoding]; objectLen = [object cStringLengthWithEncoding: encoding]; envp[i] = [self allocMemoryWithSize: keyLen + objectLen + 2]; memcpy(envp[i], [key cStringWithEncoding: encoding], keyLen); envp[i][keyLen] = '='; memcpy(envp[i] + keyLen + 1, [object cStringWithEncoding: encoding], objectLen); envp[i][keyLen + objectLen + 1] = '\0'; } envp[i] = NULL; return envp; } #else - (of_char16_t*)OF_environmentForDictionary: (OFDictionary*)environment { OFDataArray *env = [OFDataArray dataArrayWithItemSize: 2]; OFEnumerator *keyEnumerator, *objectEnumerator; OFString *key, *object; const of_char16_t equal = '='; const of_char16_t zero = 0; keyEnumerator = [environment keyEnumerator]; objectEnumerator = [environment objectEnumerator]; while ((key = [keyEnumerator nextObject]) != nil && (object = [objectEnumerator nextObject]) != nil) { [env addItems: [key UTF16String] count: [key UTF16StringLength]]; [env addItems: &equal count: 1]; [env addItems: [object UTF16String] count: [object UTF16StringLength]]; [env addItems: &zero count: 1]; } [env addItems: &zero count: 1]; return [env items]; } #endif - (bool)lowlevelIsAtEndOfStream { #ifndef _WIN32 if (_readPipe[0] == -1) #else if (_readPipe[0] == NULL) #endif return true; return _atEndOfStream; } - (size_t)lowlevelReadIntoBuffer: (void*)buffer length: (size_t)length { #ifndef _WIN32 ssize_t ret; #else DWORD ret; #endif #ifndef _WIN32 if (_readPipe[0] == -1 || _atEndOfStream || (ret = read(_readPipe[0], buffer, length)) < 0) @throw [OFReadFailedException exceptionWithObject: self requestedLength: length]; #else if (length > UINT32_MAX) @throw [OFOutOfRangeException exception]; if (_readPipe[0] == NULL || _atEndOfStream || !ReadFile(_readPipe[0], buffer, (DWORD)length, &ret, NULL)) { if (GetLastError() == ERROR_BROKEN_PIPE) { _atEndOfStream = true; return 0; } @throw [OFReadFailedException exceptionWithObject: self requestedLength: length]; } #endif if (ret == 0) _atEndOfStream = true; return ret; } - (void)lowlevelWriteBuffer: (const void*)buffer length: (size_t)length { #ifndef _WIN32 if (_writePipe[1] == -1 || _atEndOfStream || write(_writePipe[1], buffer, length) < length) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; #else DWORD ret; if (length > UINT32_MAX) @throw [OFOutOfRangeException exception]; if (_writePipe[1] == NULL || _atEndOfStream || !WriteFile(_writePipe[1], buffer, (DWORD)length, &ret, NULL) || ret < length) @throw [OFWriteFailedException exceptionWithObject: self requestedLength: length]; #endif } - (int)fileDescriptorForReading { #ifndef _WIN32 return _readPipe[0]; #else OF_UNRECOGNIZED_SELECTOR #endif } - (int)fileDescriptorForWriting { #ifndef _WIN32 return _writePipe[1]; #else OF_UNRECOGNIZED_SELECTOR #endif } - (void)closeForWriting { #ifndef _WIN32 if (_writePipe[1] != -1) close(_writePipe[1]); _writePipe[1] = -1; #else if (_writePipe[1] != NULL) CloseHandle(_writePipe[1]); _writePipe[1] = NULL; #endif } - (void)close { #ifndef _WIN32 if (_readPipe[0] != -1) close(_readPipe[0]); if (_writePipe[1] != -1) close(_writePipe[1]); if (_pid != -1) { kill(_pid, SIGKILL); waitpid(_pid, &_status, WNOHANG); } _pid = -1; _readPipe[0] = -1; _writePipe[1] = -1; #else if (_readPipe[0] != NULL) CloseHandle(_readPipe[0]); if (_writePipe[1] != NULL) CloseHandle(_writePipe[1]); if (_process != INVALID_HANDLE_VALUE) { TerminateProcess(_process, 0); CloseHandle(_process); } _process = INVALID_HANDLE_VALUE; _readPipe[0] = NULL; _writePipe[1] = NULL; #endif } @end