Example of command line access from Cocoa

rharder

Do not read this sign.
I've not found any examples of how to call a command-line utility (ipfw in my case) from a Cocoa app, and all the applications I've seen where people do that are Shareware not open source, so they don't help me.

I know there are already two good firewall config programs: Firewalk X and Brickhouse, but I'm starting an open source config utility. I've been putting off the parts of the code that actually call ipfw 'cause I don't know how!

I'd sure appreciate a few url's or examples if anyone has some.

-Rob
 

ladavacm

Unperson Spotter
Originally posted by rharder
I've not found any examples of how to call a command-line utility (ipfw in my case) from a Cocoa app, and all the applications I've seen where people do that are Shareware not open source, so they don't help me.

I'd sure appreciate a few url's or examples if anyone has some.

-Rob
You do not say whether you use ObjC or Java. If ObjC, it is quite simple: use the standard C or POSIX means of starting external programs.

The C way would be

system( "your command line comes here" );

man 3 system to get more info about system C standard library API.

The POSIX API is safer and has less overhead: check fork(2) and execve(2) family of syscalls.

Since you need superuser privileges to load ipfw rules, a suid root command line wrapper called by your Cocoa app might be better because the Cocoa part then does not need the superuser privileges (and superuser privileges should be restricted to programs with as little complexity as possible, in order to avoid security issues)

Happy hacking!

 

endian

Dis Member
system( "your command line comes here" ); man 3 system to get more info about system C standard library API. The POSIX API is safer and has less overhead: check fork(2) and execve(2) family of syscalls.
You shouldn't do it this way,for reasons that escape me at the moment (search the Omnigroup OSX-dev archives). The recommended way is to use NSTask or NSPipe
 

rharder

Do not read this sign.
RacerX, thanks for the OpenUp idea. It's looking good.

(back from looking) Oh, but the article doesn't discuss running a program externally. I'll look at the source...

-Rob
 

blb

`'
I agree with endian, the best Objective C way is to use NSTask and possibly NSPipe (if you want output from the program). For example:

NSTask *theTask = [ [ NSTask alloc ] init ];
NSPipe *taskPipe = [ [ NSPipe alloc ] init ];

[ theTask setStandardError:taskPipe ];
[ theTask setStandardOutput:taskPipe ];
[ theTask setLaunchPath:mad:"/bin/ls" ];
[ theTask setArguments:[ NSArray arrayWithObject:mad:"-l" ] ];
[ theTask launch ];

Then you can use the task pipe to read output.
 

RacerX

Old Rhapsody User
I had been thinking of making a GUI app for the all the command line stuff I use from time to time (which was why I had planned on looking into the OpenUp source), but this looks like a lot of cool information.:D

Thanks for the thread rharder.
 

ladavacm

Unperson Spotter
Originally posted by endian


You shouldn't do it this way,for reasons that escape me at the moment (search the Omnigroup OSX-dev archives). The recommended way is to use NSTask or NSPipe
I would like to know the reasons (I can guess at some, e.g. internal cocoa mutex management, etc) why a fork(2) could be dangerous, but fork/execve ought to be safe. Needless to say that it gives more control over POSIX aspects of MacOS X (passing descriptors other than 0, 1, and 2, control over zombies, that kind of ilk).

Granted, for 99+ percent of uses, NSTask is probably the better way.
 

rharder

Do not read this sign.
Right, so NSTask <em>does</em> seem to be the answer.

Now I just have to figure out NSPipe! Looks like I can create an NSString out of it by creating the NSString with the initWithData directive and feeding it the NSData I get from the pipe.

If I get this down slick as jello, I'll post it in a nice, neat form. If someone beats me to it, please post it.

-Rob
 

blb

`'
To get data from NSPipe, what I do is use the fileHandleForReading method to get a file handle, then read the data:

NSData *inData;

readHandle = [ taskPipe fileHandleForReading ];
if( ( inData = [ readHandle availableData ] ) && [ inData length ] )
// do something with inData, or [ inData bytes ]

One thing I've found is if your task outputs quite a bit of data, you need to continually read from the pipe, or the task will stall. I've found it to be at around 8k of data when this happens.

Here's to hoping Apple expands on their Cocoa documentation...
 

endian

Dis Member
As a general tip, don't even bother with the documentation unless you need more info about what a class does. Just go straight to the header files; they're pretty well commented
 
Top