1998-01-19 15:20:15 +00:00
|
|
|
/* Implementation for NSTask for GNUStep
|
1999-04-14 10:34:56 +00:00
|
|
|
Copyright (C) 1998,1999 Free Software Foundation, Inc.
|
1998-01-19 15:20:15 +00:00
|
|
|
|
|
|
|
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
Date: 1998
|
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library; if not, write to the Free
|
1999-09-09 02:56:20 +00:00
|
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
1998-01-19 15:20:15 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
1998-12-20 21:27:47 +00:00
|
|
|
#include <base/preface.h>
|
1998-01-19 15:20:15 +00:00
|
|
|
#include <Foundation/NSObject.h>
|
1999-04-14 10:34:56 +00:00
|
|
|
#include <Foundation/NSBundle.h>
|
1998-01-19 15:20:15 +00:00
|
|
|
#include <Foundation/NSData.h>
|
|
|
|
#include <Foundation/NSDate.h>
|
|
|
|
#include <Foundation/NSString.h>
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
#include <Foundation/NSFileHandle.h>
|
|
|
|
#include <Foundation/NSFileManager.h>
|
1999-01-05 16:55:29 +00:00
|
|
|
#include <Foundation/NSMapTable.h>
|
1998-01-19 15:20:15 +00:00
|
|
|
#include <Foundation/NSProcessInfo.h>
|
|
|
|
#include <Foundation/NSRunLoop.h>
|
|
|
|
#include <Foundation/NSNotification.h>
|
|
|
|
#include <Foundation/NSNotificationQueue.h>
|
|
|
|
#include <Foundation/NSTask.h>
|
1999-06-24 19:30:29 +00:00
|
|
|
#include <Foundation/NSLock.h>
|
1999-02-15 06:37:33 +00:00
|
|
|
#include <Foundation/NSDebug.h>
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
1998-01-19 15:20:15 +00:00
|
|
|
#include <sys/signal.h>
|
|
|
|
#include <sys/types.h>
|
1999-04-15 09:28:37 +00:00
|
|
|
#include <sys/param.h>
|
1998-01-19 15:20:15 +00:00
|
|
|
#include <sys/wait.h>
|
|
|
|
|
1999-04-15 09:28:37 +00:00
|
|
|
/*
|
|
|
|
* If we don't have NFILE, default to 256 open descriptors.
|
|
|
|
*/
|
|
|
|
#ifndef NOFILE
|
|
|
|
#define NOFILE 256
|
|
|
|
#endif
|
|
|
|
|
1998-01-19 15:20:15 +00:00
|
|
|
NSString *NSTaskDidTerminateNotification = @"NSTaskDidTerminateNotification";
|
|
|
|
|
1999-01-05 16:55:29 +00:00
|
|
|
static NSRecursiveLock *tasksLock = nil;
|
|
|
|
static NSMapTable *activeTasks = 0;
|
|
|
|
|
1998-01-19 15:20:15 +00:00
|
|
|
@interface NSTask (Private)
|
|
|
|
- (void) _collectChild;
|
|
|
|
- (void) _sendNotification;
|
1999-01-05 16:55:29 +00:00
|
|
|
- (void) _terminatedChild: (int)status;
|
1998-01-19 15:20:15 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSTask
|
|
|
|
|
1999-01-05 16:55:29 +00:00
|
|
|
static void handleSignal(int sig)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
result = waitpid(-1, &status, WNOHANG);
|
|
|
|
if (result > 0)
|
|
|
|
{
|
|
|
|
if (WIFEXITED(status))
|
|
|
|
{
|
|
|
|
NSTask *t;
|
1999-01-05 21:20:33 +00:00
|
|
|
|
1999-01-05 16:55:29 +00:00
|
|
|
[tasksLock lock];
|
|
|
|
t = (NSTask*)NSMapGet(activeTasks, (void*)result);
|
|
|
|
[tasksLock unlock];
|
|
|
|
if (t)
|
|
|
|
{
|
|
|
|
[t _terminatedChild: WEXITSTATUS(status)];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (result > 0);
|
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [NSTask class])
|
|
|
|
{
|
1999-01-05 16:55:29 +00:00
|
|
|
[gnustep_global_lock lock];
|
|
|
|
if (tasksLock == nil)
|
|
|
|
{
|
|
|
|
tasksLock = [NSRecursiveLock new];
|
|
|
|
activeTasks = NSCreateMapTable(NSIntMapKeyCallBacks,
|
|
|
|
NSNonOwnedPointerMapValueCallBacks, 0);
|
|
|
|
}
|
|
|
|
[gnustep_global_lock unlock];
|
|
|
|
|
1999-01-05 21:20:33 +00:00
|
|
|
signal(SIGCHLD, handleSignal);
|
1998-11-30 10:15:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-12-16 20:32:59 +00:00
|
|
|
+ (NSTask*) launchedTaskWithLaunchPath: (NSString*)path
|
|
|
|
arguments: (NSArray*)args
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
NSTask* task = [NSTask new];
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
[task setLaunchPath: path];
|
|
|
|
[task setArguments: args];
|
|
|
|
[task launch];
|
1999-04-14 10:34:56 +00:00
|
|
|
return AUTORELEASE(task);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1999-04-14 10:34:56 +00:00
|
|
|
- (void) gcFinalize
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1999-01-05 16:55:29 +00:00
|
|
|
[tasksLock lock];
|
1999-09-16 07:21:34 +00:00
|
|
|
NSMapRemove(activeTasks, (void*)_taskId);
|
1999-01-05 16:55:29 +00:00
|
|
|
[tasksLock unlock];
|
1999-04-14 10:34:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
[self gcFinalize];
|
1999-09-16 07:21:34 +00:00
|
|
|
RELEASE(_arguments);
|
|
|
|
RELEASE(_environment);
|
|
|
|
RELEASE(_launchPath);
|
|
|
|
RELEASE(_currentDirectoryPath);
|
|
|
|
RELEASE(_standardError);
|
|
|
|
RELEASE(_standardInput);
|
|
|
|
RELEASE(_standardOutput);
|
1998-11-30 10:15:35 +00:00
|
|
|
[super dealloc];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Querying task parameters.
|
|
|
|
*/
|
|
|
|
|
|
|
|
- (NSArray*) arguments
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return _arguments;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) currentDirectoryPath
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_currentDirectoryPath == nil)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[self setCurrentDirectoryPath:
|
1998-01-19 15:20:15 +00:00
|
|
|
[[NSFileManager defaultManager] currentDirectoryPath]];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
return _currentDirectoryPath;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*) environment
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_environment == nil)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[self setEnvironment: [[NSProcessInfo processInfo] environment]];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
return _environment;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) launchPath
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
return _launchPath;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-12-16 20:32:59 +00:00
|
|
|
- (id) standardError
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_standardError == nil)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[self setStandardError: [NSFileHandle fileHandleWithStandardError]];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
return _standardError;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-12-16 20:32:59 +00:00
|
|
|
- (id) standardInput
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_standardInput == nil)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[self setStandardInput: [NSFileHandle fileHandleWithStandardInput]];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
return _standardInput;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-12-16 20:32:59 +00:00
|
|
|
- (id) standardOutput
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_standardOutput == nil)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[self setStandardOutput: [NSFileHandle fileHandleWithStandardOutput]];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
return _standardOutput;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setting task parameters.
|
|
|
|
*/
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
- (void) setArguments: (NSArray*)args
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGN(_arguments, args);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
- (void) setCurrentDirectoryPath: (NSString*)path
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGN(_currentDirectoryPath, path);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
- (void) setEnvironment: (NSDictionary*)env
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGN(_environment, env);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
- (void) setLaunchPath: (NSString*)path
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGN(_launchPath, path);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-12-16 20:32:59 +00:00
|
|
|
- (void) setStandardError: (id)hdl
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1998-12-16 20:32:59 +00:00
|
|
|
NSAssert([hdl isKindOfClass: [NSFileHandle class]] ||
|
|
|
|
[hdl isKindOfClass: [NSPipe class]], NSInvalidArgumentException);
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGN(_standardError, hdl);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
- (void) setStandardInput: (NSFileHandle*)hdl
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1998-12-16 20:32:59 +00:00
|
|
|
NSAssert([hdl isKindOfClass: [NSFileHandle class]] ||
|
|
|
|
[hdl isKindOfClass: [NSPipe class]], NSInvalidArgumentException);
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGN(_standardInput, hdl);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
- (void) setStandardOutput: (NSFileHandle*)hdl
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1998-12-16 20:32:59 +00:00
|
|
|
NSAssert([hdl isKindOfClass: [NSFileHandle class]] ||
|
|
|
|
[hdl isKindOfClass: [NSPipe class]], NSInvalidArgumentException);
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
ASSIGN(_standardOutput, hdl);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Obtaining task state
|
|
|
|
*/
|
|
|
|
|
|
|
|
- (BOOL) isRunning
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasCollected == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[self _collectChild];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasTerminated == YES)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
return NO;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
return YES;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (int) terminationStatus
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has not yet launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasCollected == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[self _collectChild];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasTerminated == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has not yet terminated"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
return _terminationStatus;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handling a task.
|
|
|
|
*/
|
|
|
|
- (void) interrupt
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
[self notImplemented: _cmd]; /* Undocumented as yet */
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1999-04-14 10:34:56 +00:00
|
|
|
/* Declaration from find_exec.c */
|
|
|
|
extern char *objc_find_executable(const char *name);
|
|
|
|
|
1998-01-19 15:20:15 +00:00
|
|
|
- (void) launch
|
|
|
|
{
|
1998-12-16 20:32:59 +00:00
|
|
|
NSMutableArray *toClose;
|
1999-04-14 10:34:56 +00:00
|
|
|
NSFileManager *mgr = [NSFileManager defaultManager];
|
|
|
|
NSString *libs = [NSBundle _library_combo];
|
|
|
|
NSString *arch = [NSBundle _gnustep_target_dir];
|
|
|
|
NSString *prog;
|
|
|
|
NSString *lpath;
|
|
|
|
NSString *base_path;
|
|
|
|
NSString *arch_path;
|
|
|
|
NSString *full_path;
|
1998-11-30 10:15:35 +00:00
|
|
|
int pid;
|
|
|
|
const char *executable;
|
|
|
|
const char *path;
|
|
|
|
int idesc;
|
|
|
|
int odesc;
|
|
|
|
int edesc;
|
|
|
|
NSDictionary *e = [self environment];
|
|
|
|
NSArray *k = [e allKeys];
|
|
|
|
NSArray *a = [self arguments];
|
|
|
|
int ec = [e count];
|
|
|
|
int ac = [a count];
|
|
|
|
const char *args[ac+2];
|
|
|
|
const char *envl[ec+1];
|
1998-12-16 20:32:59 +00:00
|
|
|
id hdl;
|
1998-11-30 10:15:35 +00:00
|
|
|
int i;
|
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_launchPath == nil)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - no launch path set"];
|
|
|
|
}
|
1999-04-14 10:34:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set lpath to the actual path to use for the executable.
|
|
|
|
* First choice - base_path/architecture/library_combo/prog.
|
|
|
|
* Second choice - base_path/architecture/prog.
|
|
|
|
* Third choice - base_path/prog.
|
|
|
|
* Otherwise - try using PATH environment variable if possible.
|
|
|
|
*/
|
1999-09-16 07:21:34 +00:00
|
|
|
prog = [_launchPath lastPathComponent];
|
|
|
|
base_path = [_launchPath stringByDeletingLastPathComponent];
|
1999-04-14 10:34:56 +00:00
|
|
|
if ([[base_path lastPathComponent] isEqualToString: libs] == YES)
|
|
|
|
base_path = [base_path stringByDeletingLastPathComponent];
|
|
|
|
if ([[base_path lastPathComponent] isEqualToString: arch] == YES)
|
|
|
|
base_path = [base_path stringByDeletingLastPathComponent];
|
|
|
|
arch_path = [base_path stringByAppendingPathComponent: arch];
|
|
|
|
full_path = [arch_path stringByAppendingPathComponent: libs];
|
|
|
|
|
|
|
|
lpath = [full_path stringByAppendingPathComponent: prog];
|
|
|
|
if ([mgr isExecutableFileAtPath: lpath] == NO)
|
|
|
|
{
|
|
|
|
lpath = [arch_path stringByAppendingPathComponent: prog];
|
|
|
|
if ([mgr isExecutableFileAtPath: lpath] == NO)
|
|
|
|
{
|
|
|
|
lpath = [base_path stringByAppendingPathComponent: prog];
|
|
|
|
if ([mgr isExecutableFileAtPath: lpath] == NO)
|
|
|
|
{
|
|
|
|
const char *cpath = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Last resort - if the launch path was simply a program name
|
|
|
|
* get objc_find_executable() to try using the PATH environment
|
|
|
|
* variable to find the executable.
|
|
|
|
*/
|
|
|
|
if ([base_path isEqualToString: @""] == YES)
|
|
|
|
{
|
|
|
|
|
|
|
|
cpath = objc_find_executable([prog cString]);
|
|
|
|
}
|
|
|
|
if (cpath == 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
format: @"NSTask - launch path is not valid"];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lpath = [NSString stringWithCString: cpath];
|
|
|
|
OBJC_FREE((void*)cpath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Make sure we have a standardised absolute path to pass to execve()
|
|
|
|
*/
|
|
|
|
if ([lpath isAbsolutePath] == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
1999-04-14 10:34:56 +00:00
|
|
|
NSString *current = [mgr currentDirectoryPath];
|
|
|
|
|
|
|
|
lpath = [current stringByAppendingPathComponent: lpath];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1999-04-14 10:34:56 +00:00
|
|
|
lpath = [lpath stringByStandardizingPath];
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1999-04-14 10:34:56 +00:00
|
|
|
executable = [lpath cString];
|
1999-09-02 07:43:08 +00:00
|
|
|
args[0] = executable;
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
for (i = 0; i < ac; i++)
|
|
|
|
{
|
|
|
|
args[i+1] = [[[a objectAtIndex: i] description] cString];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
args[ac+1] = 0;
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
for (i = 0; i < ec; i++)
|
|
|
|
{
|
|
|
|
NSString *s;
|
|
|
|
id key = [k objectAtIndex: i];
|
|
|
|
id val = [e objectForKey: key];
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
if (val)
|
|
|
|
{
|
1999-01-05 10:18:56 +00:00
|
|
|
s = [NSString stringWithFormat: @"%@=%@", key, val];
|
1998-04-02 14:27:40 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
else
|
|
|
|
{
|
1999-01-05 10:18:56 +00:00
|
|
|
s = [NSString stringWithFormat: @"%@=", key];
|
1998-04-02 14:27:40 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
envl[i] = [s cString];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
envl[ec] = 0;
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
path = [[self currentDirectoryPath] cString];
|
1998-12-16 20:32:59 +00:00
|
|
|
|
|
|
|
toClose = [NSMutableArray arrayWithCapacity: 3];
|
|
|
|
hdl = [self standardInput];
|
|
|
|
if ([hdl isKindOfClass: [NSPipe class]])
|
|
|
|
{
|
|
|
|
hdl = [hdl fileHandleForReading];
|
|
|
|
[toClose addObject: hdl];
|
|
|
|
}
|
|
|
|
idesc = [hdl fileDescriptor];
|
|
|
|
|
|
|
|
hdl = [self standardOutput];
|
|
|
|
if ([hdl isKindOfClass: [NSPipe class]])
|
|
|
|
{
|
|
|
|
hdl = [hdl fileHandleForWriting];
|
|
|
|
[toClose addObject: hdl];
|
|
|
|
}
|
|
|
|
odesc = [hdl fileDescriptor];
|
|
|
|
|
|
|
|
hdl = [self standardError];
|
|
|
|
if ([hdl isKindOfClass: [NSPipe class]])
|
|
|
|
{
|
|
|
|
hdl = [hdl fileHandleForWriting];
|
|
|
|
/*
|
|
|
|
* If we have the same pipe twice we don't want to close it twice
|
|
|
|
*/
|
|
|
|
if ([toClose indexOfObjectIdenticalTo: hdl] == NSNotFound)
|
|
|
|
{
|
|
|
|
[toClose addObject: hdl];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
edesc = [hdl fileDescriptor];
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
pid = fork();
|
|
|
|
if (pid < 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - failed to create child process"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
if (pid == 0)
|
|
|
|
{
|
1999-04-15 09:28:37 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the task gets default signal setup.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
{
|
|
|
|
signal(i, SIG_DFL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure task is run in it's own process group.
|
|
|
|
*/
|
1999-01-05 16:55:29 +00:00
|
|
|
#if HAVE_SETPGRP
|
1999-06-26 05:28:11 +00:00
|
|
|
#ifdef SETPGRP_VOID
|
1999-01-05 16:55:29 +00:00
|
|
|
setpgrp();
|
1999-06-26 05:28:11 +00:00
|
|
|
#else
|
|
|
|
setpgrp(getpid(), getpid());
|
|
|
|
#endif
|
1999-01-05 16:55:29 +00:00
|
|
|
#else
|
1999-05-11 09:21:38 +00:00
|
|
|
#if defined(__WIN32__)
|
1999-05-06 05:49:55 +00:00
|
|
|
pid = (int)GetCurrentProcessId(),
|
|
|
|
#else
|
|
|
|
pid = (int)getpid();
|
|
|
|
#endif
|
1999-01-05 16:55:29 +00:00
|
|
|
#if HAVE_SETPGID
|
|
|
|
setpgid(pid, pid);
|
|
|
|
#endif
|
|
|
|
#endif
|
1999-04-15 09:28:37 +00:00
|
|
|
|
1999-01-05 10:18:56 +00:00
|
|
|
/*
|
|
|
|
* Set up stdin, stdout and stderr by duplicating descriptors as
|
|
|
|
* necessary and closing the originals (to ensure we won't have a
|
|
|
|
* pipe left with two write descriptors etc).
|
|
|
|
*/
|
1998-11-30 10:15:35 +00:00
|
|
|
if (idesc != 0)
|
|
|
|
{
|
|
|
|
dup2(idesc, 0);
|
|
|
|
}
|
|
|
|
if (odesc != 1)
|
|
|
|
{
|
|
|
|
dup2(odesc, 1);
|
|
|
|
}
|
|
|
|
if (edesc != 2)
|
|
|
|
{
|
|
|
|
dup2(edesc, 2);
|
|
|
|
}
|
1999-04-15 09:28:37 +00:00
|
|
|
|
|
|
|
for (i = 3; i < NOFILE; i++)
|
|
|
|
{
|
|
|
|
(void) close(i);
|
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
chdir(path);
|
1999-06-24 19:30:29 +00:00
|
|
|
execve(executable, (char**)args, (char**)envl);
|
1998-11-30 10:15:35 +00:00
|
|
|
exit(-1);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
else
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
_taskId = pid;
|
|
|
|
_hasLaunched = YES;
|
|
|
|
ASSIGN(_launchPath, lpath); // Actual path used.
|
1999-01-05 16:55:29 +00:00
|
|
|
|
|
|
|
[tasksLock lock];
|
1999-09-16 07:21:34 +00:00
|
|
|
NSMapInsert(activeTasks, (void*)_taskId, (void*)self);
|
1999-01-05 16:55:29 +00:00
|
|
|
[tasksLock unlock];
|
|
|
|
|
1998-12-16 20:32:59 +00:00
|
|
|
/*
|
|
|
|
* Close the ends of any pipes used by the child.
|
|
|
|
*/
|
|
|
|
while ([toClose count] > 0)
|
|
|
|
{
|
|
|
|
hdl = [toClose objectAtIndex: 0];
|
|
|
|
[hdl closeFile];
|
|
|
|
[toClose removeObjectAtIndex: 0];
|
|
|
|
}
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) terminate
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasLaunched == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has not yet launched"];
|
|
|
|
}
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasTerminated)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
return;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
_hasTerminated = YES;
|
1998-05-29 15:25:41 +00:00
|
|
|
#ifdef HAVE_KILLPG
|
1999-09-16 07:21:34 +00:00
|
|
|
killpg(_taskId, SIGTERM);
|
1998-05-29 15:25:41 +00:00
|
|
|
#else
|
1999-09-16 07:21:34 +00:00
|
|
|
kill(-_taskId, SIGTERM);
|
1998-05-29 15:25:41 +00:00
|
|
|
#endif
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) waitUntilExit
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
while ([self isRunning])
|
|
|
|
{
|
|
|
|
NSDate *limit;
|
|
|
|
|
|
|
|
/*
|
1999-01-05 10:18:56 +00:00
|
|
|
* Poll at 0.1 second intervals.
|
1998-11-30 10:15:35 +00:00
|
|
|
*/
|
1999-01-05 10:18:56 +00:00
|
|
|
limit = [[NSDate alloc] initWithTimeIntervalSinceNow: 0.1];
|
1998-11-30 10:15:35 +00:00
|
|
|
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
|
1999-01-05 10:18:56 +00:00
|
|
|
beforeDate: limit];
|
1999-04-14 10:34:56 +00:00
|
|
|
RELEASE(limit);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSTask (Private)
|
|
|
|
|
|
|
|
- (void) _collectChild
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasCollected == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
1999-01-05 10:18:56 +00:00
|
|
|
int result;
|
|
|
|
|
|
|
|
errno = 0;
|
1999-09-16 07:21:34 +00:00
|
|
|
result = waitpid(_taskId, &_terminationStatus, WNOHANG);
|
1999-01-05 16:55:29 +00:00
|
|
|
if (result < 0)
|
|
|
|
{
|
|
|
|
NSLog(@"waitpid %d, result %d, error %s",
|
1999-09-16 07:21:34 +00:00
|
|
|
_taskId, result, strerror(errno));
|
1999-01-05 16:55:29 +00:00
|
|
|
[self _terminatedChild: -1];
|
|
|
|
}
|
1999-09-28 04:34:22 +00:00
|
|
|
else if (result == _taskId || (result > 0 && errno == 0))
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (WIFEXITED(_terminationStatus))
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
1999-01-05 16:55:29 +00:00
|
|
|
#ifdef WAITDEBUG
|
|
|
|
NSLog(@"waitpid %d, termination status = %d",
|
1999-09-16 07:21:34 +00:00
|
|
|
_taskId, _terminationStatus);
|
1999-01-05 16:55:29 +00:00
|
|
|
#endif
|
1999-09-16 07:21:34 +00:00
|
|
|
[self _terminatedChild: WEXITSTATUS(_terminationStatus)];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1999-01-05 16:55:29 +00:00
|
|
|
#ifdef WAITDEBUG
|
1999-01-05 10:18:56 +00:00
|
|
|
else
|
1999-01-05 16:55:29 +00:00
|
|
|
NSLog(@"waitpid %d, event status = %d",
|
1999-09-16 07:21:34 +00:00
|
|
|
_taskId, _terminationStatus);
|
1999-01-05 10:18:56 +00:00
|
|
|
#endif
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1999-01-05 16:55:29 +00:00
|
|
|
#ifdef WAITDEBUG
|
1999-01-05 10:18:56 +00:00
|
|
|
else
|
1999-01-05 16:55:29 +00:00
|
|
|
NSLog(@"waitpid %d, result %d, error %s",
|
1999-09-16 07:21:34 +00:00
|
|
|
_taskId, result, strerror(errno));
|
1999-01-05 10:18:56 +00:00
|
|
|
#endif
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _sendNotification
|
|
|
|
{
|
1999-09-16 07:21:34 +00:00
|
|
|
if (_hasNotified == NO)
|
1998-11-30 10:15:35 +00:00
|
|
|
{
|
|
|
|
NSNotification *n;
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
_hasNotified = YES;
|
1998-11-30 10:15:35 +00:00
|
|
|
n = [NSNotification notificationWithName: NSTaskDidTerminateNotification
|
|
|
|
object: self
|
|
|
|
userInfo: nil];
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
[[NSNotificationQueue defaultQueue] enqueueNotification: n
|
1998-01-19 15:20:15 +00:00
|
|
|
postingStyle: NSPostASAP
|
|
|
|
coalesceMask: NSNotificationNoCoalescing
|
|
|
|
forModes: nil];
|
|
|
|
}
|
|
|
|
}
|
1999-01-05 16:55:29 +00:00
|
|
|
|
|
|
|
- (void) _terminatedChild: (int)status
|
|
|
|
{
|
|
|
|
[tasksLock lock];
|
1999-09-16 07:21:34 +00:00
|
|
|
NSMapRemove(activeTasks, (void*)_taskId);
|
1999-01-05 16:55:29 +00:00
|
|
|
[tasksLock unlock];
|
1999-09-16 07:21:34 +00:00
|
|
|
_terminationStatus = status;
|
|
|
|
_hasCollected = YES;
|
|
|
|
_hasTerminated = YES;
|
|
|
|
if (_hasNotified == NO)
|
1999-01-05 16:55:29 +00:00
|
|
|
{
|
|
|
|
[self _sendNotification];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-01-19 15:20:15 +00:00
|
|
|
@end
|
|
|
|
|