In my cocoa app I have to call system() function to launch an external app. The command I use is:
system("./main &");
If I run the app from Xcode, it works fine, because I know the folder where to put main.
If I create an archive, and distribute my .app application, system() can't find "main". Where I have to put it?? Or otherwise, how can I run an app using "./" when I'm not in the folder the application is?
EDIT: Maybe I solved using NSTask, but how can I run "main" in background? Now it opens in a new terminal window.
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/Applications/Multibo/main"];
[task setArguments:[NSArray arrayWithObjects:[NSString stringWithFormat:#"./main &"], nil]];
[task launch];
Thanks
Try appending the full path before the executable's name and use it as an argument to system. Note that system is implementation defined -- its behavior is not guranteed to be the same across systems and its usage is thus not recommended. You should probably look for a suitable alternative such as NSWorkspace.
While dirkgently's answer is directly correct, the real answer is more complex.
First, NSTask is not a generic command line invoker. That is why adding & doesn't do what you expect. In fact, all tasks invoked via NSTask are effectively background.
But you really don't want to use NSTask. You should really be using an XPC service. Now, if your goal is something that runs even after your program exits, you should be looking into LaunchServices.
Related
I'm currently looking for a way to get a list of all the bootable partitions on a Mac?
I know you can get a list of all the volumes? But I don't know how to check if each particular volume is bootable?
Is there a way to do this using swift or objective c?
With objective-c or swift probably not, but you can run an apple script (terminal commands) from your objective-c app (on OS X, I don't know whether it can be done on iOS)
This is how you execute an apple script:
//Begin of the script
NSAppleScript *script = [[NSAppleScript alloc]
initWithSource:#"Tell application \"Terminal\" \n\
do shell script \" some script here \"\n\
end tell"];
NSDictionary *errors = nil;
NSAppleEventDescriptor *result = [script executeAndReturnError:&errors];
NSLog(#"result: %#", result);
NSLog(#"errors: %#", errors);
if(errors==NULL){
NSLog(#"Succeeded");
}
else{
NSLog(#"Failed");
}
//End of the script
The quick and dirty way to do this is to shell out to either bless --info or systemsetup liststartupdisks commands. You can specify that you want the output in plist format to make it easier to parse as well.
As far as executing those commands the typical NSTask should work. You could also use system() I suppose, but it isn't really standard practice on OS X as NSTask has many advantages over it.
A purely code way to do this would be to get the list of disks and then look at each them for the known files that make OS X bootable. Things like the boot.efi file in /System/Library/CoreServices, the mach kernel file, and the contents of /System/Library/CoreServices/SystemVersion.plist.
I have an OSX Application that I am working on. It is not sandboxed (it is an internal application that does things which prevent sandboxing).
At some point my application kicks off an auxiliary application, really just a command line application. I would like to be able to strip this application of being able to do anything except write files into the TMPDIR. I am trying to follow the principle of least privilege.
The current code I am using is:
NSTask* task = [NSTask new];
NSBundle* thisBundle = [NSBundle mainBundle];
[task setArguments:#[a, b, c]];
[task setLaunchPath:[thisBundle pathForAuxiliaryExecutable:#"MyProgram"]];
[task launch];
Is this possible with NSTask? If not, what mechanisms can I use to start MyProgram with very low privileges?
The best tool for this is called XPC. It was kind of weak in 10.7, but in 10.8 it's incredibly powerful. Its entire purpose is to let you segment your program this way, while making the IPC very easy. Some docs:
"Creating XPC Services" in Daemons and Services Programming Guide
NSXPCConnection
Cocoa Interprocess Communication With XPC (WWDC 2012 session)
Introducing XPC (WWDC 2011 session)
ObjectToXPC - 3rdparty library. I haven't played with it but it promises to do object marshaling in 10.7 (see below why object marshaling can be dangerous, though). You could also just use your own JSON protocol or the like.
One of the great changes for XPC in 10.8 is that they added NSSecureCoding. They modified the compiler to inject class information into protocol definitions so that object marshaling can be done more safely. This means that when you say that a ObjC protocol passes an NSString, they can actually check that the object is an NSString. (Before 10.8, there was no class information in the Protocol object. You could only check whether an argument should be "an object.")
Who cares? Well, say I hijack your low-privilege task and trick it into returning an NSSomethingElse rather than an NSString (maybe I just overwrite the isa pointer to modify its class). And let's say that NSSomethingElse has a length method that does something useful to me as the attacker. Now, when your high-privilege task calls [returnedValue length], it's going to run the wrong method. Alternately, maybe NSSomethingElse has no length method, so I can force the high-privelege task to throw a "does not implement selector" exception, which could be useful to me as well. With NSSecureCoding, this kind of attack is much harder. It can introspect the returned object, note that it isn't an NSString, and refuse to return it to the calling code.
Even without the niceties of NSXPCConnection, I recommend XPC for this kind of work if you're targeting 10.7+.
I've run into an while trying to launch an NSTask from inside an NSOperation.
Here's a very simple app I've put together to showcase the problem. When you click the button, an NSOperation is queued. It sets up an NSRunLoop, and calls a method which invokes an NSTask. The task is really simple- it just launches /bin/sleep for two seconds (enough to easily see the spinner when things are working properly).
The app works as advertised, however if you change line 23 of TaskPerformer.m to an autorelease, (sorry, I'm a new poster so I can't link directly) or comment it out entirely (thus leaking the NSTask object), the NSOperation's thread will never exit. Its main runloop seems to be blocking on something.
Now, the problem here is twofold. First off, I don't understand why my thread is blocking, but moreover, if I turn on garbage collection for this application, the same behavior manifests itself. Because there's no way for me to manually release the NSTask, the thread blocks no matter what.
If someone could tell me what's going on, I'd be eternally grateful!
I see a couple different issues in the sample project you posted. In TaskPerformer.m you have:
[task waitUntilExit];
[task launch];
The waitUntilExit call is meant to be called after launching the task, and will simply block and do nothing until the task has completed. If you only care about waiting until the task has finished, and not about getting output from it or anything, then you should be able to just call launch followed by waitUntilExit, and not have to bother messing with the run loop at all.
If you do want to get output from the task though, then you'll want to get its standardOutput, which by default should return you an instance of NSFileHandle. You can then call readDataOfLength: or readDataToEndOfFile, both of which will block and return data from the process when data is available.
Since this entire thing is going to be done in a background thread anyway, it's OK that these methods block, but you wouldn't want to do the same thing on the main thread, since that would lock up the interface for the user until the task was complete. If you did this on the main thread, you'd want to use NSFileHandle's readInBackgroundAndNotify and friends. For a background thread though, using NSRunLoop shouldn't really be necessary.
I figured out what was going on here. Turns out my call to [runLoop run] was setting up the loop and running it indefinitely. It was never falling down into the while (!done) loop. Turns out that after calling run, an NSRunLoop will run until it has no more inputs. Calling release on my NSTask led to exactly this scenario, so (quite by accident) my runloop exited.
The solution was to remove [runLoop run] and simply rely on my own while loop. Hope this helps somebody else!
While playing with RubyCocoa, I keep progressing with my idea for my application. Because my application will be going to use configuration files, I would like to know how I discover the relative path to store these inside my application structure (or if a better idea emerges, please elaborate also the "why").
Also good for me to know is to discover environment variables, such as operating system version, the amount of memory that is available and such. Hyperlinks would be awesome too.
Please notice I use RubyCocoa and thank you for your feedback, comments and answers!
To access inside the application bundle, you use NSBundle. See NSBundle reference. In Obj-C, you use +[NSBundle mainBundle] to get the main bundle of the app. Then you use -[NSBundle pathForResource:ofType:] to get the file. I don't know RubyCocoa syntax, but I assume you know how to translate to it :)
If by the configuration file you mean a user-configurable things, remember you can't write inside the app bundle at runtime. Instead one uses NSUserDefaults. See User Defaults Guide.
Here's some Cocoa code I use to write all the environment variables to the console. Again, I don't use RubyCocoa, so you'll have to translate:
NSProcessInfo *myProcessInfo = [NSProcessInfo processInfo];
NSDictionary *env = [myProcessInfo environment];
for (id key in env)
{
NSLog (#"key: %#, value: %#", key, [env objectForKey: key]);
}
This is probably a stupid question, but how can I execute a shell command from my Cocoa app?
I have the command as a string "command", but can easily manipulate data as needed.
There is no need to get a returned output value.
NSTask is pretty easy to do this with. For a synchronous call, you can use something like this fragment:
NSString *path = #"/path/to/executable";
NSArray *args = [NSArray arrayWithObjects:..., nil];
[[NSTask launchedTaskWithLaunchPath:path arguments:args] waitUntilExit];
The -waitUntilExit call makes sure it finishes before proceeding. If the task can be asynchronous, you can remove that call and just let the NSTask do it's thing.
If you just want to run something and don't care about the output or return code (for example, you want to touch a file), you can just do
system("touch myfile.txt");
Easy as that.
NSTask
Using the NSTask class, your program can run another program as a subprocess and can monitor that program’s execution.