execute lame with NSTask but No Output - objective-c

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

Related

Run NSTask synchronously

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];

Start Application as specific user from LaunchDaemon

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;
}

How would I execute this command with NSTask?

I am trying to execute this task to record some music but I am getting errors. I am sure it's pretty simple:
sox -V6 -t coreaudio 'WavTap' --bits 16 test.wav
It's saying 'no such file or dir: wavtap'.
However, if I run it in the terminal it works fine. I am guessing something to do with the quotes throwing it off?
Any thoughts?
You could try something like this, although i don't know if this is what you're already doing.
NSArray *args = [NSArray arrayWithObjects:#"-V6", #"-t", #"coreaudio", #"'WavTap'", #"--bits", #"16", #"test.wav", nil];
NSTask *task = [[NSTask alloc] init];
#try {
[task setLaunchPath:pathOfSoxCommand];
[task setArguments:args];
[task launch];
}
#catch (NSException *exception) {
NSLog(#"%#",exception.reason);
}
#finally {
[task release];
}

NSTask with long output (OSX)

I've been digging for an answer for a long time but I never get to a working code.
I have a code that uses dd to generate a file. Sometimes it takes a few minutes depending on the size and I thought it would be great to have a progress bar. So far everything works and is tied up. The progress bar, however, doesn't update because I need to constantly send values to it. I found a way to get the current value, I managed to get pv to display the current data, but now I can't get the output in real time inside the application, except in the log.
So far, this is the best I got to:
// Action:
// dd if=/dev/zero bs=1048576 count=500 |pv -Wn -s <size>|of=/Users/me/Desktop/asd.img
// Be careful, it generates files of 500MB!!
NSTask * d1Task = [[NSTask alloc] init];
NSTask * pvTask = [[NSTask alloc] init];
NSTask * d2Task = [[NSTask alloc] init];
[d1Task setLaunchPath:#"/bin/dd"];
[pvTask setLaunchPath:#"/Users/me/Desktop/pv"];
[d2Task setLaunchPath:#"/bin/dd"];
// For pvTask I use something like...
// [[NSBundle mainBundle] pathForAuxiliaryExecutable: #"pv"]
// ... in the final version.
[d1Task setArguments: [NSArray arrayWithObjects:
#"if=/dev/zero"
, #"bs=1048576"
, #"count=500"
, nil]];
[pvTask setArguments:[NSArray arrayWithObjects:
#"-Wn"
, [ NSString stringWithFormat:#"-s %d", 1048576 * 500]
, nil]];
[d2Task setArguments: [NSArray arrayWithObjects:
#"of=/Users/me/Desktop/file.dmg"
, nil]];
NSPipe * pipeBetween1 = [NSPipe pipe];
[d1Task setStandardOutput: pipeBetween1];
[pvTask setStandardInput: pipeBetween1];
NSPipe * pipeBetween2 = [NSPipe pipe];
[pvTask setStandardOutput: pipeBetween2];
[d2Task setStandardInput: pipeBetween2];
// Missing code here
[d1Task launch];
[pvTask launch];
[d2Task launch];
In the missing code part, I tried several things. First I tried an observer, like this:
NSFileHandle * pvOutput = [pipeBetween2 fileHandleForReading];
[pvOutput readInBackgroundAndNotify];
[ [NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(outputData:)
name:NSFileHandleDataAvailableNotification
object:pvOutput
];
No success. I get feedback only in the beginning or the end of the execution, but still no feedbacks during it.
I also tried something like:
[pvOutput setReadabilityHandler:^(NSFileHandle *file) {
// Stuff here
}];
But there is no such method here. Maybe my XCode is outdated? (I use 4.2).
Lately I've been trying the same generic code using /sbin/ping pinging 10 times a server, but no success getting the output. How can I do this? Any documents I can read about this subject?
Thanks!
The pv tool writes the progress output to standard error, so you should establish
another pipe:
NSPipe *pvStderrPipe = [NSPipe pipe];
[pvTask setStandardError:pvStderrPipe];
NSFileHandle *pvError = [pvStderrPipe fileHandleForReading];
[pvError waitForDataInBackgroundAndNotify];
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
object:pvError queue:nil
usingBlock:^(NSNotification *note)
{
NSData *progressData = [pvError availableData];
NSString *progressStr = [[NSString alloc] initWithData:progressData encoding:NSUTF8StringEncoding];
NSLog(#"progress: %#", progressStr);
[pvError waitForDataInBackgroundAndNotify];
}];
A completely different solution might be to use the following feature of "dd":
If dd receives a SIGINFO signal,
the current input and output block counts will be written to the
standard error output in the same format as the standard completion
message.
So you could connect a pipe to stderr of "dd" and call
kill([ddTask processIdentifier], SIGINFO);
at regular intervals. Then you don't need "pv" and probably only one "dd" task.

Code to use NSTask with ps to verify a process is running

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.