1998-01-19 15:20:15 +00:00
|
|
|
/* Implementation for NSTask for GNUStep
|
|
|
|
Copyright (C) 1998 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
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
|
|
|
|
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <gnustep/base/preface.h>
|
|
|
|
#include <Foundation/NSObject.h>
|
|
|
|
#include <Foundation/NSData.h>
|
|
|
|
#include <Foundation/NSDate.h>
|
|
|
|
#include <Foundation/NSString.h>
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
#include <Foundation/NSFileHandle.h>
|
|
|
|
#include <Foundation/NSFileManager.h>
|
|
|
|
#include <Foundation/NSProcessInfo.h>
|
|
|
|
#include <Foundation/NSRunLoop.h>
|
|
|
|
#include <Foundation/NSNotification.h>
|
|
|
|
#include <Foundation/NSNotificationQueue.h>
|
|
|
|
#include <Foundation/NSTask.h>
|
|
|
|
|
|
|
|
#include <sys/signal.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
NSString *NSTaskDidTerminateNotification = @"NSTaskDidTerminateNotification";
|
|
|
|
|
|
|
|
@interface NSTask (Private)
|
|
|
|
- (void) _collectChild;
|
|
|
|
- (void) _sendNotification;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSTask
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [NSTask class])
|
|
|
|
{
|
|
|
|
signal(SIGCHLD, SIG_IGN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-01-19 15:20:15 +00:00
|
|
|
+ (NSTask*)launchedTaskWithLaunchPath:(NSString*)path arguments: (NSArray*)args
|
|
|
|
{
|
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];
|
|
|
|
return [task autorelease];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
[arguments release];
|
|
|
|
[environment release];
|
|
|
|
[launchPath release];
|
|
|
|
[currentDirectoryPath release];
|
|
|
|
[standardError release];
|
|
|
|
[standardInput release];
|
|
|
|
[standardOutput release];
|
|
|
|
[super dealloc];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Querying task parameters.
|
|
|
|
*/
|
|
|
|
|
|
|
|
- (NSArray*) arguments
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
return arguments;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) currentDirectoryPath
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (currentDirectoryPath == nil)
|
|
|
|
{
|
|
|
|
[self setCurrentDirectoryPath:
|
1998-01-19 15:20:15 +00:00
|
|
|
[[NSFileManager defaultManager] currentDirectoryPath]];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
return currentDirectoryPath;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSDictionary*) environment
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (environment == nil)
|
|
|
|
{
|
|
|
|
[self setEnvironment: [[NSProcessInfo processInfo] environment]];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
return environment;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) launchPath
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
return launchPath;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSFileHandle*) standardError
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (standardError == nil)
|
|
|
|
{
|
|
|
|
[self setStandardError: [NSFileHandle fileHandleWithStandardError]];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
return standardError;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSFileHandle*) standardInput
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (standardInput == nil)
|
|
|
|
{
|
|
|
|
[self setStandardInput: [NSFileHandle fileHandleWithStandardInput]];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
return standardInput;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSFileHandle*) standardOutput
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (standardOutput == nil)
|
|
|
|
{
|
|
|
|
[self setStandardOutput: [NSFileHandle fileHandleWithStandardOutput]];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +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
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
[args retain];
|
|
|
|
[arguments release];
|
|
|
|
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
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
[path retain];
|
|
|
|
[currentDirectoryPath release];
|
|
|
|
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
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
[env retain];
|
|
|
|
[environment release];
|
|
|
|
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
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
[path retain];
|
|
|
|
[launchPath release];
|
|
|
|
launchPath = path;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
- (void) setStandardError: (NSFileHandle*)hdl
|
1998-01-19 15:20:15 +00:00
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
[hdl retain];
|
|
|
|
[standardError release];
|
|
|
|
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-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
[hdl retain];
|
|
|
|
[standardInput release];
|
|
|
|
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-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has been launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
[hdl retain];
|
|
|
|
[standardOutput release];
|
|
|
|
standardOutput = hdl;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Obtaining task state
|
|
|
|
*/
|
|
|
|
|
|
|
|
- (BOOL) isRunning
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched == NO)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (hasCollected == NO)
|
|
|
|
{
|
|
|
|
[self _collectChild];
|
|
|
|
}
|
|
|
|
if (hasTerminated == YES)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched == NO)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has not yet launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasCollected == NO)
|
|
|
|
{
|
|
|
|
[self _collectChild];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasTerminated == NO)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has not yet terminated"];
|
|
|
|
}
|
1998-11-30 10:15:35 +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
|
|
|
}
|
|
|
|
|
|
|
|
- (void) launch
|
|
|
|
{
|
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];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (hasLaunched)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (launchPath == nil)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - no launch path set"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
else if ([[NSFileManager defaultManager] isExecutableFileAtPath:
|
|
|
|
launchPath] == NO)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - launch path is not valid"];
|
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
executable = [[self launchPath] cString];
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
args[0] = [[[self launchPath] lastPathComponent] cString];
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
s = [NSString stringWithFormat: @"%s=%s",
|
1998-04-02 14:27:40 +00:00
|
|
|
[key cString], [val cString]];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
s = [NSString stringWithFormat: @"%s=", [key cString]];
|
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];
|
|
|
|
idesc = [[self standardInput] fileDescriptor];
|
|
|
|
odesc = [[self standardError] fileDescriptor];
|
|
|
|
edesc = [[self standardOutput] 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)
|
|
|
|
{
|
|
|
|
if (idesc != 0)
|
|
|
|
{
|
|
|
|
dup2(idesc, 0);
|
|
|
|
}
|
|
|
|
if (odesc != 1)
|
|
|
|
{
|
|
|
|
dup2(odesc, 1);
|
|
|
|
}
|
|
|
|
if (edesc != 2)
|
|
|
|
{
|
|
|
|
dup2(edesc, 2);
|
|
|
|
}
|
|
|
|
chdir(path);
|
|
|
|
execve(executable, args, envl);
|
|
|
|
exit(-1);
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
taskId = pid;
|
|
|
|
hasLaunched = YES;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) terminate
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasLaunched == NO)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-01-19 15:20:15 +00:00
|
|
|
format: @"NSTask - task has not yet launched"];
|
|
|
|
}
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasTerminated)
|
|
|
|
{
|
|
|
|
return;
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
hasTerminated = YES;
|
1998-05-29 15:25:41 +00:00
|
|
|
#ifdef HAVE_KILLPG
|
1998-11-30 10:15:35 +00:00
|
|
|
killpg(taskId, SIGTERM);
|
1998-05-29 15:25:41 +00:00
|
|
|
#else
|
1998-11-30 10:15:35 +00:00
|
|
|
kill(-taskId, SIGTERM);
|
1998-05-29 15:25:41 +00:00
|
|
|
#endif
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasNotified == NO)
|
|
|
|
{
|
|
|
|
[self _sendNotification];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) waitUntilExit
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
while ([self isRunning])
|
|
|
|
{
|
|
|
|
NSDate *limit;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Poll at 1.0 second intervals.
|
|
|
|
*/
|
|
|
|
limit = [[NSDate alloc] initWithTimeIntervalSinceNow: 1.0];
|
|
|
|
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
|
|
|
|
beforeDate: nil];
|
|
|
|
[limit release];
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSTask (Private)
|
|
|
|
|
|
|
|
- (void) _collectChild
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasCollected == NO)
|
|
|
|
{
|
|
|
|
if (waitpid(taskId, &terminationStatus, WNOHANG) == taskId)
|
|
|
|
{
|
|
|
|
if (WIFEXITED(terminationStatus))
|
|
|
|
{
|
|
|
|
terminationStatus = WEXITSTATUS(terminationStatus);
|
|
|
|
hasCollected = YES;
|
|
|
|
hasTerminated = YES;
|
|
|
|
if (hasNotified == NO)
|
|
|
|
{
|
|
|
|
[self _sendNotification];
|
1998-04-02 14:27:40 +00:00
|
|
|
}
|
1998-01-19 15:20:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _sendNotification
|
|
|
|
{
|
1998-11-30 10:15:35 +00:00
|
|
|
if (hasNotified == NO)
|
|
|
|
{
|
|
|
|
NSNotification *n;
|
1998-01-19 15:20:15 +00:00
|
|
|
|
1998-11-30 10:15:35 +00:00
|
|
|
hasNotified = YES;
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|