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?
Apparently, the
sudo -icommand 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 usingsudo -iis that the user can useexitcommand to exit from sudo mode which I don't want. So launching the bash from root level is the right solution.