215 lines
6.8 KiB
Objective-C
215 lines
6.8 KiB
Objective-C
|
|
#import <AppKit/AppKit.h>
|
|
#import "SUInstaller.h"
|
|
#import "SUHost.h"
|
|
#import "SUStandardVersionComparator.h"
|
|
#import "SUStatusController.h"
|
|
#import "SUPlainInstallerInternals.h"
|
|
#import "SULog.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
#define LONG_INSTALLATION_TIME 5 // If the Installation takes longer than this time the Application Icon is shown in the Dock so that the user has some feedback.
|
|
#define CHECK_FOR_PARENT_TO_QUIT_TIME .5 // Time this app uses to recheck if the parent has already died.
|
|
|
|
@interface TerminationListener : NSObject
|
|
{
|
|
const char *hostpath;
|
|
const char *executablepath;
|
|
pid_t parentprocessid;
|
|
const char *folderpath;
|
|
NSString *selfPath;
|
|
NSString *installationPath;
|
|
NSTimer *watchdogTimer;
|
|
NSTimer *longInstallationTimer;
|
|
SUHost *host;
|
|
BOOL shouldRelaunch;
|
|
}
|
|
|
|
- (void) parentHasQuit;
|
|
|
|
- (void) relaunch;
|
|
- (void) install;
|
|
|
|
- (void) showAppIconInDock:(NSTimer *)aTimer;
|
|
- (void) watchdog:(NSTimer *)aTimer;
|
|
|
|
@end
|
|
|
|
@implementation TerminationListener
|
|
|
|
- (id) initWithHostPath:(const char *)inhostpath executablePath:(const char *)execpath parentProcessId:(pid_t)ppid folderPath: (const char*)infolderpath shouldRelaunch:(BOOL)relaunch
|
|
selfPath: (NSString*)inSelfPath
|
|
{
|
|
if( !(self = [super init]) )
|
|
return nil;
|
|
|
|
hostpath = inhostpath;
|
|
executablepath = execpath;
|
|
parentprocessid = ppid;
|
|
folderpath = infolderpath;
|
|
selfPath = [inSelfPath retain];
|
|
shouldRelaunch = relaunch;
|
|
|
|
BOOL alreadyTerminated = (getppid() == 1); // ppid is launchd (1) => parent terminated already
|
|
|
|
if( alreadyTerminated )
|
|
[self parentHasQuit];
|
|
else
|
|
watchdogTimer = [[NSTimer scheduledTimerWithTimeInterval:CHECK_FOR_PARENT_TO_QUIT_TIME target:self selector:@selector(watchdog:) userInfo:nil repeats:YES] retain];
|
|
|
|
return self;
|
|
}
|
|
|
|
|
|
-(void) dealloc
|
|
{
|
|
[longInstallationTimer invalidate];
|
|
[longInstallationTimer release];
|
|
longInstallationTimer = nil;
|
|
|
|
[selfPath release];
|
|
selfPath = nil;
|
|
|
|
[installationPath release];
|
|
|
|
[watchdogTimer release];
|
|
watchdogTimer = nil;
|
|
|
|
[host release];
|
|
host = nil;
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
|
|
-(void) parentHasQuit
|
|
{
|
|
[watchdogTimer invalidate];
|
|
longInstallationTimer = [[NSTimer scheduledTimerWithTimeInterval: LONG_INSTALLATION_TIME
|
|
target: self selector: @selector(showAppIconInDock:)
|
|
userInfo:nil repeats:NO] retain];
|
|
|
|
if( folderpath )
|
|
[self install];
|
|
else
|
|
[self relaunch];
|
|
}
|
|
|
|
- (void) watchdog:(NSTimer *)aTimer
|
|
{
|
|
ProcessSerialNumber psn;
|
|
if (GetProcessForPID(parentprocessid, &psn) == procNotFound)
|
|
[self parentHasQuit];
|
|
}
|
|
|
|
- (void)showAppIconInDock:(NSTimer *)aTimer;
|
|
{
|
|
ProcessSerialNumber psn = { 0, kCurrentProcess };
|
|
TransformProcessType( &psn, kProcessTransformToForegroundApplication );
|
|
}
|
|
|
|
|
|
- (void) relaunch
|
|
{
|
|
if (shouldRelaunch)
|
|
{
|
|
NSString *appPath = nil;
|
|
if( !folderpath || strcmp(executablepath, hostpath) != 0 )
|
|
appPath = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:executablepath length:strlen(executablepath)];
|
|
else
|
|
appPath = installationPath;
|
|
[[NSWorkspace sharedWorkspace] openFile: appPath];
|
|
}
|
|
|
|
if (folderpath)
|
|
{
|
|
NSError *theError = nil;
|
|
if( ![SUPlainInstaller _removeFileAtPath: [SUInstaller updateFolder] error: &theError] )
|
|
SULog( @"Couldn't remove update folder: %@.", theError );
|
|
}
|
|
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
|
|
[[NSFileManager defaultManager] removeFileAtPath: selfPath handler: nil];
|
|
#else
|
|
[[NSFileManager defaultManager] removeItemAtPath: selfPath error: NULL];
|
|
#endif
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
|
|
- (void) install
|
|
{
|
|
NSBundle *theBundle = [NSBundle bundleWithPath: [[NSFileManager defaultManager] stringWithFileSystemRepresentation: hostpath length:strlen(hostpath)]];
|
|
host = [[SUHost alloc] initWithBundle: theBundle];
|
|
installationPath = [[host installationPath] copy];
|
|
|
|
// Perhaps a poor assumption but: if we're not relaunching, we assume we shouldn't be showing any UI either. Because non-relaunching installations are kicked off without any user interaction, we shouldn't be interrupting them.
|
|
if (shouldRelaunch) {
|
|
SUStatusController* statusCtl = [[SUStatusController alloc] initWithHost: host]; // We quit anyway after we've installed, so leak this for now.
|
|
[statusCtl setButtonTitle: SULocalizedString(@"Cancel Update",@"") target: nil action: Nil isDefault: NO];
|
|
[statusCtl beginActionWithTitle: SULocalizedString(@"Installing update...",@"")
|
|
maxProgressValue: 0 statusText: @""];
|
|
[statusCtl showWindow: self];
|
|
}
|
|
|
|
[SUInstaller installFromUpdateFolder: [[NSFileManager defaultManager] stringWithFileSystemRepresentation: folderpath length: strlen(folderpath)]
|
|
overHost: host
|
|
installationPath: installationPath
|
|
delegate: self synchronously: NO
|
|
versionComparator: [SUStandardVersionComparator defaultComparator]];
|
|
}
|
|
|
|
- (void) installerFinishedForHost:(SUHost *)aHost
|
|
{
|
|
[self relaunch];
|
|
}
|
|
|
|
- (void) installerForHost:(SUHost *)host failedWithError:(NSError *)error
|
|
{
|
|
// Perhaps a poor assumption but: if we're not relaunching, we assume we shouldn't be showing any UI either. Because non-relaunching installations are kicked off without any user interaction, we shouldn't be interrupting them.
|
|
if (shouldRelaunch)
|
|
NSRunAlertPanel( @"", @"%@", @"OK", @"", @"", [error localizedDescription] );
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
@end
|
|
|
|
int main (int argc, const char * argv[])
|
|
{
|
|
if( argc < 5 || argc > 6 )
|
|
return EXIT_FAILURE;
|
|
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
//ProcessSerialNumber psn = { 0, kCurrentProcess };
|
|
//TransformProcessType( &psn, kProcessTransformToForegroundApplication );
|
|
[[NSApplication sharedApplication] activateIgnoringOtherApps: YES];
|
|
|
|
#if 0 // Cmdline tool
|
|
NSString* selfPath = nil;
|
|
if( argv[0][0] == '/' )
|
|
selfPath = [[NSFileManager defaultManager] stringWithFileSystemRepresentation: argv[0] length: strlen(argv[0])];
|
|
else
|
|
{
|
|
selfPath = [[NSFileManager defaultManager] currentDirectoryPath];
|
|
selfPath = [selfPath stringByAppendingPathComponent: [[NSFileManager defaultManager] stringWithFileSystemRepresentation: argv[0] length: strlen(argv[0])]];
|
|
}
|
|
#else
|
|
NSString* selfPath = [[NSBundle mainBundle] bundlePath];
|
|
#endif
|
|
|
|
[NSApplication sharedApplication];
|
|
[[[TerminationListener alloc] initWithHostPath: (argc > 1) ? argv[1] : NULL
|
|
executablePath: (argc > 2) ? argv[2] : NULL
|
|
parentProcessId: (argc > 3) ? atoi(argv[3]) : 0
|
|
folderPath: (argc > 4) ? argv[4] : NULL
|
|
shouldRelaunch: (argc > 5) ? atoi(argv[5]) : 1
|
|
selfPath: selfPath] autorelease];
|
|
[[NSApplication sharedApplication] run];
|
|
|
|
[pool drain];
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|