Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Relaunching a cocoa app

I have an application that checks its command line parameters and stores values in persistent stores. One of those is a password that I don't want sticking around for people to see with 'ps' and friends. The approach I'm currently looking at is to, after I've stored the values I need, relaunch the process without the command line parameters. My naive approach is this, where args[0] is the path to the application:

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:[args objectAtIndex:0]];
[task launch];
[task release];
[NSApp terminate:nil];

The child is run. However, when my app is terminated the child doesn't seem to orphan but gets stuck. Am I just way off on this one?

More info: So it seems that when I call [NSApp terminate:nil] the NSTask that was launched gets stuck, but if I just exit() then it works fine. However, I'm concerned that things that are open (keychain, plist, etc.) will be in a bad state if I do that.

And note that lots of example code out there is about some watchdog-like process that restarts a separate process when needed. I'm trying to restart the current process that's already running from within that same process.

like image 643
devguydavid Avatar asked Jun 30 '10 00:06

devguydavid


2 Answers

There are plenty of examples on the web, but this one (also below) looks like it has all the code you need. There are more detailed explanations out there, as well.

// gcc -Wall -arch i386 -arch ppc -mmacosx-version-min=10.4 -Os -framework AppKit -o relaunch relaunch.m
 
#import <AppKit/AppKit.h>
 
@interface TerminationListener : NSObject
{
    const char *executablePath;
    pid_t parentProcessId;
}
 
- (void) relaunch;
 
@end
 
@implementation TerminationListener
 
- (id) initWithExecutablePath:(const char *)execPath parentProcessId:(pid_t)ppid
{
    self = [super init];
    if (self != nil) {
        executablePath = execPath;
        parentProcessId = ppid;
 
        // This adds the input source required by the run loop
        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil];
        if (getppid() == 1) {
            // ppid is launchd (1) => parent terminated already
            [self relaunch];
        }
    }
    return self;
}
 
- (void) applicationDidTerminate:(NSNotification *)notification
{
    if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) {
        // parent just terminated
        [self relaunch];
    }
}
 
- (void) relaunch
{
    [[NSWorkspace sharedWorkspace] launchApplication:[NSString stringWithUTF8String:executablePath]];
    exit(0);
}
 
@end
 
int main (int argc, const char * argv[])
{
    if (argc != 3) return EXIT_FAILURE;
 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
    [[[TerminationListener alloc] initWithExecutablePath:argv[1] parentProcessId:atoi(argv[2])] autorelease];
    [[NSApplication sharedApplication] run];
 
    [pool release];
 
    return EXIT_SUCCESS;
}
like image 145
Carl Norum Avatar answered Nov 09 '22 23:11

Carl Norum


I know its a bit late to answer but this answer may help others. Here is a cool trick that can help you.

By using the terminal command, just open your application as a new instance and terminate the current instance.

This is how it is done:

....        
        NSString* path = [[NSBundle mainBundle] bundlePath];

        NSString* cmd = [NSString stringWithFormat:@"open -n %@", path];

        [self runCommand:cmd];

        exit(0);
    }

    /// temrinal function

    -(NSString*)runCommand:(NSString*)commandToRun;
    {
        NSTask *task;
        task = [[NSTask alloc] init];
        [task setLaunchPath: @"/bin/sh"];

        NSArray *arguments = [NSArray arrayWithObjects:
                              @"-c" ,
                              [NSString stringWithFormat:@"%@", commandToRun],
                              nil];
        NSLog(@"run command: %@",commandToRun);
        [task setArguments: arguments];

        NSPipe *pipe;
        pipe = [NSPipe pipe];
        [task setStandardOutput: pipe];

        NSFileHandle *file;
        file = [pipe fileHandleForReading];

        [task launch];

        NSData *data;
        data = [file readDataToEndOfFile];

        NSString *output;
        output = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
        return output;
    }
like image 38
Vikas Bansal Avatar answered Nov 10 '22 01:11

Vikas Bansal