@interface RMTask : NSObject
@property (strong) NSDictionary *environment;
@property (strong) NSArray *arguments;
@property (strong) NSString *launchPath;
@property (assign) pid_t pid;
@property (assign) int terminationStatus;
@end

@implementation RMTask

+ (instancetype)launchedTaskWithLaunchPath:(NSString *)path arguments:(NSArray *)arguments;
{
    RMTask *task = [self new];
    task.launchPath = path;
    task.arguments = arguments;
    [task launch];
    return [task autorelease];
}

- (void)dealloc;
{
    [_environment release]; _environment = nil;
    [_arguments release]; _arguments = nil;
    [_launchPath release]; _launchPath = nil;
    [super dealloc];
}

- (void)launch;
{
    // NSLog(@"LAUNCH: %@ - %@ - %@", self.launchPath, self.arguments, self.environment);
    NSParameterAssert(self.launchPath);
    NSDictionary *env = self.environment;
    const char *cpath = [self.launchPath UTF8String];
    const char *cargs[self.arguments.count + 1];
    size_t i = 0;
    cargs[i++] = cpath;
    for (NSString *arg in self.arguments) {
	cargs[i++] = [arg UTF8String];
    }
    cargs[i] = NULL;
    pid_t pid = fork();
    if (pid == -1) {
	assert(false && "failed to spawn process");
    }
    else if (pid == 0) {
	for (NSString *name in env) {
	    setenv([name UTF8String], [env[name] UTF8String], 1);
	}
	execvp(cpath, (char **)cargs);
	assert(false && "failed to exec process");
    }
    else {
	self.pid = pid;
    }
}

- (void)terminate;
{
    kill(self.pid, SIGTERM);
}

- (void)waitUntilExit;
{
    pid_t result = 0;
    while (result >= 0 && errno != EINTR) {
	result = waitpid(self.pid, NULL, 0);
    }
    self.terminationStatus = (int)result;
}

- (int)processIdentifier;
{
    return (int)self.pid;
}

@end
