21
21
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
23
23
<title>NSTask class reference</title>
24
$Date: 2002/02/24 06:14:31 $ $Revision: 1.47 $
24
$Date: 2005/04/12 18:12:43 $ $Revision: 1.70.2.1 $
28
#include <base/preface.h>
29
#include <Foundation/NSObject.h>
30
#include <Foundation/NSBundle.h>
31
#include <Foundation/NSData.h>
32
#include <Foundation/NSDate.h>
33
#include <Foundation/NSString.h>
34
#include <Foundation/NSException.h>
35
#include <Foundation/NSFileHandle.h>
36
#include <Foundation/NSFileManager.h>
37
#include <Foundation/NSMapTable.h>
38
#include <Foundation/NSProcessInfo.h>
39
#include <Foundation/NSRunLoop.h>
40
#include <Foundation/NSNotification.h>
41
#include <Foundation/NSNotificationQueue.h>
42
#include <Foundation/NSTask.h>
43
#include <Foundation/NSTimer.h>
44
#include <Foundation/NSLock.h>
45
#include <Foundation/NSDebug.h>
28
#include "GNUstepBase/preface.h"
29
#include "Foundation/NSObject.h"
30
#include "Foundation/NSAutoreleasePool.h"
31
#include "Foundation/NSBundle.h"
32
#include "Foundation/NSCharacterSet.h"
33
#include "Foundation/NSData.h"
34
#include "Foundation/NSDate.h"
35
#include "Foundation/NSEnumerator.h"
36
#include "Foundation/NSString.h"
37
#include "Foundation/NSException.h"
38
#include "Foundation/NSFileHandle.h"
39
#include "Foundation/NSFileManager.h"
40
#include "Foundation/NSMapTable.h"
41
#include "Foundation/NSProcessInfo.h"
42
#include "Foundation/NSRunLoop.h"
43
#include "Foundation/NSNotification.h"
44
#include "Foundation/NSNotificationQueue.h"
45
#include "Foundation/NSTask.h"
46
#include "Foundation/NSTimer.h"
47
#include "Foundation/NSLock.h"
48
#include "Foundation/NSDebug.h"
47
50
#include <string.h>
49
52
#include <unistd.h>
51
54
#include <sys/types.h>
539
543
* The default behavior is to inherit the parent processes
540
544
* stdout stream.<br />
541
545
* This method cannot be used after a task is launched ...
542
* it raises an NSInvalidArgumentException.
546
* it raises an NSInvalidArgumentException.
544
548
- (void) setStandardOutput: (id)hdl
546
NSAssert([hdl isKindOfClass: [NSFileHandle class]] ||
547
[hdl isKindOfClass: [NSPipe class]], NSInvalidArgumentException);
548
550
if (_hasLaunched)
550
552
[NSException raise: NSInvalidArgumentException
551
553
format: @"NSTask - task has been launched"];
555
NSAssert(hdl != nil && ([hdl isKindOfClass: [NSFileHandle class]] ||
556
[hdl isKindOfClass: [NSPipe class]]), NSInvalidArgumentException);
553
557
ASSIGN(_standardOutput, hdl);
618
622
* Sends a terminate signal to the receiver and any subtasks.<br />
619
623
* If the task has not been launched, raises an
620
* NSInvalidArgumentException.<br />
624
* <code>NSInvalidArgumentException</code>.<br />
621
625
* Has no effect on a task that has already terminated.<br />
622
* When a task temrinates, either due to this method being called,
623
* or normal termination, an NSTaskDidTerminateNotification is
626
* When a task terminates, either due to this method being called,
627
* or normal termination, an <code>NSTaskDidTerminateNotification</code> is
626
630
- (void) terminate
737
741
full_path = [arch_path stringByAppendingPathComponent: libs];
739
743
lpath = [full_path stringByAppendingPathComponent: prog];
745
if ([mgr isExecutableFileAtPath: lpath] == NO
746
&& [mgr isExecutableFileAtPath:
747
(lpath = [lpath stringByAppendingPathExtension: @"exe"])] == NO)
740
749
if ([mgr isExecutableFileAtPath: lpath] == NO)
742
752
lpath = [arch_path stringByAppendingPathComponent: prog];
754
if ([mgr isExecutableFileAtPath: lpath] == NO
755
&& [mgr isExecutableFileAtPath:
756
(lpath = [lpath stringByAppendingPathExtension: @"exe"])] == NO)
743
758
if ([mgr isExecutableFileAtPath: lpath] == NO)
745
761
lpath = [base_path stringByAppendingPathComponent: prog];
763
if ([mgr isExecutableFileAtPath: lpath] == NO
764
&& [mgr isExecutableFileAtPath:
765
(lpath = [lpath stringByAppendingPathExtension: @"exe"])] == NO)
746
767
if ([mgr isExecutableFileAtPath: lpath] == NO)
749
771
* Last resort - if the launch path was simply a program name
880
/* FIXME: Implement */
914
if (hadChildSignal == YES)
921
a = NSAllMapTableValues(activeTasks);
926
NSConcreteWindowsTask *t = [a objectAtIndex: c];
929
if (GetExitCodeProcess(t->procInfo.hProcess, &eCode) != 0)
931
if (eCode != STILL_ACTIVE)
933
[t _terminatedChild: eCode];
884
942
- (void) gcFinalize
886
944
[super gcFinalize];
887
if (proc_info.hProcess != NULL)
888
CloseHandle(proc_info.hProcess);
889
if (proc_info.hThread != NULL)
890
CloseHandle(proc_info.hThread);
947
CloseHandle(wThread);
949
if (procInfo.hProcess != NULL)
951
CloseHandle(procInfo.hProcess);
953
if (procInfo.hThread != NULL)
955
CloseHandle(procInfo.hThread);
893
959
- (void) interrupt
909
975
_hasTerminated = YES;
910
TerminateProcess(proc_info.hProcess, 10);
976
TerminateProcess(procInfo.hProcess, 10);
980
* Wait for child process completion.
982
static DWORD WINAPI _threadFunction(LPVOID t)
984
DWORD milliseconds = 60000;
985
int taskId = [(NSTask*)t processIdentifier];
989
NSConcreteWindowsTask *task;
992
task = (NSConcreteWindowsTask*)NSMapGet(activeTasks, (void*)taskId);
996
return 0; // Task gone away.
998
switch (WaitForSingleObject(task->procInfo.hProcess, milliseconds))
1001
handleSignal(0); // Signal child process state change.
1005
break; // Timeout ... retry
1008
return 0; // Error ... stop watching.
1015
endSlashesDoubledFromString(NSString *aString)
1017
int i = [aString length] - 2;
1018
NSMutableString *returnString;
1020
if (![aString hasSuffix:@"\\"])
1024
returnString = [NSMutableString stringWithFormat: @"%@\\", aString];
1025
while ([aString characterAtIndex: i] == '\\' && i >= 0)
1027
[returnString appendString:@"\\"];
1031
return returnString;
1035
quotedFromString(NSString *aString)
1037
NSString *resultString;
1038
NSMutableArray *components;
1041
/* First split on "'s */
1042
components = [NSMutableArray arrayWithArray:
1043
[aString componentsSeparatedByString: @"\""]];
1045
/* Iterate over all but the last component and double slashes if needed */
1046
i = [components count];
1049
[components replaceObjectAtIndex: i withObject:
1050
endSlashesDoubledFromString([components objectAtIndex: i])];
1053
/* Join them again with \" as separator */
1054
resultString = [components componentsJoinedByString: @"\\\""];
1056
/* Put in in "'s if it contains spaces */
1057
if ([resultString rangeOfCharacterFromSet:
1058
[NSCharacterSet whitespaceCharacterSet]].length > 0)
1060
resultString = [NSString stringWithFormat: @"\"%@\"", resultString];
1062
return resultString;
915
STARTUPINFO start_info;
1068
STARTUPINFOW start_info;
916
1069
NSString *lpath;
918
1071
NSEnumerator *arg_enum;
919
1072
NSMutableString *args;
1075
const wchar_t *wexecutable;
923
1079
if (_hasLaunched)
928
1084
lpath = [self _fullLaunchPath];
929
args = [lpath mutableCopy];
1085
lpath = [lpath localFromOpenStepPath];
1086
wexecutable = [lpath unicharString];
1088
args = [[NSMutableString alloc] initWithString: quotedFromString(lpath)];
930
1089
arg_enum = [[self arguments] objectEnumerator];
931
1090
while ((arg = [arg_enum nextObject]))
933
1092
[args appendString: @" "];
934
[args appendString: arg];
936
c_args = NSZoneMalloc(NSDefaultMallocZone(), [args cStringLength]+1);
937
[args getCString: c_args];
1093
[args appendString: quotedFromString(arg)];
1096
w_args = NSZoneMalloc(NSDefaultMallocZone(),
1097
sizeof(wchar_t) * ([args length] + 1));
1098
[args getCharacters: (unichar*)w_args];
1099
w_args[[args length]] = 0;
1101
env = [self environment];
1102
if ([env count] > 0)
1104
NSMutableData *data = [NSMutableData dataWithCapacity: 10240];
1105
NSEnumerator *enumerator;
1107
unichar terminator = 0;
1108
CREATE_AUTORELEASE_POOL(pool);
1110
// Win32 environment variables must be sorted by name
1111
enumerator = [[[env allKeys]
1112
sortedArrayUsingSelector: @selector(compare:)] objectEnumerator];
1113
while ((key = [enumerator nextObject]))
1115
NSString *value = [env objectForKey:key];
1118
NSRange r = NSMakeRange(0,0);
1119
unichar buffer[1024];
1121
setting = [NSString stringWithFormat: @"%@=%@", key, value];
1122
l = [setting length];
1123
while (r.location < l)
1125
r.length = l - r.location;
1126
if (r.length > 1024)
1131
[setting getCharacters: buffer range: r];
1132
[data appendBytes: buffer length: (r.length)*sizeof(unichar)];
1134
r.location += r.length;
1136
[data appendBytes: &terminator length: 2]; // end of setting
1138
[data appendBytes: &terminator length: 2]; // end of environment
1140
envp = [data bytes];
939
1143
memset (&start_info, 0, sizeof(start_info));
940
1144
start_info.cb = sizeof(start_info);
941
1145
start_info.dwFlags |= STARTF_USESTDHANDLES;
942
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
943
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
944
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1146
start_info.hStdInput = [[self standardInput] nativeHandle];
1147
start_info.hStdOutput = [[self standardOutput] nativeHandle];
1148
start_info.hStdError = [[self standardError] nativeHandle];
946
result = CreateProcess([lpath fileSystemRepresentation],
948
NULL, /* proc attrs */
949
NULL, /* thread attrs */
950
1, /* inherit handles */
951
0, /* creation flags */
952
NULL, /* env block */
953
[[self currentDirectoryPath] fileSystemRepresentation],
956
NSZoneFree(NSDefaultMallocZone(), c_args);
1150
result = CreateProcessW(wexecutable,
1152
NULL, /* proc attrs */
1153
NULL, /* thread attrs */
1154
1, /* inherit handles */
1155
CREATE_UNICODE_ENVIRONMENT, /* creation flags */
1156
envp, /* env block */
1157
[[[self currentDirectoryPath] localFromOpenStepPath] unicharString],
1160
NSZoneFree(NSDefaultMallocZone(), w_args);
957
1161
if (result == 0)
959
1163
NSLog(@"Error launching task: %@", lpath);
962
_taskId = proc_info.dwProcessId;
1167
_taskId = procInfo.dwProcessId;
963
1168
_hasLaunched = YES;
964
1169
ASSIGN(_launchPath, lpath); // Actual path used.
966
1171
[tasksLock lock];
967
1172
NSMapInsert(activeTasks, (void*)_taskId, (void*)self);
968
1173
[tasksLock unlock];
1175
* Create thread to watch for termination of process.
1177
wThread = CreateThread(NULL, 0, _threadFunction, (LPVOID)self, 0, &tid);
971
1180
- (void) _collectChild
973
1182
if (_hasCollected == NO)
975
/* FIXME: Implement */
979
- (int) terminationStatus
984
[super terminationStatus];
985
result = GetExitCodeProcess(proc_info.hProcess, &exit_code);
986
_terminationStatus = exit_code;
989
NSLog(@"Error getting exit code");
995
- (void) waitUntilExit
999
result = WaitForSingleObject(proc_info.hProcess, INFINITE);
1186
if (GetExitCodeProcess(procInfo.hProcess, &eCode) == 0)
1188
NSLog(@"Error getting exit code for process %d", _taskId);
1190
else if (eCode != STILL_ACTIVE)
1192
[self _terminatedChild: eCode];
1004
1199
#else /* !MINGW */
1329
1524
#ifdef WAITDEBUG
1330
1525
NSLog(@"waitpid %d, termination status = %d",
1331
_taskId, _terminationStatus);
1526
_taskId, _terminationStatus);
1333
1528
[self _terminatedChild: WTERMSIG(_terminationStatus)];
1335
1530
#ifdef WAITDEBUG
1337
NSLog(@"waitpid %d, event status = %d",
1338
_taskId, _terminationStatus);
1533
NSLog(@"waitpid %d, event status = %d",
1534
_taskId, _terminationStatus);
1341
1538
#ifdef WAITDEBUG
1343
NSLog(@"waitpid %d, result %d, error %s",
1344
_taskId, result, GSLastErrorStr(errno));
1541
NSLog(@"waitpid %d, result %d, error %s",
1542
_taskId, result, GSLastErrorStr(errno));