/* * $Id$ * MacPorts.Framework * * Authors: * George Armah * * Copyright (c) 2008 George Armah * 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. */ #define MP_DEFAULT_PKG_PATH @"/Library/Tcl" #import #include #include "BetterAuthorizationSampleLib.h" #include #include "MPHelperCommon.h" #include "MPHelperNotificationsProtocol.h" #include "MPHelperNotificationsCommon.h" #include #include #include #include #include #include #include //According to the docs all I need is //the file descriptor that MPNotifications //obtained when creating the server socket //I'll save that here when retrieving info. //fromt he request dictionary int notificationsFileDescriptor; static int hasInstalledSignalToSocket = 0; BOOL hasSetFileDescriptor = NO; NSString * ipcFilePath; #pragma mark - #pragma mark ASL Logging @interface ASLLogger : NSObject { } + (BOOL) logString:(NSString *)log; @end @implementation ASLLogger +(BOOL) logString:(NSString *)log { //Initialize asl loggin asl logging stuff aslmsg logMsg = asl_new(ASL_TYPE_MSG) ; assert(logMsg != NULL); asl_set(logMsg, ASL_KEY_FACILITY, "com.apple.console"); asl_set(logMsg, ASL_KEY_SENDER, "MPHelperTool"); aslclient logClient = asl_open(NULL , NULL, ASL_OPT_STDERR); assert(logClient != NULL); int res = asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: %@" , log); asl_close(logClient); if (res == 0) return YES; return NO; } @end #pragma mark - ///////////////////////////////////////////////////////////////// #pragma mark ***** Notifications Connection Abstraction // A ConnectionRef represents a connection from the client to the server. // The internals of this are opaque to external callers. All operations on // a connection are done via the routines in this section. enum { kConnectionStateMagic = 'LCCM' // Local Client Connection Magic }; typedef struct ConnectionState * ConnectionRef; // Pseudo-opaque reference to the connection. typedef Boolean (*ConnectionCallbackProcPtr)( ConnectionRef conn, const PacketHeader * packet, void * refCon ); // When the client enables listening on a connection, it supplies a // function of this type as a callback. We call this function in // the context of the runloop specified by the client when they enable // listening. // // conn is a reference to the connection. It will not be NULL. // // packet is a pointer to the packet that arrived, or NULL if we've // detected that the connection to the server is broken. // // refCon is a value that the client specified when it registered this // callback. // // If the server sends you a bad packet, you can return false to // tell the connection management system to shut down the connection. // ConnectionState is the structure used to track a single connection to // the server. All fields after fSockFD are only relevant if the client // has enabled listening. struct ConnectionState { OSType fMagic; // kConnectionStateMagic int fSockFD; // UNIX domain socket to server CFSocketRef fSockCF; // CFSocket wrapper for the above CFRunLoopSourceRef fRunLoopSource; // runloop source for the above CFMutableDataRef fBufferedPackets; // buffer for incomplete packet data ConnectionCallbackProcPtr fCallback; // client's packet callback void * fCallbackRefCon; // refCon for the above. }; // Forward declarations. See the comments associated with the function definition. static void ConnectionShutdown(ConnectionRef conn); static void ConnectionCloseInternal(ConnectionRef conn, Boolean sayGoodbye); enum { kResultColumnWidth = 10 }; static int ConnectionSend(ConnectionRef conn, const PacketHeader *packet) // Send a packet to the server. Use this when you're not expecting a // reply. // // conn must be a valid connection // packet must be a valid, ready-to-send, packet // Returns an errno-style error code { int err; assert(conn != NULL); assert(conn->fSockFD != -1); // connection must not be shut down // conn->fSockCF may or may not be NULL; it's OK to send a packet when listening // because there's no reply; OTOH, you can't do an RPC while listening because // an unsolicited packet might get mixed up with the RPC reply. assert(packet != NULL); assert(packet->fMagic == kPacketMagic); assert(packet->fSize >= sizeof(PacketHeader)); // Simply send the packet down the socket. err = MoreUNIXWrite(conn->fSockFD, packet, packet->fSize, NULL); return err; } static int ConnectionOpen(ConnectionRef *connPtr) // Opens a connection to the server. // // On entry, connPtr must not be NULL // On entry, *connPtr must be NULL // Returns an errno-style error code // On success, *connPtr will not be NULL // On error, *connPtr will be NULL { int err; ConnectionRef conn; Boolean sayGoodbye; assert( connPtr != NULL); assert(*connPtr == NULL); sayGoodbye = false; // Allocate a ConnectionState structure and fill out some basic fields. err = 0; conn = (ConnectionRef) calloc(1, sizeof(*conn)); if (conn == NULL) { err = ENOMEM; } if (err == 0) { conn->fMagic = kConnectionStateMagic; // For clean up to work properly, we must make sure that, if // the connection record is allocated successfully, we always // set fSockFD to -1. So, while the following line is redundant // in the current code, it's present to press home this point. conn->fSockFD = -1; } // Create a UNIX domain socket and connect to the server. if (err == 0) { conn->fSockFD = socket(AF_UNIX, SOCK_STREAM, 0); err = MoreUNIXErrno(conn->fSockFD); } if (err == 0) { struct sockaddr_un connReq; connReq.sun_len = sizeof(connReq); connReq.sun_family = AF_UNIX; //strcpy(connReq.sun_path, kServerSocketPath); // char testPath [MAXPATHLEN]; // strcpy(testPath, [ipcFilePath cStringUsingEncoding:NSUTF8StringEncoding]); [ASLLogger logString:[NSString stringWithFormat:@"ipcFilePath is %@", ipcFilePath]]; // const char * testPath = [ipcFilePath cStringUsingEncoding:NSUTF8StringEncoding]; strcpy(connReq.sun_path, [ipcFilePath cStringUsingEncoding:NSUTF8StringEncoding]); err = connect(conn->fSockFD, (struct sockaddr *) &connReq, SUN_LEN(&connReq)); err = MoreUNIXErrno(err); sayGoodbye = (err == 0); } // Clean up. if (err != 0) { ConnectionCloseInternal(conn, sayGoodbye); conn = NULL; } *connPtr = conn; assert( (err == 0) == (*connPtr != NULL) ); return err; } static int ConnectionRPC( ConnectionRef conn, const PacketHeader * request, PacketHeader * reply, size_t replySize ) // Perform an RPC (Remote Procedure Call) with the server. That is, send // the server a packet and wait for a reply. You can only use this on // connections that are not in listening mode. // // conn must be a valid connection // // packet must be a valid, ready-to-send, packet // // reply and replySize specify a buffer where the reply packet is placed; // reply size must not be NULL; replySize must not be less that the // packet header size (sizeof(PacketHeader)); if the reply packet is bigger // than replySize, the data that won't fit is discarded; you can detect this // by looking at reply->fSize // // Returns an errno-style error code // On success, the buffer specified by reply and replySize will contain the // reply packet; on error, the contents of that buffer is invalid; also, // if this routine errors the connection is no longer useful (conn is still // valid, but you can't use it to transmit any more data) { int err; assert(conn != NULL); assert(conn->fSockFD != -1); // connection must not be shut down assert(conn->fSockCF == NULL); // RPC and listening are mutually exclusive // because unsolicited packet might get mixed up // with the reply assert(request != NULL); assert(request->fMagic == kPacketMagic); assert(request->fSize >= sizeof(PacketHeader)); assert(reply != NULL); assert(replySize >= sizeof(PacketHeader)); // Send the request. err = ConnectionSend(conn, request); // Read and validate the reply header. if (err == 0) { err = MoreUNIXRead(conn->fSockFD, reply, sizeof(PacketHeader), NULL); } if ( (err == 0) && (reply->fMagic != kPacketMagic) ) { fprintf(stderr, "ConnectionRPC: Bad magic (%.4s).\n", (char *) &reply->fMagic); err = EINVAL; } if ( (err == 0) && (reply->fType != kPacketTypeReply) ) { fprintf(stderr, "ConnectionRPC: Type wrong (%.4s).\n", (char *) &reply->fType); err = EINVAL; } if ( (err == 0) && (reply->fID != request->fID) ) { fprintf(stderr, "ConnectionRPC: ID mismatch (%" PRId32 ").\n", reply->fID); err = EINVAL; } if ( (err == 0) && ( (reply->fSize < sizeof(PacketHeader)) || (reply->fSize > kPacketMaximumSize) ) ) { fprintf(stderr, "ConnectionRPC: Bogus packet size (%" PRIu32 ").\n", reply->fSize); err = EINVAL; } // Read the packet payload that will fit in the reply buffer. if ( (err == 0) && (reply->fSize > sizeof(PacketHeader)) ) { uint32_t payloadToRead; if (reply->fSize > replySize) { payloadToRead = replySize; } else { payloadToRead = reply->fSize; } payloadToRead -= sizeof(PacketHeader); err = MoreUNIXRead(conn->fSockFD, ((char *) reply) + sizeof(PacketHeader), payloadToRead, NULL); } // Discard any remaining packet payload that will fit in the reply buffer. // The addition check in the next line is necessary to avoid the undefined behaviour // of malloc(0) in the dependent block. if ( (err == 0) && (reply->fSize > replySize) ) { uint32_t payloadToJunk; void * junkBuf; payloadToJunk = reply->fSize - replySize; junkBuf = malloc(payloadToJunk); if (junkBuf == NULL) { err = ENOMEM; } if (err == 0) { err = MoreUNIXRead(conn->fSockFD, junkBuf, payloadToJunk, NULL); } free(junkBuf); } // Any errors cause us to immediately shut down our connection because we // we're no longer sure of the state of the channel (that is, did we leave // half a packet stuck in the pipe). if (err != 0) { ConnectionShutdown(conn); } return err; } static void ConnectionShutdown(ConnectionRef conn) // This routine shuts down down the connection to the server // without saying goodbye; it leaves conn valid. This routine // is primarily used internally to the connection abstraction // where we notice that the connection has failed for some reason. // It's also called by the client after a successful quit RPC // because we know that the server has closed its end of the // connection. // // It's important to nil out the fields as we close them because // this routine is called if any messaging routine fails. If it // doesn't nil out the fields, two bad things might happen: // // o When the connection is eventually closed, ConnectionCloseInternal // will try to send a Goodbye, which fails triggering an assert. // // o If ConnectionShutdown is called twice on a particular connection // (which happens a lot; this is a belts and braces implementation // [that's "belts and suspenders" for the Americans reading this; // ever wonder why Monty Python's lumberjacks sing about "suspenders // and a bra"?; well look up "suspenders" in a non-American dictionary // for a quiet chuckle :-] ) { int junk; Boolean hadSockCF; assert(conn != NULL); conn->fCallback = NULL; conn->fCallbackRefCon = NULL; if (conn->fRunLoopSource != NULL) { CFRunLoopSourceInvalidate(conn->fRunLoopSource); CFRelease(conn->fRunLoopSource); conn->fRunLoopSource = NULL; } // CFSocket will close conn->fSockFD when we invalidate conn->fSockCF, // so we remember whether we did this so that, later on, we know // whether to close the file descriptor ourselves. We need an extra // variable because we NULL out fSockCF as we release it, for the reason // described above. hadSockCF = (conn->fSockCF != NULL); if (conn->fSockCF != NULL) { CFSocketInvalidate(conn->fSockCF); CFRelease(conn->fSockCF); conn->fSockCF = NULL; } if (conn->fBufferedPackets != NULL) { CFRelease(conn->fBufferedPackets); conn->fBufferedPackets = NULL; } if ( (conn->fSockFD != -1) && ! hadSockCF ) { junk = close(conn->fSockFD); assert(junk == 0); } // We always set fSockFD to -1 because either we've closed it or // CFSocket has. conn->fSockFD = -1; } static void ConnectionCloseInternal(ConnectionRef conn, Boolean sayGoodbye) // The core of ConnectionClose. It's called by ConnectionClose // and by ConnectionOpen, if it fails for some reason. This exists // as a separate routine so that we can add the sayGoodbye parameter, // which controls whether we send a goodbye packet to the server. We // need this because we should always try to say goodbye if we're called // from ConnectionClose, but if we're called from ConnectionOpen we // should only try to say goodbye if we successfully connected the // socket. // // Regardless, the bulk of the work of this routine is done by // ConnectionShutdown. This routine exists to a) say goodbye, if // necessary, and b) free the memory associated with the connection. { int junk; if (conn != NULL) { assert(conn->fMagic == kConnectionStateMagic); if ( (conn->fSockFD != -1) && sayGoodbye ) { PacketGoodbye goodbye; InitPacketHeader(&goodbye.fHeader, kPacketTypeGoodbye, sizeof(goodbye), false); snprintf(goodbye.fMessage, sizeof(goodbye.fMessage), "Process %ld signing off", (long) getpid()); junk = ConnectionSend(conn, &goodbye.fHeader); assert(junk == 0); } ConnectionShutdown(conn); free(conn); } } static void ConnectionClose(ConnectionRef conn) // Closes the connection. It's legal to pass conn as NULL, in which // case this does nothing (kinda like free'ing NULL). { ConnectionCloseInternal(conn, true); } ///////////////////////////////////////////////////////////////// #pragma mark ***** Notifications Command Utilities static void SIGINTRunLoopCallback(const siginfo_t *sigInfo, void *refCon) // This routine is called in response to a SIGINT signal. // It is not, however, a signal handler. Rather, we // orchestrate to have it called from the runloop (via // the magic of InstallSignalToSocket). It's purpose // is to stop the runloop when the user types ^C. { #pragma unused(sigInfo) #pragma unused(refCon) // Stop the runloop. Note that we can get a reference to the runloop by // calling CFRunLoopGetCurrent because this is called from the runloop. CFRunLoopStop( CFRunLoopGetCurrent() ); // Print a bonus newline to ensure that the next command prompt isn't // printed on the same line as the echoed ^C. fprintf(stderr, "\n"); } static void PrintResult(const char *command, int errNum, const char *arg) // Prints the result of a command. command is the name of the // command, errNum is the errno-style error number, and arg // (if not NULL) is the command argument. { if (errNum == 0) { if (arg == NULL) { fprintf(stderr, "%*s\n", kResultColumnWidth, command); } else { fprintf(stderr, "%*s \"%s\"\n", kResultColumnWidth, command, arg); } } else { fprintf(stderr, "%*s failed with error %d\n", kResultColumnWidth, command, errNum); } } static void DoShout(ConnectionRef conn, const char *message, int * ret) // Implements the "shout" command by sending a shout packet to the server. // Note that this is /not/ an RPC. // // The server responds to this packet by echoing it to each registered // listener. { int err; NSString * shoutString = [NSString stringWithFormat:@"Do Shout is passing %@", [NSString stringWithCString:message encoding:NSUTF8StringEncoding]]; [ASLLogger logString:shoutString]; PacketShout request; InitPacketHeader(&request.fHeader, kPacketTypeShout, sizeof(request), false); snprintf(request.fMessage, sizeof(request.fMessage), "%s", message); err = ConnectionSend(conn, &request.fHeader); PrintResult("shout", err, message); *ret = err; } static void DoQuit(ConnectionRef conn, int * ret) // Implements the "quit" command by doing a quit RPC with the server. // The server responds to this RPC by quitting. Cleverly, it sends us // the RPC reply right before quitting. { int err; PacketQuit request; PacketReply reply; InitPacketHeader(&request.fHeader, kPacketTypeQuit, sizeof(request), true); err = ConnectionRPC(conn, &request.fHeader, &reply.fHeader, sizeof(reply)); if (err == 0) { err = reply.fErr; *ret = err; } if (err == 0) { // If the quit is successful, we shut down our end of the connection // because we know that the server has shut down its end. ConnectionShutdown(conn); } [ASLLogger logString:@"DoQuit being called"]; PrintResult("quit", err, NULL); } void initIPC (ConnectionRef iConn) { int err = 0; //ConnectionRef conn; iConn = NULL; // SIGPIPE is evil, so tell the system not to send it to us. if (err == 0) { err = MoreUNIXIgnoreSIGPIPE(); //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: err started out as ZERO %i", err); [ASLLogger logString:[NSString stringWithFormat:@"MPHelperTool: err started out as ZERO %i", err]]; } // Organise to have SIGINT delivered to a runloop callback. if (err == 0 && hasInstalledSignalToSocket == 0) { sigset_t justSIGINT; (void) sigemptyset(&justSIGINT); (void) sigaddset(&justSIGINT, SIGINT); err = InstallSignalToSocket( &justSIGINT, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, SIGINTRunLoopCallback, NULL ); hasInstalledSignalToSocket = 1; //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: IgnoreSigPipe Successful"); [ASLLogger logString:@"MPHelperTool: IgnoreSigPipe Successful"]; } // Connect to the server. if (err == 0) { //err = ConnectionOpen(&iConn); //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: Installed Signal to Socket!"); [ASLLogger logString:@"MPHelperTool: Installed Signal to Socket!"]; } if (err == 0) { //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: calling DoShout"); [ASLLogger logString:@"MPHelperTool: calling DoShout"]; int i; DoShout(iConn, "Testing initIPC", &i); } else [ASLLogger logString:@"MPHelperTool: NOT calling DoShout"]; //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: NOT calling DoShout"); } #pragma mark - #pragma mark Notifications Connection Abstraction @interface NotificationsClient : NSObject { ConnectionRef nConn; BOOL connected; } -(BOOL) initializeConnection; -(BOOL) doShout:(NSString *)shout; -(BOOL) closeConnection; -(BOOL) connected; @end @implementation NotificationsClient -(BOOL) initializeConnection { int err = 0; nConn = NULL; // SIGPIPE is evil, so tell the system not to send it to us. if (err == 0) { err = MoreUNIXIgnoreSIGPIPE(); [ASLLogger logString:[NSString stringWithFormat:@"MPHelperTool: err started out as ZERO %i", err]]; } // Organise to have SIGINT delivered to a runloop callback. if (err == 0 && hasInstalledSignalToSocket == 0) { sigset_t justSIGINT; (void) sigemptyset(&justSIGINT); (void) sigaddset(&justSIGINT, SIGINT); err = InstallSignalToSocket( &justSIGINT, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, SIGINTRunLoopCallback, NULL ); hasInstalledSignalToSocket = 1; [ASLLogger logString:@"MPHelperTool: IgnoreSigPipe Successful"]; } // Connect to the server. if (err == 0) { err = ConnectionOpen(&nConn); [ASLLogger logString:[NSString stringWithFormat:@"MPHelperTool: Installed Signal to Socket! %i", err]]; } if (err == 0) connected = YES; else connected = NO; return connected; } -(BOOL) connected { return connected; } -(BOOL) doShout:(NSString *)shout { BOOL ret = YES; int v; if(nConn != NULL) { DoShout(nConn, [shout cStringUsingEncoding:NSUTF8StringEncoding], &v); if (v != 0) { ret = NO; } } else ret = NO; return ret; } -(BOOL) closeConnection { int v; BOOL ret = YES; if(nConn != NULL) { DoQuit(nConn, &v); if ( v != 0) { ret = NO; } } else ret = NO; return ret; } @end # pragma mark - #pragma mark Tcl Commands NotificationsClient * notifier = nil; //For now we just log to Console ... soon we will be doing fully fledged IPC int SimpleLog_Command ( ClientData clientData, Tcl_Interp *interpreter, int objc, Tcl_Obj *CONST objv[] ) { int returnCode = TCL_ERROR; NSMutableString * data; ++objv, --objc; if (objc) { int tclCount; int tclResult; const char **tclElements; tclResult = Tcl_SplitList(interpreter, Tcl_GetString(*objv), &tclCount, &tclElements); if (tclResult == TCL_OK) { if (tclCount > 0) { data = [NSMutableString stringWithUTF8String:tclElements[0]]; [data appendString:MPSEPARATOR]; if(tclCount > 1 && tclElements[1]) { [data appendString:[NSString stringWithUTF8String:tclElements[1]]]; [data appendString:MPSEPARATOR]; } else { [data appendString:@"None"]; [data appendString:MPSEPARATOR]; } if(tclCount > 2 && tclElements[2]) { [data appendString:[NSString stringWithUTF8String:tclElements[2]]]; [data appendString:MPSEPARATOR]; } else { [data appendString:@"None"]; [data appendString:MPSEPARATOR]; } } else { data = [NSMutableString stringWithFormat:@"None%@None%@None%@", MPSEPARATOR, MPSEPARATOR, MPSEPARATOR ]; } } } //Now get the actual message ++objv; --objc; if (objc) { [data appendString:[NSString stringWithUTF8String:Tcl_GetString(*objv)]]; } else { [data appendString:@"None"]; } if (data != nil) { //[ASLLogger logString:data]; if (notifier != nil && [notifier connected]) { if([notifier doShout:data]) { returnCode = TCL_OK; [ASLLogger logString:@"DoShout successful"]; } else [ASLLogger logString:@"DoShout unsuccessful"]; } else [ASLLogger logString:[NSString stringWithFormat:@"notifier didn't connect has value %@", notifier]]; } return returnCode; } #pragma mark - ///////////////////////////////////////////////////////////////// #pragma mark ***** Tool Infrastructure static OSStatus DoEvaluateTclString ( AuthorizationRef auth, const void * userData, CFDictionaryRef request, CFMutableDictionaryRef response, aslclient asl, aslmsg aslMsg ) { OSStatus retval = noErr; //Pre conditions assert(auth != NULL); //userData may be NULL assert(request != NULL); assert(response != NULL); //asl may be null //aslMsg may be null //Get the ipc file path ipcFilePath = [[NSString alloc] initWithString:(NSString *) (CFStringRef)CFDictionaryGetValue(request, CFSTR(kServerFileSocketPath))]; if (ipcFilePath == nil) { retval = coreFoundationUnknownErr; } else CFDictionaryAddValue(response, CFSTR("SocketServerFilePath"), (CFStringRef)ipcFilePath); [notifier initializeConnection]; //Get the string that was passed in the request dictionary NSString * tclCmd = (NSString *) (CFStringRef)CFDictionaryGetValue(request, CFSTR(kTclStringToBeEvaluated)); if (tclCmd == nil) { retval = coreFoundationUnknownErr; } else CFDictionaryAddValue(response, CFSTR("TclCommandInput"), (CFStringRef)tclCmd); //Create Tcl Interpreter Tcl_Interp * interpreter = Tcl_CreateInterp(); if(interpreter == NULL) { NSLog(@"Error in Tcl_CreateInterp, aborting."); //For Debugging CFDictionaryAddValue(response, CFSTR("TclInterpreterCreate"), CFSTR("NO")); retval = coreFoundationUnknownErr; } else {//For Debugging CFDictionaryAddValue(response, CFSTR("TclInterpreterCreate"), CFSTR("YES")); } //Initialize Tcl Interpreter if(Tcl_Init(interpreter) == TCL_ERROR) { NSLog(@"Error in Tcl_Init: %s", Tcl_GetStringResult(interpreter)); Tcl_DeleteInterp(interpreter); retval = coreFoundationUnknownErr; //For Dbg CFDictionaryAddValue(response, CFSTR("TclInterpreterInit"), CFSTR("NO")); } else {//For Dbg. CFDictionaryAddValue(response, CFSTR("TclInterpreterInit"), CFSTR("YES")); } //Get the tcl Interpreter pkg path NSString * tclPkgPath = (NSString *) (CFStringRef) CFDictionaryGetValue(request, CFSTR(kTclInterpreterInitPath)); if (tclPkgPath == nil) { retval = coreFoundationUnknownErr; } else CFDictionaryAddValue(response, CFSTR("TclPkgPath"), (CFStringRef)tclPkgPath); //Load macports1.0 package NSString * mport_fastload = [[@"source [file join \"" stringByAppendingString:tclPkgPath] stringByAppendingString:@"\" macports1.0 macports_fastload.tcl]"]; if(Tcl_Eval(interpreter, [mport_fastload UTF8String]) == TCL_ERROR) { NSLog(@"Error in Tcl_EvalFile macports_fastload.tcl: %s", Tcl_GetStringResult(interpreter)); Tcl_DeleteInterp(interpreter); retval = coreFoundationUnknownErr; //For Dbg CFDictionaryAddValue(response, CFSTR("MPFastload"), CFSTR("NO")); } else { CFDictionaryAddValue(response, CFSTR("MPFastload"), CFSTR("YES")); } //Add simplelog tcl command Tcl_CreateObjCommand(interpreter, "simplelog", SimpleLog_Command, NULL, NULL); if (Tcl_PkgProvide(interpreter, "simplelog", "1.0") != TCL_OK) { NSLog(@"Error in Tcl_PkgProvide: %s", Tcl_GetStringResult(interpreter)); retval = coreFoundationUnknownErr; //For Dbg CFDictionaryAddValue(response, CFSTR("simplelog"), CFSTR("NO")); } else { CFDictionaryAddValue(response, CFSTR("simplelog"), CFSTR("YES")); } //Get path for and load interpInit.tcl file to Tcl Interpreter NSString * interpInitFilePath = (NSString *) (CFStringRef) CFDictionaryGetValue(request, CFSTR(kInterpInitFilePath)); if(interpInitFilePath == nil) { CFDictionaryAddValue(response, CFSTR("interpInitFilePath"), CFSTR("NO")); retval = coreFoundationUnknownErr; } else { CFDictionaryAddValue(response, CFSTR("interpInitFilePath"), (CFStringRef)interpInitFilePath); } NSLog(@"FOO TEST 2"); if(Tcl_EvalFile(interpreter, [interpInitFilePath UTF8String]) == TCL_ERROR) { NSLog(@"Error in Tcl_EvalFile init.tcl: %s", Tcl_GetStringResult(interpreter)); Tcl_DeleteInterp(interpreter); retval = coreFoundationUnknownErr; CFDictionaryAddValue(response, CFSTR("interpInit.tcl Evaluation"), CFSTR("NO")); } else { CFDictionaryAddValue(response, CFSTR("interpInit.tcl Evaluation"), CFSTR("YES")); } ///Evaluate String and set return string value NSString * result; int retCode = Tcl_Eval(interpreter, [tclCmd UTF8String]); NSNumber * retNum = [NSNumber numberWithInt:retCode]; if( retCode == TCL_ERROR ) { //Do some error handling retval = coreFoundationUnknownErr; result = [@"TCL COMMAND EXECUTION FAILED BOO!:" stringByAppendingString:[NSString stringWithUTF8String:Tcl_GetStringResult(interpreter)]]; CFDictionaryAddValue(response, CFSTR(kTclStringEvaluationResult), (CFStringRef)result); //Set the Tcl return code CFDictionaryAddValue(response, CFSTR(kTclReturnCode), (CFNumberRef)retNum); } else { retval = noErr; result = [@"TCL COMMAND EXECUTION SUCCEEDED YAAY!:" stringByAppendingString:[NSString stringWithUTF8String:Tcl_GetStringResult(interpreter)]]; CFDictionaryAddValue(response, CFSTR(kTclStringEvaluationResult), (CFStringRef)result); //Set the Tcl return code CFDictionaryAddValue(response, CFSTR(kTclReturnCode), (CFNumberRef)retNum); } assert(response != NULL); return retval; } /* IMPORTANT --------- This array must be exactly parallel to the kMPHelperCommandSet array in "MPHelperCommon.c". */ static const BASCommandProc kMPHelperCommandProcs[] = { DoEvaluateTclString, NULL }; int main(int argc, char const * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; //assert(logClient != NULL); // // int err = 0; // ConnectionRef conn; // conn = NULL; // // // SIGPIPE is evil, so tell the system not to send it to us. // if (err == 0) { // err = MoreUNIXIgnoreSIGPIPE(); // //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: err started out as ZERO %i", err); // [ASLLogger logString:[NSString stringWithFormat:@"MPHelperTool: err started out as ZERO %i", err]]; // // } // // // Organise to have SIGINT delivered to a runloop callback. // if (err == 0) { // sigset_t justSIGINT; // // (void) sigemptyset(&justSIGINT); // (void) sigaddset(&justSIGINT, SIGINT); // // err = InstallSignalToSocket( // &justSIGINT, // CFRunLoopGetCurrent(), // kCFRunLoopDefaultMode, // SIGINTRunLoopCallback, // NULL // ); // //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: IgnoreSigPipe Successful"); // [ASLLogger logString:@"MPHelperTool: IgnoreSigPipe Successful"]; // } // // // Connect to the server. // if (err == 0) { // err = ConnectionOpen(&conn); // //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: Installed Signal to Socket!"); // [ASLLogger logString:@"MPHelperTool: Installed Signal to Socket!"]; // } // // // // if (err == 0) { // //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: calling DoShout"); // [ASLLogger logString:@"MPHelperTool: calling DoShout"]; // // DoShout(conn, "Testing MPHelperTool IPC"); // } // else // [ASLLogger logString:@"MPHelperTool: NOT calling DoShout"]; // //asl_NSLog(logClient , logMsg, ASL_LEVEL_DEBUG, @"MPHelperTool: NOT calling DoShout"); //initIPC(globalConn); //if(!globalConnInitialized){ // initIPC(globalConn); // globalConnInitialized = YES; // } // notifier = [[NotificationsClient alloc] init]; int result = BASHelperToolMain(kMPHelperCommandSet, kMPHelperCommandProcs); // Clean up. //DoQuit(globalConn); // ConnectionClose(globalConn); //asl_close(logClient); [notifier closeConnection]; notifier = nil; ipcFilePath = nil; [pool release]; return result; }