NSTask interrupt not working - Cocoa Objective C

46 views Asked by At

I am using a NSTask to execute shell command like terminal. I want to run the command in root level so I am switching the sh to root user. This works fine until I want to interrupt a running process like ping command. Below is my code,

#import "ViewController.h"

@interface ViewController()
{
    NSTask* task;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupTask];
    [self executeCommand:@"sudo -S -i"];
    [self executeCommand:@"password"];

    [self executeCommand:@"ping www.google.com"];
    [self performSelector:@selector(terminateTask) withObject:nil afterDelay:3];
}

-(void)setupTask {
    @autoreleasepool {
        
        NSPipe *inPipe = [NSPipe new]; // pipe for shell input
        NSPipe *outPipe = [NSPipe new]; // pipe for shell output
        NSPipe* errorPipe = [NSPipe new];
        
        task = [NSTask new];
        [task setLaunchPath:@"/bin/sh"]; //
        [task setStandardInput:inPipe];
        [task setStandardOutput:outPipe];
        [task setStandardError:errorPipe];
        [task launch];
        
        
        // ... and wait for shell output.
        [[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
        [[errorPipe fileHandleForReading] waitForDataInBackgroundAndNotify];

        // Wait asynchronously for shell output.
        // The block is executed as soon as some data is available on the shell output pipe.
        [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
                                                          object:[outPipe fileHandleForReading] queue:nil
                                                      usingBlock:^(NSNotification *note)
         {
            // Read from shell output
            NSData *outData = [[outPipe fileHandleForReading] availableData];
            if ([outData length] > 0) {
                NSString *outStr = [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
                
                if ([outStr isEqualToString:@"Password:"]) {
                    NSLog(@"Password asked");
                    [[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
                } else {
                    NSLog(@"output: %@", outStr);
                }
                // Continue waiting for shell output.
            }
            [[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
        }];
    }
}

-(void)executeCommand:(NSString*)command {
    [task resume];
    NSData* data = [command dataUsingEncoding:NSUTF8StringEncoding];
    NSError* error;
    [[[task standardInput] fileHandleForWriting] writeData:data error:&error];
    if (error != NULL) {
        NSLog(@"%@", [error localizedDescription]);
    }
    
    NSData* newLine = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
    [[[task standardInput] fileHandleForWriting] writeData:newLine error:&error];
    if (error != NULL) {
        NSLog(@"%@", [error localizedDescription]);
    }
    
}

-(void)terminateTask {
    [task interrupt];
}

@end

How can I run the commands in root mode with signals like interrupt, suspend handled?

1

There are 1 answers

0
prabhu On

Apparently, the sudo -i command opens another bash, hence the signals are not passed. Solution is to launch my app in root level hence the bash will be launched in root level by default and I don't have to change to sudo manually. Another drawback of using sudo -i is that the user can use exit command to exit from sudo mode which I don't want. So launching the bash from root level is the right solution.