/* * $Id$ * MacPorts.Framework * * Authors: * Randall H. Wood * * Copyright (c) 2007 Randall H. Wood * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright owner nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #import "MPInterpreter.h" #import "MPMacPorts.h" #include "BetterAuthorizationSampleLib.h" #include "MPHelperCommon.h" #include "MPHelperNotificationsProtocol.h" static AuthorizationRef internalMacPortsAuthRef; static NSString* GenericPKGPath = @"/Library/Tcl"; static NSString* PKGPath = @"/opt/local/libexec/macports/lib/"; static NSTask* aTask; #pragma mark - @implementation MPInterpreter #pragma mark Notifications Code int Notifications_Send(int objc, Tcl_Obj *CONST objv[], int global, Tcl_Interp *interpreter) { NSString *name; NSMutableString *msg; //Our info dictionary is of size 5 and contains the following keys //NOTIFICATION_NAME - e.g. MPWARN, MPDEBUG etc. //CHANNEL - eg. stdout, stderr //PREFIX - prefix string for this message e.g. DEBUG: //METHOD - the function whose operation led to this notification eg. sync, selfupdate //MESSAGE - the message logged to channel NSMutableDictionary *info = [NSMutableDictionary dictionaryWithCapacity:5]; MPNotifications *mln = [MPNotifications sharedListener]; int tclCount; int tclResult; const char **tclElements; name = [NSString stringWithUTF8String:Tcl_GetString(*objv)]; //NSLog(@"name is %@", name); [info setObject:name forKey:MPNOTIFICATION_NAME]; //Name and Notification constants should match. Convention //used is MPPriorityNotification. Is it ok to just return TCL_OK ? if( [mln checkIfNotificationBlocked:name] ){ return TCL_OK; } ++objv; --objc; tclResult = Tcl_SplitList(interpreter, Tcl_GetString(*objv), &tclCount, &tclElements); //NSLog(@"tclElements is %S and tclCount is %i", &tclElements, tclCount); if (tclResult == TCL_OK) { //I have sacrificed generality for simplicity in the code below if (tclCount > 0) { [info setObject:[NSString stringWithUTF8String:tclElements[0]] forKey:MPCHANNEL]; if(tclElements[1]) [info setObject:[NSString stringWithUTF8String:tclElements[1]] forKey:MPPREFIX]; else [info setObject:@"None" forKey:MPPREFIX]; } else { [info setObject:@"None" forKey:MPCHANNEL]; [info setObject:@"None" forKey:MPPREFIX]; } //Get ui_* message separately Hopefully this should never be null ++objv; --objc; if(objv != NULL) { msg = [NSMutableString stringWithUTF8String:Tcl_GetString(*objv)]; //NSLog(@"Message is %@", msg); //strip off "--->" over here NSArray * temp = [msg componentsSeparatedByString:@"--->"]; [msg setString:[temp componentsJoinedByString:@""]]; [info setObject:msg forKey:MPMESSAGE]; } //Get the Tcl function that called this method if (! [[mln performingTclCommand] isEqualToString:@""]) { [info setObject:[mln performingTclCommand] forKey:MPMETHOD]; } if (global != 0) { [[NSDistributedNotificationCenter defaultCenter] postNotificationName:name object:nil userInfo:info]; } else { [[NSNotificationCenter defaultCenter] postNotificationName:name object:nil userInfo:info]; } } else { return TCL_ERROR; } return TCL_OK; } int Notifications_Command(ClientData clientData, Tcl_Interp *interpreter, int objc, Tcl_Obj *CONST objv[]) { MPNotifications * mln = [MPNotifications sharedListener]; //Should I do the filtering in Notificaitons_Send instead? if( [mln checkIfNotificationBlocked:MPALL] ) { NSLog(@"ALL NOTIFICATIONS BLOCKED"); return TCL_OK; } NSString *action = nil; int returnCode = TCL_ERROR; ++objv, --objc; if (objc) { action = [NSString stringWithUTF8String:Tcl_GetString(*objv)]; ++objv, --objc; if ([action isEqualToString:@"send"]) { if ([[NSString stringWithUTF8String:Tcl_GetString(*objv)] isEqualToString:@"global"]) { ++objv, --objc; returnCode = Notifications_Send(objc, objv, 1, interpreter); } else { returnCode = Notifications_Send(objc, objv, 0, interpreter); } } } return returnCode; } #pragma mark - #pragma mark MPInterpreter Code //This variable is set during initialization and is //not changed thereafter. Is sole purpose is to enable //passing the path to macports1.0 package to the helper //tool static NSString * tclInterpreterPkgPath = nil; +(NSString*) PKGPath { return PKGPath; } +(void) setPKGPath:(NSString*)newPath { if([PKGPath isNotEqualTo:newPath]) { [PKGPath release]; PKGPath = [newPath copy]; //I should check if interp is nil. *not needed now MPInterpreter *interp = (MPInterpreter*) [[[NSThread currentThread] threadDictionary] objectForKey:@"sharedMPInterpreter"]; [interp resetTclInterpreterWithPath:PKGPath]; } } #pragma mark - #pragma mark Internal Methods //Internal method for initializing actual C Tcl interpreter //Should I be using a double pointer like is done for NSError ? -(BOOL) initTclInterpreter:(Tcl_Interp * *)interp withPath:(NSString *)path { BOOL result = NO; *interp = Tcl_CreateInterp(); if(*interp == NULL) { NSLog(@"Error in Tcl_CreateInterp, aborting."); return result; } if(Tcl_Init(*interp) == TCL_ERROR) { NSLog(@"Error in Tcl_Init: %s", Tcl_GetStringResult(*interp)); Tcl_DeleteInterp(*interp); return result; } if (path == nil) path = PKGPath; /* NSString * mport_fastload = [[@"source [file join \"" stringByAppendingString:path] stringByAppendingString:@"\" macports1.0 macports_fastload.tcl]"]; if(Tcl_Eval(*interp, [mport_fastload UTF8String]) != TCL_OK) { NSLog(@"Error in Tcl_EvalFile macports_fastload.tcl: %s", Tcl_GetStringResult(*interp)); Tcl_DeleteInterp(*interp); return result; }*/ Tcl_CreateObjCommand(*interp, "notifications", Notifications_Command, NULL, NULL); if(Tcl_PkgProvide(*interp, "notifications", "1.0") != TCL_OK) { NSLog(@"Error in Tcl_PkgProvide: %s", Tcl_GetStringResult(*interp)); Tcl_DeleteInterp(*interp); return result; } if(Tcl_EvalFile(*interp, [[[NSBundle bundleWithIdentifier:@"org.macports.frameworks.macports"] pathForResource:@"init" ofType:@"tcl"] UTF8String]) != TCL_OK) { const char * path = [[[NSBundle bundleWithIdentifier:@"org.macports.frameworks.macports"] pathForResource:@"init" ofType:@"tcl"] UTF8String]; //printf("Test: %s", path); NSLog(@"Error in Tcl_EvalFile init.tcl: %s", Tcl_GetStringResult(*interp)); Tcl_DeleteInterp(*interp); return result; } result = YES; return result; } //Internal method for setting port Tcl_interpreter options -(BOOL) setOptions:(NSArray *)options forTclInterpreter:(Tcl_Interp * *)interp { BOOL result = NO; //I wish I could use fast enumeration if (options != nil) { if ([options count] > 0 ) { NSEnumerator * optionsEnum = [options objectEnumerator]; id opt; while ((opt = [optionsEnum nextObject])) { if (Tcl_Eval(*interp , [[NSString stringWithFormat:@"set ui_options(%@) \"yes\"", opt] UTF8String]) != TCL_OK) { NSLog(@"Error in Tcl_Eval for set ui_options: %s", Tcl_GetStringResult(*interp)); return result; } } result = YES; return result; } } return result; } //Wrapper method for above. Used when -(BOOL) setOptionsForNewTclPort:(NSArray *)options { BOOL result = NO; //First delete our internal Tcl interpreter Tcl_DeleteInterp(_interpreter); if (tclInterpreterPkgPath == nil) result = [self initTclInterpreter:&_interpreter withPath:PKGPath]; else result = [self initTclInterpreter:&_interpreter withPath:tclInterpreterPkgPath]; BOOL tempResult = [self setOptions:options forTclInterpreter:&_interpreter]; return (result && tempResult) ; } -(BOOL) resetTclInterpreterWithPath:(NSString*) path { Tcl_DeleteInterp(_interpreter); return [self initTclInterpreter:&_interpreter withPath:PKGPath]; } - (id) initWithPkgPath:(NSString *)path portOptions:(NSArray *)options { if (self = [super init]) { [self initTclInterpreter:&_interpreter withPath:path]; //set port options maybe I should do this elsewhere? defaultPortOptions = [NSArray arrayWithObjects: MPDEBUGOPTION, nil]; if (options == nil) options = defaultPortOptions; [self setOptions:options forTclInterpreter:&_interpreter]; //Initialize helperToolInterpCommand helperToolInterpCommand = @""; helperToolCommandResult = @""; //Initialize the MacPorts Tcl Package Path string tclInterpreterPkgPath = [NSString stringWithString:path]; } return self; } #pragma mark API methods - (id) init { NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL existsAsDirectory = NO; BOOL containsMacPortsTcl = NO; NSString *macportsDir = [PKGPath stringByAppendingPathComponent:@"macports1.0"]; NSString *macportsFile = [macportsDir stringByAppendingPathComponent:@"macports.tcl"]; [fileManager fileExistsAtPath:macportsDir isDirectory:&existsAsDirectory]; if (existsAsDirectory) { containsMacPortsTcl = [fileManager fileExistsAtPath:macportsFile isDirectory:nil]; } if (!containsMacPortsTcl) { return [self initWithPkgPath:GenericPKGPath portOptions:nil]; } return [self initWithPkgPath:PKGPath portOptions:nil]; } + (MPInterpreter*)sharedInterpreterWithPkgPath:(NSString *)path { return [self sharedInterpreterWithPkgPath:path portOptions:nil]; } + (MPInterpreter*)sharedInterpreter{ return [self sharedInterpreterWithPkgPath:PKGPath]; } + (MPInterpreter*)sharedInterpreterWithPkgPath:(NSString *)path portOptions:(NSArray *)options { @synchronized(self) { if ([PKGPath isNotEqualTo:path]) { [self setPKGPath:path]; } if ([[[NSThread currentThread] threadDictionary] objectForKey:@"sharedMPInterpreter"] == nil) { [[self alloc] initWithPkgPath:path portOptions:options]; // assignment not done here } } return [[[NSThread currentThread] threadDictionary] objectForKey:@"sharedMPInterpreter"]; } + (id)allocWithZone:(NSZone*)zone { @synchronized(self) { if ([[[NSThread currentThread] threadDictionary] objectForKey:@"sharedMPInterpreter"] == nil) { [[[NSThread currentThread] threadDictionary] setObject:[super allocWithZone:zone] forKey:@"sharedMPInterpreter"]; return [[[NSThread currentThread] threadDictionary] objectForKey:@"sharedMPInterpreter"]; // assignment and return on first allocation } } return nil; // subsequent allocation attempts return nil } - (id)copyWithZone:(NSZone*)zone { return self; } - (id)retain { return self; } - (NSUInteger)retainCount { return NSUIntegerMax; //denotes an object that cannot be released } - (void)release { } - (id)autorelease { return self; } #pragma Port Operations #pragma Port Settings #pragma Utilities - (NSArray *)arrayFromTclListAsString:(NSString *)list { NSMutableArray *array; int tclCount; int tclResult; int i; const char **tclElements; const char * string = [list UTF8String]; tclResult = Tcl_SplitList(_interpreter, [list UTF8String], &tclCount, &tclElements); if (tclResult == TCL_OK) { array = [[NSMutableArray alloc] initWithCapacity:tclCount]; for (i = 0; i < tclCount; i++) { [array addObject:[NSString stringWithUTF8String:tclElements[i]]]; } } else { array = [[[NSMutableArray alloc] init] autorelease]; } Tcl_Free((char *)tclElements); return [NSArray arrayWithArray:array]; } - (NSDictionary *)dictionaryFromTclListAsString:(NSString *)list { NSDictionary * foo = [NSDictionary dictionaryWithDictionary:[self mutableDictionaryFromTclListAsString:list]]; return foo; } - (NSMutableDictionary *)mutableDictionaryFromTclListAsString:(NSString *)list { NSMutableDictionary *dictionary; NSArray *array; int i; array = [self arrayFromTclListAsString:list]; dictionary = [[NSMutableDictionary alloc] initWithCapacity:[array count]]; unsigned int array_count = [array count]; for (i = 0; i < array_count; i += 2) { [dictionary setObject:[array objectAtIndex:(i + 1)] forKey:[array objectAtIndex:i]]; } return dictionary; } - (NSArray *)getVariableAsArray:(NSString *)variable { return [self arrayFromTclListAsString:[NSString stringWithUTF8String:Tcl_GetVar(_interpreter, [variable UTF8String], TCL_LIST_ELEMENT)]]; } - (NSString *)getVariableAsString:(NSString *)variable { return [NSString stringWithUTF8String:Tcl_GetVar(_interpreter, [variable UTF8String], 0)]; } #pragma mark - #pragma mark -evaluateString* routines - (NSString *)evaluateStringAsString:(NSString *)statement error:(NSError**)mportError{ //NSLog(@"Calling evaluateStringAsString with argument %@", statement); NSLog(@"Statement: %@", statement); int return_code = Tcl_Eval(_interpreter, [statement UTF8String]); //Should I check for (return_code != TCL_Ok && return_code != TCL_RETURN) instead ? if (return_code != TCL_OK) { Tcl_Obj * interpObj = Tcl_GetObjResult(_interpreter); int length, errCode; NSString * errString = [NSString stringWithUTF8String:Tcl_GetStringFromObj(interpObj, &length)]; //NSLog(@"TclObj string is %@ with length %d", errString , length); errCode = Tcl_GetErrno(); //NSLog(@"Errno Id is %@ with value %d", [NSString stringWithUTF8String:Tcl_ErrnoId()], errCode); //NSLog(@"Errno Msg is %@", [NSString stringWithUTF8String:Tcl_ErrnoMsg(errCode)]); //Handle errors here ... Framework users can do !mportError to find out if //method was successful NSString *descrip = NSLocalizedString(errString, @""); NSDictionary *errDict; //For now all error codes are TCL_ERROR //Create underlying error - For now I'll create the underlying Posix Error NSError *undError = [[[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errCode userInfo:nil] autorelease]; //Create and return custom domain error NSArray *objArray = [NSArray arrayWithObjects:descrip, undError, nil]; NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSUnderlyingErrorKey, nil]; errDict = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray]; if (mportError != NULL) { *mportError = [[[NSError alloc] initWithDomain:MPFrameworkErrorDomain code:TCL_ERROR userInfo:errDict] autorelease]; } return nil; } return [NSString stringWithUTF8String:Tcl_GetStringResult(_interpreter)]; } - (NSString *)evaluateStringWithPossiblePrivileges:(NSString *)statement error:(NSError **)mportError { //N.B. I am going to insist that funciton users not pass in nil for the //mportError parameter NSString * result; [[MPNotifications sharedListener] sendIPCNotification:@"MPInfoNotification_&MP&_stdout_&MP&_None_&MP&_Starting up"]; // Is this the best way to know if the running user can use macports without privileges? if ([[NSFileManager defaultManager] isWritableFileAtPath:PKGPath]) { result = [self evaluateStringWithMPPortProcess:statement error:mportError]; } else { result = [self evaluateStringWithMPHelperTool:statement error:mportError]; } [[MPNotifications sharedListener] sendIPCNotification:@"MPInfoNotification_&MP&_stdout_&MP&_None_&MP&_Shutting down"]; return result; } //NOTE: We expect the Framework client to initialize the AuthorizationRef //Completely before calling any privileged operation. If not we will pass in //a barely initialzed one. This method returns nil if there is an error - (NSString *) evaluateStringWithMPHelperTool:(NSString *) statement error:(NSError **)mportError { OSStatus err; BASFailCode failCode; NSString * bundleID; NSDictionary * request; CFDictionaryRef response; NSString * result = nil; response = NULL; //Creating file path for IPC with helper tool NSString * ipcFilePath = [NSString stringWithFormat:@"%@_%@", @kServerSocketPath, [NSDate date]]; NSString * ipcFilePathCopy = [NSString stringWithString:ipcFilePath]; //We need to use the notificationsObject to set up IPC with the helper tool //I hope BAS's main method is blocking ... it should be since we obtain //a return value MPNotifications * notificationObject = [MPNotifications sharedListener]; //if ([notificationObject respondsToSelector:@selector(prepareIPCServerThread)]) { NSLog(@"PREPARING SERVER THREAD"); [notificationObject prepareIPCServerThread]; //} //if ([notificationObject respondsToSelector:@selector(startServerThread)]) { NSThread * cThread = [NSThread currentThread]; NSLog(@"STARTING SERVER THREAD with previous thread %@", [cThread threadDictionary]); //This is important to note ... the tcl command being executed is saved in the //current thread's thread dictionary in the upper tier method that calls this one. // This means we are only going to guarantee //thread saftey for Framework clients at the the level of MPMacPorts, MPPorts etc. and above NSDictionary * serverInfo = [NSDictionary dictionaryWithObjectsAndKeys: ipcFilePathCopy, @"ipcFilePath", [[MPNotifications sharedListener] performingTclCommand], @"currentMethod", nil]; [NSThread detachNewThreadSelector:@selector(startIPCServerThread:) toTarget:notificationObject withObject:serverInfo]; //[notificationObject startIPCServerThread]; //} //Retrieving the path for interpInit.tcl for our helper tool NSString * interpInitPath = [[NSBundle bundleForClass:[MPInterpreter class]] pathForResource:@"interpInit" ofType:@"tcl"]; request = [NSDictionary dictionaryWithObjectsAndKeys: @kMPHelperEvaluateTclCommand, @kBASCommandKey, statement, @kTclStringToBeEvaluated, tclInterpreterPkgPath, @kTclInterpreterInitPath , interpInitPath, @kInterpInitFilePath, ipcFilePath, @kServerFileSocketPath , nil]; assert(request != NULL); bundleID = [[NSBundle bundleForClass:[self class]] bundleIdentifier]; assert(bundleID != NULL); //In order to make the framework work normally by default ... we do a bare initialization //of internalMacPortsAuthRef if the delegate hasn't initialzed it already if (internalMacPortsAuthRef == NULL) { OSStatus res = AuthorizationCreate (NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &internalMacPortsAuthRef); assert(res == noErr); } BASSetDefaultRules(internalMacPortsAuthRef, kMPHelperCommandSet, (CFStringRef) bundleID, NULL); NSLog(@"BEFORE Tool Execution request is %@ , response is %@ \n\n", request, response); err = BASExecuteRequestInHelperTool(internalMacPortsAuthRef, kMPHelperCommandSet, (CFStringRef) bundleID, (CFDictionaryRef) request, &response); if (err == noErr){// retrieve result here if available if( response != NULL) result = (NSString *) (CFStringRef) CFDictionaryGetValue(response, CFSTR(kTclStringEvaluationResult)); } else { //Try to recover error NSLog(@"*****************************************"); failCode = BASDiagnoseFailure(internalMacPortsAuthRef, (CFStringRef) bundleID); //Need to pass in URL's to helper and install tools since I //modified BASFixFaliure NSBundle * mpBundle = [NSBundle bundleForClass:[self class]]; NSString * installToolPath = [mpBundle pathForResource:@"MPHelperInstallTool" ofType:nil]; assert(installToolPath != nil); NSURL * installToolURL = [NSURL fileURLWithPath:installToolPath]; assert(installToolURL != nil); NSString * helperToolPath = [mpBundle pathForResource:@"MPHelperTool" ofType:nil]; assert(helperToolPath != nil); NSURL * helperToolURL = [NSURL fileURLWithPath:helperToolPath]; assert(helperToolURL != nil); err = BASFixFailure(internalMacPortsAuthRef, (CFStringRef) bundleID, (CFURLRef) installToolURL, (CFURLRef) helperToolURL, failCode); //Making the following assumption in error handling. If we return //a noErr then response dictionary cannot be nil since everything went ok. //Hence I'm only checking for errors WITHIN the following blocks ... // // NOTE: We don't automatically retry as the user wants to have a consistent cancel // action (i.e. terminate the process when he wants to) if (err != noErr) {//This means FixFaliure failed ... Report that in returned error if (mportError != NULL) { //I'm not sure of exactly how to report this error ... //Do we need some error codes for our domain? I'll define one NSError * undError = [[[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:err userInfo:[NSDictionary dictionaryWithObjectsAndKeys: NSLocalizedString(@"Check error code for OSStatus returned",@""), NSLocalizedDescriptionKey, nil]] autorelease]; *mportError = [[[NSError alloc] initWithDomain:MPFrameworkErrorDomain code:MPHELPINSTFAILED userInfo:[NSDictionary dictionaryWithObjectsAndKeys: NSLocalizedString(@"Unable to fix faliure for MPHelperTool execution", @""), NSLocalizedDescriptionKey, undError, NSUnderlyingErrorKey, NSLocalizedString(@"BASFixFaliure routine wasn't completed successfuly", @""), NSLocalizedFailureReasonErrorKey, nil]] autorelease]; } } } //NSLog(@"AFTER Tool Execution request is %@ , response is %@ \n\n", request, response); return result; } - (NSString *) evaluateStringWithMPPortProcess:(NSString *) statement error:(NSError **)mportError { aTask = [[NSTask alloc] init]; NSMutableArray *args = [NSMutableArray array]; /* set arguments */ [args addObject:PKGPath]; [aTask setCurrentDirectoryPath:[[NSBundle bundleForClass:[self class]] resourcePath]]; [aTask setLaunchPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"MPPortProcess" ofType:@""]]; [aTask setArguments:args]; [aTask launch]; NSConnection *notificationsConnection = [NSConnection defaultConnection]; // Vending MPNotifications sharedListener [notificationsConnection setRootObject:[MPNotifications sharedListener]]; // Register the named connection if ( [notificationsConnection registerName:@"MPNotifications"] ) { NSLog( @"Successfully registered connection with port %@", [[notificationsConnection receivePort] description] ); } else { NSLog( @"Name used by %@", [[[NSPortNameServer systemDefaultPortNameServer] portForName:@"MPNotifications"] description] ); } id theProxy; do { theProxy = [NSConnection rootProxyForConnectionWithRegisteredName:@"MPPortProcess" host:nil]; } while (theProxy == nil); [theProxy evaluateString:statement]; [aTask waitUntilExit]; int status = [aTask terminationStatus]; if (status != TCL_OK) { NSLog(@"Task failed. Code: %i", status); } [[notificationsConnection receivePort] invalidate]; return nil; } + (NSTask*) task { return aTask; } + (void) terminateMPHelperTool { NSString * bundleID; //In order to make the framework work normally by default ... we do a bare initialization //of internalMacPortsAuthRef if the delegate hasn't iniitialzed it already if (internalMacPortsAuthRef == NULL) { OSStatus res = AuthorizationCreate (NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &internalMacPortsAuthRef); assert(res == noErr); } NSBundle * mpBundle = [NSBundle bundleForClass:[self class]]; NSString * installToolPath = [mpBundle pathForResource:@"MPHelperInstallTool" ofType:nil]; bundleID = [mpBundle bundleIdentifier]; BASSetDefaultRules(internalMacPortsAuthRef, kMPHelperCommandSet, (CFStringRef) bundleID, NULL); BASTerminateCommand(internalMacPortsAuthRef, [bundleID UTF8String], [installToolPath UTF8String]); return; } #pragma mark - #pragma mark Authorization Code - (BOOL)checkIfAuthorized { if (internalMacPortsAuthRef == NULL ) { return NO; } return YES; } -(void)setAuthorizationRef:(AuthorizationRef)authRef { //I can do this since Framework client responsible //for managing memory for Authorization internalMacPortsAuthRef = authRef; } #pragma mark - #pragma mark For Internal Use - (int) execute:(NSString *)pathToExecutable withArgs:(NSArray *)args { NSTask * task = [[NSTask alloc] init]; [task setLaunchPath:pathToExecutable]; [task setArguments:args]; [task launch]; [task waitUntilExit]; int status = [task terminationStatus]; [task release]; return status; } - (Tcl_Interp *) sharedInternalTclInterpreter { return _interpreter; } @end