I have an .app file that I'm running through NSTask and I wish the thread to be blocked till .app execution is over.
My current code is:
NSTask *task = [[NSTask alloc] init];
task.launchPath = #"/bin/bash";
task.arguments = [NSArray arrayWithObjects:#"-c", "/usr/bin/open myApp.app", nil];
[task launch];
[task waitUntilExit]; // doesn't guarantee task will wait until exit according to docs
I know I can use NSTask.terminationHandler but since I have a lot of tasks I don't want to get into a callback hell situation (and also I don't care if everything will run sync and take some time)
(I also tried adding nohup to the execution command but it didn't made the affect i wanted.)
Is there a way to execute NSTask synchronously and wait until execution is over?
It seems that the problem was in the open command which executes the app and return context even if execution is not over.
I found out that open has a -W argument:
-W, --wait-apps Blocks until the used applications are closed (even if they were already running).
adding this argument solved my problem.
final code:
NSTask *task = [[NSTask alloc] init];
task.launchPath = #"/bin/bash";
task.arguments = [NSArray arrayWithObjects:#"-c", "/usr/bin/open -W myApp.app", nil];
[task launch];
[task waitUntilExit];
Related
I am currently trying to launch an Application in /Applications from a LaunchDaemon as specific user. Is there a way that I can launch this application without giving the program root privileges? I am writing the Daemon in objective C.
In your launch daemon's plist, which should reside in /Library/LaunchDaemons, you can set the UserName key:
<key>UserName</key>
<string>userForThisProcess</string>
where userForThisProcess is the user you want to use to run the application.
I have solved this issue in a little quirky way now. I use NSTask in conjunction with sudo and open. Maybe someone needs this in the future:
+ (bool)start_app_bundle_as_user:(NSString *)path with_user_name:(NSString *)user_name
{
NSString *cmd = [NSString stringWithFormat:#"/usr/bin/sudo -i -u %# -- open -a %#", user_name, path];
NSTask *task = [[NSTask alloc] init];
NSArray *args = [NSArray arrayWithObjects:#"-c", cmd, nil];
[task setLaunchPath:#"/bin/sh"];
[task setArguments:args];
[task launch];
[task waitUntilExit];
return [task terminationStatus] == 0;
}
I'm new to Objective-C. Currently I'm trying to execute lame with NSTask. The following code seems to be working because Xcode's output space shows me lame's standardoutput i.e. shows same as lame's output on Terminal.
But I can't get any output file i.e. test.mp3 on my desktop. Why I can't get any output? Is there any wrong with my code?
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/local/bin/lame"];
[task setArguments:[NSArray arrayWithObjects:#"/Users/xanadu62/Music/test.wav",nil]];
[task setStandardOutput:[NSFileHandle fileHandleForWritingAtPath:#"/Users/xanadu62/Desktop/test.mp3"]];
[task launch];
Also, I'd like to use "--preset extreme" as lame option. But "task setArguments:" doesn't allow to use this option as argument. I'd like to know how can I solve this issue too.
Try it this way:
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/local/bin/lame"];
[task setArguments: [NSArray arrayWithObjects:
#"--preset",
#"extreme",
#"/Users/xanadu62/Music/test.wav",
#"/Users/xanadu62/Desktop/test.mp3",
nil]
];
[task launch];
You don't need to use pipes.
usage: lame [options] <infile> [outfile]
<infile> and/or <outfile> can be "-", which means stdin/stdout.
Never used lame, but by looking at the docs the correct terminal command would be
"lame --preset extreme /Users/lawrencepires/Desktop/test.mp3 /Users/lawrencepires/Desktop/test1.mp3"
test.mp3 being the input file, test1.mp3 being the output file.
Working Code - (may be worth changing for live output)
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
[self lameconvert];
}
- (void)lameconvert {
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/local/bin/lame"];
NSArray *argArray = [NSArray arrayWithObjects:#"--preset",#"extreme",#"/Users/xanadu62/Music/test.wav",#"/Users/xanadu62/Music/test.wav",nil];
[task setArguments:argArray];
[task launch];
[task waitUntilExit];
NSLog(#"Conversion Complete");
}
#end
Ok. There are several questions on stack overflow about this. This question was the only question comes closest to mines, but it uses notifications.
The code is very simple. Create a new empty Mac OSX project and just paste the following code in the applicationDidFinishLaunching: method. It supposed to get the path of any executable file (in this case GIT).
NSTask *aTask = [[NSTask alloc] init];
NSPipe *outputPipe = [NSPipe pipe];
NSPipe *errorPipe = [NSPipe pipe];
[aTask setStandardOutput: outputPipe];
[aTask setStandardError: errorPipe];
[aTask setArguments:[NSArray arrayWithObject:#"which"]];
[aTask setLaunchPath:#"/usr/bin/git"];
NSFileHandle *outputFileHandler = [outputPipe fileHandleForReading];
NSFileHandle *errorFileHandler = [errorPipe fileHandleForReading];
[aTask launch];
[aTask waitUntilExit];
// Task launched now just read and print the data
NSData *data = [outputFileHandler readDataToEndOfFile];
NSString *outPutValue = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSData *errorData = [errorFileHandler readDataToEndOfFile];
NSString *errorValue = [[NSString alloc] initWithData:errorData encoding:NSUTF8StringEncoding];
NSLog(#"Error value: %#",errorValue);
NSLog(#"Output Value: %#",outPutValue);
This code sets up two reading pipes and runs one command: which git.
If i run this in XCode i get this results corretly:
Error value: ""
Output Value: /usr/bin/git
If i go to my build/Products/Debug folder and double click the executable file, i get this message printed on the console app:
Question: So, what is really the problem here? please just dont make an alternative solution... I also want to know what the problem is.. thanks.
OK turns out the answer is on stack overflow, but its spread across different questions.
The question was asked here -> Commands with NSTask and here -> NSTask launch path not accessible as well
But their answers as of this date arent clear as to what the problem was. It's only after reading the question from NSTask not picking up $PATH from the user's environment (the question's title was misleading) and with these two answers NSTask not picking up $PATH from the user's environment and Find out location of an executable file in Cocoa that I realized the solution.
It looks like this is about setting up either NS
Task or the user's shell (e.g., ~/.bashrc) so that the correct
environment ($PATH) is seen by NSTask.
Solution:
[task setLaunchPath:#"/bin/bash"];
NSArray *args = [NSArray arrayWithObjects:#"-l",
#"-c",
#"which git", //Assuming git is the launch path you want to run
nil];
[task setArguments: args];
However this assumes the user's shell is always bash and it will fail for others. Solve this by determining the shell.
NSDictionary *environmentDict = [[NSProcessInfo processInfo] environment];
NSString *shellString = [environmentDict objectForKey:#"SHELL"];
I am having endless problems checking to see if the screen saver is running. If I use an NSTask with ps, it crashes or hangs on a lot of users. If I use notifications it seems to be spotty for others.
Any ideas as to why this NSTask is flakey? (Yes, I know it's messy for now as I debug)
-(BOOL)checkScreenSaverRunning
{
MYLog(#"Building task to check screen saver running");
BOOL foundSaver=FALSE;
NSTask *task;
int i;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/ps"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: #"-ax", nil];
[task setArguments: arguments];
NSPipe *stdpipe;
stdpipe = [NSPipe pipe];
[task setStandardOutput: stdpipe];
NSFileHandle *stdfile;
stdfile = [stdpipe fileHandleForReading];
MYLog(#"Launching task to check screen saver running");
[task launch];
while ([task isRunning]){
NSData *stddata;
stddata = [stdfile readDataToEndOfFile];
if([stddata length]>0){
NSString *stdstring = [[NSString alloc] initWithData:stddata
encoding:NSUTF8StringEncoding];
NSArray *stdReturnValues=[stdstring componentsSeparatedByString:#"\n"];
for(i=0;i<[stdReturnValues count];i++){
if([[stdReturnValues objectAtIndex:i]
rangeOfString:#"ScreenSaverEngine"].location != NSNotFound){
foundSaver=TRUE;
MYLog(#"Found screensaver in running processes");
}
}
[stdstring release];
stdstring=nil;
}
}
MYLog(#"Task ended");
[task release];
if(foundSaver)screenSaverIsActive=TRUE;
else screenSaverIsActive=FALSE;
return(foundSaver);
}
What is your higher-level purpose for wanting to know if the screen saver is running? There may be a better way to accomplish that.
If you're trying to diagnose a crash or a hang, show the crash or hang report.
Anyway, if you're going to spawn a subprocess for this, you should probably use killall -0 ScreenSaverEngine instead of ps. killall will find a process by name for you. Using the signal 0 (-0) means "just test for process existence, don't actually signal it". Do [task setStandardError:[NSFileHandle fileHandleWithNullDevice]] to make sure its output goes nowhere. You determine if the process existed by examining the success or failure status of the task after it terminates.
I mounted the hard drive by using the GUI in Mac.
However, I want to mount the hard drive by using the terminal commands.
How can I execute a terminal command mount_smbfs from my Objective-C Cocoa application?
NSTask* task = [[NSTask alloc] init];
[task setLaunchPath:#"/sbin/mount_smbfs"];
[task setArguments:[NSArray arrayWithObjects:#"//user:50000#smb://192.168.2.1/Share",#"Volumes/C$/upload", nil]];
[task launch];
Here is my edited with my code Could you please help me?
You can wrap the call to mount_smbfs in NSTask to execute it from your Obj-C program:
NSTask* task = [NSTask new];
[task setLaunchPath:#"/sbin/mount_smbfs"];
[task setArguments:[NSArray arrayWithObjects:#"//myUser:myPassword#SERVER/share", #"mountPath", nil]];
In setArguments you provide an array with at least 2 elements: the path to the share, and the mount point.
Also check man mount_smbfs for more argument options.
It's simpler to do this with AppleScript:
- (BOOL) mount {
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:
#"tell application \"Finder\"\n"
" mount volume \"smb://server.domain/SomeMountPoint\"\n"
"end tell"];
if (!script) {
NSLog(#"Error creating AppleScript object");
return NO;
}
NSDictionary *errorMessage = nil;
NSAppleEventDescriptor *result = [script executeAndReturnError:&errorMessage];
return (BOOL)result;
}
There are some limitations:
You have to use NSAppleScript on the main thread.
Your application won't respond to any events while Finder tries to mount the volume.
If mounting fails, you don't have any control over how Finder presents the error message.