[38876] branches/gsoc08-framework/MacPorts_Framework

armahg at macports.org armahg at macports.org
Fri Aug 1 07:07:23 PDT 2008


Revision: 38876
          http://trac.macosforge.org/projects/macports/changeset/38876
Author:   armahg at macports.org
Date:     2008-08-01 07:07:22 -0700 (Fri, 01 Aug 2008)
Log Message:
-----------
Added BetterAuthorization and MPHelper files. Project set up as described in BetterAuthorizationSample documentation but crashes on running Test Bundle. Time to debug

Modified Paths:
--------------
    branches/gsoc08-framework/MacPorts_Framework/MPHelperTool.m
    branches/gsoc08-framework/MacPorts_Framework/MPInterpreter.h
    branches/gsoc08-framework/MacPorts_Framework/MPInterpreter.m
    branches/gsoc08-framework/MacPorts_Framework/MPInterpreterTest.h
    branches/gsoc08-framework/MacPorts_Framework/MPInterpreterTest.m
    branches/gsoc08-framework/MacPorts_Framework/MPMacPortsTest.h
    branches/gsoc08-framework/MacPorts_Framework/MPMacPortsTest.m
    branches/gsoc08-framework/MacPorts_Framework/MacPorts.Framework.xcodeproj/project.pbxproj

Added Paths:
-----------
    branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.c
    branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.h
    branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLibInstallTool.c
    branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.c
    branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.h

Added: branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.c
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.c	                        (rev 0)
+++ branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.c	2008-08-01 14:07:22 UTC (rev 38876)
@@ -0,0 +1,2420 @@
+/*
+	File:       BetterAuthorizationSampleLib.c
+
+    Contains:   Implementation of reusable code for privileged helper tools.
+
+    Written by: DTS
+
+    Copyright:  Copyright (c) 2007 Apple Inc. All Rights Reserved.
+
+    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple, Inc.
+                ("Apple") in consideration of your agreement to the following terms, and your
+                use, installation, modification or redistribution of this Apple software
+                constitutes acceptance of these terms.  If you do not agree with these terms,
+                please do not use, install, modify or redistribute this Apple software.
+
+                In consideration of your agreement to abide by the following terms, and subject
+                to these terms, Apple grants you a personal, non-exclusive license, under Apple's
+                copyrights in this original Apple software (the "Apple Software"), to use,
+                reproduce, modify and redistribute the Apple Software, with or without
+                modifications, in source and/or binary forms; provided that if you redistribute
+                the Apple Software in its entirety and without modifications, you must retain
+                this notice and the following text and disclaimers in all such redistributions of
+                the Apple Software.  Neither the name, trademarks, service marks or logos of
+                Apple, Inc. may be used to endorse or promote products derived from the
+                Apple Software without specific prior written permission from Apple.  Except as
+                expressly stated in this notice, no other rights or licenses, express or implied,
+                are granted by Apple herein, including but not limited to any patent rights that
+                may be infringed by your derivative works or by other works in which the Apple
+                Software may be incorporated.
+
+                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+                COMBINATION WITH YOUR PRODUCTS.
+
+                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+// Define BAS_PRIVATE so that we pick up our private definitions from 
+// "BetterAuthorizationSampleLib.h".
+
+#define BAS_PRIVATE 1
+
+#include "BetterAuthorizationSampleLib.h"
+
+#include <launch.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/event.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+
+// At runtime BAS only requires CoreFoundation.  However, at build time we need 
+// CoreServices for the various OSStatus error codes in "MacErrors.h".  Thus, by default, 
+// we include CoreServices at build time.  However, you can flip this switch to check 
+// that you're not accidentally using any other CoreServices things.
+
+#if 1
+    #include <CoreServices/CoreServices.h>
+#else
+    #warning Do not ship this way!
+    #include <CoreFoundation/CoreFoundation.h>
+    #include "/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Headers/MacErrors.h"
+#endif
+
+//////////////////////////////////////////////////////////////////////////////////
+#pragma mark ***** Constants
+
+enum {
+    kIdleTimeoutInSeconds     = 120,        // if we get no requests in 2 minutes, we quit
+    kWatchdogTimeoutInSeconds = 65          // any given request must be completely in 65 seconds
+};
+
+// IMPORTANT:
+// These values must be greater than 60 seconds.  If a job runs for less than 60 
+// seconds, launchd will consider it to have failed.
+
+// kBASMaxNumberOfKBytes has two uses:
+//
+// 1. When receiving a dictionary, it is used to limit the size of the incoming 
+//    data.  This ensures that a non-privileged client can't exhaust the 
+//    address space of a privileged helper tool.
+//
+// 2. Because it's less than 4 GB, this limit ensures that the dictionary size 
+//    can be sent as an architecture-neutral uint32_t.
+
+#define kBASMaxNumberOfKBytes			(1024 * 1024)
+
+// A hard-wired file system path for the UNIX domain socket; %s is the placeholder 
+// for the bundle ID (in file system representation).
+
+#define kBASSocketPathFormat			"/var/run/%s.socket"
+
+// The key used to get our describe our socket in the launchd property list file.
+
+#define kLaunchDSocketDictKey           "MasterSocket"
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Common Code
+
+extern int BASOSStatusToErrno(OSStatus errNum)
+    // See comment in header.
+{
+	int retval;
+    
+    #define CASE(ident)         \
+        case k ## ident ## Err: \
+            retval = ident;     \
+            break
+    switch (errNum) {
+		case noErr:
+			retval = 0;
+			break;
+        case kENORSRCErr:
+            retval = ESRCH;                 // no ENORSRC on Mac OS X, so use ESRCH
+            break;
+        case memFullErr:
+            retval = ENOMEM;
+            break;
+        CASE(EDEADLK);
+        CASE(EAGAIN);
+		case kEOPNOTSUPPErr:
+			retval = ENOTSUP;
+			break;
+        CASE(EPROTO);
+        CASE(ETIME);
+        CASE(ENOSR);
+        CASE(EBADMSG);
+        case kECANCELErr:
+            retval = ECANCELED;             // note spelling difference
+            break;
+        CASE(ENOSTR);
+        CASE(ENODATA);
+        CASE(EINPROGRESS);
+        CASE(ESRCH);
+        CASE(ENOMSG);
+        default:
+            if ( (errNum <= kEPERMErr) && (errNum >= kENOMSGErr) ) {
+				retval = (-3200 - errNum) + 1;				// OT based error
+            } else if ( (errNum >= errSecErrnoBase) && (errNum <= (errSecErrnoBase + ELAST)) ) {
+                retval = (int) errNum - errSecErrnoBase;	// POSIX based error
+            } else {
+				retval = (int) errNum;						// just return the value unmodified
+			}
+    }
+    #undef CASE
+    return retval;
+}
+
+extern OSStatus BASErrnoToOSStatus(int errNum)
+    // See comment in header.
+{
+	OSStatus retval;
+	
+	if ( errNum == 0 ) {
+		retval = noErr;
+	} else if ( (errNum >= EPERM) && (errNum <= ELAST) ) {
+		retval = (OSStatus) errNum + errSecErrnoBase;
+	} else {
+		retval = (int) errNum;      // just return the value unmodified
+	}
+    
+    return retval;
+}
+
+static Boolean BASIsBinaryPropertyListData(const void * plistBuffer, size_t plistSize)
+	// Make sure that whatever is passed into the buffer that will 
+	// eventually become a plist (and then sequentially a dictionary)
+	// is NOT in binary format.
+{
+    static const char kBASBinaryPlistWatermark[6] = "bplist";
+    
+    assert(plistBuffer != NULL);
+	
+	return (plistSize >= sizeof(kBASBinaryPlistWatermark)) 
+        && (memcmp(plistBuffer, kBASBinaryPlistWatermark, sizeof(kBASBinaryPlistWatermark)) == 0);
+}
+
+static void NormaliseOSStatusErrorCode(OSStatus *errPtr)
+    // Normalise the cancelled error code to reduce the number of checks that our clients 
+    // have to do.  I made this a function in case I ever want to expand this to handle 
+    // more than just this one case.
+{
+    assert(errPtr != NULL);
+    
+    if ( (*errPtr == errAuthorizationCanceled) || (*errPtr == (errSecErrnoBase + ECANCELED)) ) {
+        *errPtr = userCanceledErr;
+    }
+}
+
+static int BASRead(int fd, void *buf, size_t bufSize, size_t *bytesRead)
+	// A wrapper around <x-man-page://2/read> that keeps reading until either 
+	// bufSize bytes are read or until EOF is encountered, in which case you get 
+    // EPIPE.
+	//
+	// If bytesRead is not NULL, *bytesRead will be set to the number 
+	// of bytes successfully read.  On success, this will always be equal to 
+    // bufSize.  On error, it indicates how much was read before the error 
+    // occurred (which could be zero).
+{
+	int 	err;
+	char *	cursor;
+	size_t	bytesLeft;
+	ssize_t bytesThisTime;
+
+    // Pre-conditions
+
+	assert(fd >= 0);
+	assert(buf != NULL);
+    // bufSize may be 0
+	assert(bufSize <= kBASMaxNumberOfKBytes);
+    // bytesRead may be NULL
+	
+	err = 0;
+	bytesLeft = bufSize;
+	cursor = (char *) buf;
+	while ( (err == 0) && (bytesLeft != 0) ) {
+		bytesThisTime = read(fd, cursor, bytesLeft);
+		if (bytesThisTime > 0) {
+			cursor    += bytesThisTime;
+			bytesLeft -= bytesThisTime;
+		} else if (bytesThisTime == 0) {
+			err = EPIPE;
+		} else {
+			assert(bytesThisTime == -1);
+			
+			err = errno;
+			assert(err != 0);
+			if (err == EINTR) {
+				err = 0;		// let's loop again
+			}
+		}
+	}
+	if (bytesRead != NULL) {
+		*bytesRead = bufSize - bytesLeft;
+	}
+	
+	return err;
+}
+
+static int BASWrite(int fd, const void *buf, size_t bufSize, size_t *bytesWritten)
+	// A wrapper around <x-man-page://2/write> that keeps writing until either 
+	// all the data is written or an error occurs, in which case 
+	// you get EPIPE.
+	//
+	// If bytesWritten is not NULL, *bytesWritten will be set to the number 
+	// of bytes successfully written.  On success, this will always be equal to 
+    // bufSize.  On error, it indicates how much was written before the error 
+    // occurred (which could be zero).
+{
+	int 	err;
+	char *	cursor;
+	size_t	bytesLeft;
+	ssize_t bytesThisTime;
+	
+    // Pre-conditions
+
+	assert(fd >= 0);
+	assert(buf != NULL);
+    // bufSize may be 0
+	assert(bufSize <= kBASMaxNumberOfKBytes);
+	// bytesWritten may be NULL
+	
+	// SIGPIPE occurs when you write to pipe or socket 
+	// whose other end has been closed.  The default action 
+	// for SIGPIPE is to terminate the process.  That's 
+	// probably not what you wanted.  So, in the debug build, 
+	// we check that you've set the signal action to SIG_IGN 
+	// (ignore).  Of course, you could be building a program 
+	// that needs SIGPIPE to work in some special way, in 
+	// which case you should define BAS_WRITE_CHECK_SIGPIPE 
+	// to 0 to bypass this check.
+	
+	#if !defined(BAS_WRITE_CHECK_SIGPIPE)
+		#define BAS_WRITE_CHECK_SIGPIPE 1
+	#endif
+	#if !defined(NDEBUG) && BAS_WRITE_CHECK_SIGPIPE
+		{
+			int					junk;
+			struct stat			sb;
+			struct sigaction	currentSignalState;
+			int					val;
+			socklen_t			valLen;
+			
+			junk = fstat(fd, &sb);
+			assert(junk == 0);
+			
+			if ( S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode) ) {
+				junk = sigaction(SIGPIPE, NULL, &currentSignalState);
+				assert(junk == 0);
+				
+				valLen = sizeof(val);
+				junk = getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, &valLen);
+				assert(junk == 0);
+				assert(valLen == sizeof(val));
+
+				// If you hit this assertion, you need to either disable SIGPIPE in 
+				// your process or on the specific socket you're writing to.  The 
+				// standard code for the former is:
+				//
+				// (void) signal(SIGPIPE, SIG_IGN);
+				//
+				// You typically add this code to your main function.
+				//
+				// The standard code for the latter is:
+				//
+				// static const int kOne = 1;
+				// err = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &kOne, sizeof(kOne));
+				//
+				// You typically do this just after creating the socket.
+
+				assert( (currentSignalState.sa_handler == SIG_IGN) || (val == 1) );
+			}
+		}
+	#endif
+
+	err = 0;
+	bytesLeft = bufSize;
+	cursor = (char *) buf;
+	while ( (err == 0) && (bytesLeft != 0) ) {
+		bytesThisTime = write(fd, cursor, bytesLeft);
+		if (bytesThisTime > 0) {
+			cursor    += bytesThisTime;
+			bytesLeft -= bytesThisTime;
+		} else if (bytesThisTime == 0) {
+			assert(false);
+			err = EPIPE;
+		} else {
+			assert(bytesThisTime == -1);
+			
+			err = errno;
+			assert(err != 0);
+			if (err == EINTR) {
+				err = 0;		// let's loop again
+			}
+		}
+	}
+	if (bytesWritten != NULL) {
+		*bytesWritten = bufSize - bytesLeft;
+	}
+	
+	return err;
+}
+
+static int BASReadDictionary(int fdIn, CFDictionaryRef *dictPtr)
+	// Create a CFDictionary by reading the XML data from fdIn. 
+	// It first reads the size of the XML data, then allocates a 
+	// buffer for that data, then reads the data in, and finally 
+	// unflattens the data into a CFDictionary.
+    //
+    // On success, the caller is responsible for releasing *dictPtr.
+	//
+	// See also the companion routine, BASWriteDictionary, below.
+{
+	int                 err = 0;
+	uint32_t			dictSize;
+	void *				dictBuffer;
+	CFDataRef			dictData;
+	CFPropertyListRef 	dict;
+
+    // Pre-conditions
+
+	assert(fdIn >= 0);
+	assert( dictPtr != NULL);
+	assert(*dictPtr == NULL);
+	
+	dictBuffer = NULL;
+	dictData   = NULL;
+	dict       = NULL;
+
+	// Read the data size and allocate a buffer.  Always read the length as a big-endian 
+    // uint32_t, so that the app and the helper tool can be different architectures.
+	
+	err = BASRead(fdIn, &dictSize, sizeof(dictSize), NULL);
+	if (err == 0) {
+        dictSize = OSSwapBigToHostInt32(dictSize);
+		if (dictSize == 0) {
+			// According to the C language spec malloc(0) may return NULL (although the Mac OS X 
+            // malloc doesn't ever do this), so we specifically check for and error out in 
+			// that case.
+			err = EINVAL;
+		} else if (dictSize > kBASMaxNumberOfKBytes) {
+			// Abitrary limit to prevent potentially hostile client overwhelming us with data.
+			err = EINVAL;
+		}
+	}
+	if (err == 0) {
+		dictBuffer = malloc( (size_t) dictSize);
+		if (dictBuffer == NULL) {
+			err = ENOMEM;
+		}
+	}
+	
+	// Read the data and unflatten.
+	
+	if (err == 0) {
+		err = BASRead(fdIn, dictBuffer, dictSize, NULL);
+	}
+	if ( (err == 0) && BASIsBinaryPropertyListData(dictBuffer, dictSize) ) {
+        err = BASOSStatusToErrno( coreFoundationUnknownErr );
+	}
+	if (err == 0) {
+		dictData = CFDataCreateWithBytesNoCopy(NULL, dictBuffer, dictSize, kCFAllocatorNull);
+		if (dictData == NULL) {
+			err = BASOSStatusToErrno( coreFoundationUnknownErr );
+		}
+	}
+	if (err == 0) {
+		dict = CFPropertyListCreateFromXMLData(NULL, dictData, kCFPropertyListImmutable, NULL);
+		if (dict == NULL) {
+			err = BASOSStatusToErrno( coreFoundationUnknownErr );
+		}
+	}
+	if ( (err == 0) && (CFGetTypeID(dict) != CFDictionaryGetTypeID()) ) {
+		err = EINVAL;		// only CFDictionaries need apply
+	}
+	// CFShow(dict);
+	
+	// Clean up.
+	
+	if (err != 0) {
+		if (dict != NULL) {
+			CFRelease(dict);
+		}
+		dict = NULL;
+	}
+	*dictPtr = (CFDictionaryRef) dict;
+	free(dictBuffer);
+	if (dictData != NULL) {
+		CFRelease(dictData);
+	}
+	
+	assert( (err == 0) == (*dictPtr != NULL) );
+	
+	return err;
+}
+
+static int BASWriteDictionary(CFDictionaryRef dict, int fdOut)
+	// Write a dictionary to a file descriptor by flattening 
+	// it into XML.  Send the size of the XML before sending 
+	// the data so that BASReadDictionary knows how much to 
+	// read.
+	//
+	// See also the companion routine, BASReadDictionary, above.
+{
+	int                 err = 0;
+	CFDataRef			dictData;
+	uint32_t			dictSize;
+
+    // Pre-conditions
+
+	assert(dict != NULL);
+	assert(fdOut >= 0);
+	
+	dictData   = NULL;
+	
+    // Get the dictionary as XML data.
+    
+	dictData = CFPropertyListCreateXMLData(NULL, dict);
+	if (dictData == NULL) {
+		err = BASOSStatusToErrno( coreFoundationUnknownErr );
+	}
+    
+    // Send the length, then send the data.  Always send the length as a big-endian 
+    // uint32_t, so that the app and the helper tool can be different architectures.
+    //
+    // The MoreAuthSample version of this code erroneously assumed that CFDataGetBytePtr 
+    // can fail and thus allocated an extra buffer to copy the data into.  In reality, 
+    // CFDataGetBytePtr can't fail, so this version of the code doesn't do the unnecessary 
+    // allocation.
+    
+    if ( (err == 0) && (CFDataGetLength(dictData) > kBASMaxNumberOfKBytes) ) {
+        err = EINVAL;
+    }
+    if (err == 0) {
+		dictSize = OSSwapHostToBigInt32( CFDataGetLength(dictData) );
+        err = BASWrite(fdOut, &dictSize, sizeof(dictSize), NULL);
+    }
+	if (err == 0) {
+		err = BASWrite(fdOut, CFDataGetBytePtr(dictData), CFDataGetLength(dictData), NULL);
+	}
+
+	if (dictData != NULL) {
+		CFRelease(dictData);
+	}
+		
+	return err;
+}
+
+// When we pass a descriptor, we have to pass at least one byte 
+// of data along with it, otherwise the recvmsg call will not 
+// block if the descriptor hasn't been written to the other end 
+// of the socket yet.
+
+static const char kDummyData = 'D';
+
+// Due to a kernel bug in Mac OS X 10.4.x and earlier <rdar://problem/4650646>, 
+// you will run into problems if you write data to a socket while a process is 
+// trying to receive a descriptor from that socket.  A common symptom of this 
+// problem is that, if you write two descriptors back-to-back, the second one 
+// just disappears.
+//
+// To avoid this problem, we explicitly ACK all descriptor transfers.  
+// After writing a descriptor, the sender reads an ACK byte from the socket.  
+// After reading a descriptor, the receiver sends an ACK byte (kACKData) 
+// to unblock the sender.
+
+static const char kACKData   = 'A';
+
+static int BASReadDescriptor(int fd, int *fdRead)
+    // Read a descriptor from fd and place it in *fdRead.
+    //
+    // On success, the caller is responsible for closing *fdRead.
+    //
+    // See the associated BASWriteDescriptor, below.
+{
+	int 				err;
+	int 				junk;
+	struct msghdr 		msg;
+	struct iovec		iov;
+	struct {
+		struct cmsghdr 	hdr;
+		int            	fd;
+	} 					control;
+	char				dummyData;
+	ssize_t				bytesReceived;
+
+    // Pre-conditions
+
+	assert(fd >= 0);
+	assert( fdRead != NULL);
+	assert(*fdRead == -1);
+
+	iov.iov_base = (char *) &dummyData;
+	iov.iov_len  = sizeof(dummyData);
+	
+    msg.msg_name       = NULL;
+    msg.msg_namelen    = 0;
+    msg.msg_iov        = &iov;
+    msg.msg_iovlen     = 1;
+    msg.msg_control    = (caddr_t) &control;
+    msg.msg_controllen = sizeof(control);
+    msg.msg_flags	   = MSG_WAITALL;
+    
+    do {
+	    bytesReceived = recvmsg(fd, &msg, 0);
+	    if (bytesReceived == sizeof(dummyData)) {
+	    	if (   (dummyData != kDummyData)
+	    		|| (msg.msg_flags != 0) 
+	    		|| (msg.msg_control == NULL) 
+	    		|| (msg.msg_controllen != sizeof(control)) 
+	    		|| (control.hdr.cmsg_len != sizeof(control)) 
+	    		|| (control.hdr.cmsg_level != SOL_SOCKET)
+				|| (control.hdr.cmsg_type  != SCM_RIGHTS) 
+				|| (control.fd < 0) ) {
+	    		err = EINVAL;
+	    	} else {
+	    		*fdRead = control.fd;
+		    	err = 0;
+	    	}
+	    } else if (bytesReceived == 0) {
+	    	err = EPIPE;
+	    } else {
+	    	assert(bytesReceived == -1);
+
+	    	err = errno;
+	    	assert(err != 0);
+	    }
+	} while (err == EINTR);
+    
+    // Send the ACK.  If that fails, we have to act like we never got the 
+    // descriptor in our to maintain our post condition.
+    
+    if (err == 0) {
+        err = BASWrite(fd, &kACKData, sizeof(kACKData), NULL);
+        if (err != 0) {
+            junk = close(*fdRead);
+            assert(junk == 0);
+            *fdRead = -1;
+        }
+    }
+
+	assert( (err == 0) == (*fdRead >= 0) );
+	
+	return err;
+}
+
+static int BASWriteDescriptor(int fd, int fdToWrite)
+    // Write the descriptor fdToWrite to fd.
+    //
+    // See the associated BASReadDescriptor, above.
+{
+	int 				err;
+	struct msghdr 		msg;
+	struct iovec		iov;
+	struct {
+		struct cmsghdr 	hdr;
+		int            	fd;
+	} 					control;
+	ssize_t 			bytesSent;
+    char                ack;
+
+    // Pre-conditions
+
+	assert(fd >= 0);
+	assert(fdToWrite >= 0);
+
+    control.hdr.cmsg_len   = sizeof(control);
+    control.hdr.cmsg_level = SOL_SOCKET;
+    control.hdr.cmsg_type  = SCM_RIGHTS;
+    control.fd             = fdToWrite;
+
+	iov.iov_base = (char *) &kDummyData;
+	iov.iov_len  = sizeof(kDummyData);
+	
+    msg.msg_name       = NULL;
+    msg.msg_namelen    = 0;
+    msg.msg_iov        = &iov;
+    msg.msg_iovlen     = 1;
+    msg.msg_control    = (caddr_t) &control;
+    msg.msg_controllen = control.hdr.cmsg_len;
+    msg.msg_flags	   = 0;
+    do {
+	    bytesSent = sendmsg(fd, &msg, 0);
+	    if (bytesSent == sizeof(kDummyData)) {
+	    	err = 0;
+	    } else {
+	    	assert(bytesSent == -1);
+
+	    	err = errno;
+	    	assert(err != 0);
+	    }
+	} while (err == EINTR);
+
+    // After writing the descriptor, try to read an ACK back from the 
+    // recipient.  If that fails, or we get the wrong ACK, we've failed.
+    
+    if (err == 0) {
+        err = BASRead(fd, &ack, sizeof(ack), NULL);
+        if ( (err == 0) && (ack != kACKData) ) {
+            err = EINVAL;
+        }
+    }
+
+    return err;
+}
+
+extern void BASCloseDescriptorArray(
+	CFArrayRef					descArray
+)
+    // See comment in header.
+{	
+	int							junk;
+	CFIndex						descCount;
+	CFIndex						descIndex;
+	
+	// I decided to allow descArray to be NULL because it makes it 
+	// easier to call this routine using the code.
+	//
+	// BASCloseDescriptorArray((CFArrayRef) CFDictionaryGetValue(response, CFSTR(kBASDescriptorArrayKey)));
+	
+	if (descArray != NULL) {
+		if (CFGetTypeID(descArray) == CFArrayGetTypeID()) {
+			descCount = CFArrayGetCount(descArray);
+
+			for (descIndex = 0; descIndex < descCount; descIndex++) {
+				CFNumberRef thisDescNum;
+				int 		thisDesc;
+		
+				thisDescNum = (CFNumberRef) CFArrayGetValueAtIndex(descArray, descIndex);
+				if (   (thisDescNum == NULL) 
+					|| (CFGetTypeID(thisDescNum) != CFNumberGetTypeID()) 
+					|| ! CFNumberGetValue(thisDescNum, kCFNumberIntType, &thisDesc) ) {
+					assert(false);
+				} else {
+					assert(thisDesc >= 0);
+					junk = close(thisDesc);
+					assert(junk == 0);
+				}
+			}
+		} else {
+			assert(false);
+		}
+	}
+}
+
+static int BASReadDictioanaryTranslatingDescriptors(int fd, CFDictionaryRef *dictPtr)
+	// Reads a dictionary and its associated descriptors (if any) from fd, 
+	// putting the dictionary (modified to include the translated descriptor 
+	// numbers) in *dictPtr.
+    //
+    // On success, the caller is responsible for releasing *dictPtr and for 
+    // closing any descriptors it references (BASCloseDescriptorArray makes 
+    // the second part easy).
+{
+	int 				err;
+	int 				junk;
+	CFDictionaryRef		dict;
+	CFArrayRef 			incomingDescs;
+	
+    // Pre-conditions
+
+	assert(fd >= 0);
+	assert( dictPtr != NULL);
+	assert(*dictPtr == NULL);
+	
+	dict = NULL;
+	
+	// Read the dictionary.
+	
+	err = BASReadDictionary(fd, &dict);
+	
+	// Now read the descriptors, if any.
+	
+	if (err == 0) {
+		incomingDescs = (CFArrayRef) CFDictionaryGetValue(dict, CFSTR(kBASDescriptorArrayKey));
+		if (incomingDescs == NULL) {
+			// No descriptors.  Not much to do.  Just use dict as the response, 
+            // NULLing it out so that we don't release it at the end.
+			
+			*dictPtr = dict;
+			dict = NULL;
+		} else {
+			CFMutableArrayRef 		translatedDescs;
+			CFMutableDictionaryRef	mutableDict;
+			CFIndex					descCount;
+			CFIndex					descIndex;
+			
+			// We have descriptors, so there's lots of stuff to do.  Have to 
+			// receive each of the descriptors assemble them into the 
+			// translatedDesc array, then create a mutable dictionary based 
+			// on response (mutableDict) and replace the 
+			// kBASDescriptorArrayKey with translatedDesc.
+			
+			translatedDescs  = NULL;
+			mutableDict      = NULL;
+
+			// Start by checking incomingDescs.
+					
+			if ( CFGetTypeID(incomingDescs) != CFArrayGetTypeID() ) {
+				err = EINVAL;
+			}
+			
+			// Create our output data.
+			
+			if (err == 0) {
+                translatedDescs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                if (translatedDescs == NULL) {
+                    err = coreFoundationUnknownErr;
+                }
+			}
+			if (err == 0) {
+				mutableDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
+				if (mutableDict == NULL) {
+					err = BASOSStatusToErrno( coreFoundationUnknownErr );
+				}
+			}
+
+			// Now read each incoming descriptor, appending the results 
+			// to translatedDescs as we go.  By keeping our working results 
+			// in translatedDescs, we make sure that we can clean up if 
+			// we fail.
+			
+			if (err == 0) {
+				descCount = CFArrayGetCount(incomingDescs);
+				
+				// We don't actually depend on the descriptor values in the 
+				// response (that is, the elements of incomingDescs), because 
+				// they only make sense it the context of the sending process. 
+				// All we really care about is the number of elements, which 
+				// tells us how many times to go through this loop.  However, 
+				// just to be paranoid, in the debug build I check that the 
+				// incoming array is well formed.
+
+				#if !defined(NDEBUG)
+					for (descIndex = 0; descIndex < descCount; descIndex++) {
+						int 		thisDesc;
+						CFNumberRef thisDescNum;
+						
+						thisDescNum = (CFNumberRef) CFArrayGetValueAtIndex(incomingDescs, descIndex);
+						assert(thisDescNum != NULL);
+						assert(CFGetTypeID(thisDescNum) == CFNumberGetTypeID());
+						assert(CFNumberGetValue(thisDescNum, kCFNumberIntType, &thisDesc));
+						assert(thisDesc >= 0);
+					}
+				#endif
+				
+				// Here's the real work.  For descCount times, read a descriptor 
+				// from fd, wrap it in a CFNumber, and append it to translatedDescs. 
+				// Note that we have to be very careful not to leak a descriptor 
+				// if we get an error here.
+				
+				for (descIndex = 0; descIndex < descCount; descIndex++) {
+					int 		thisDesc;
+					CFNumberRef thisDescNum;
+					
+					thisDesc = -1;
+					thisDescNum = NULL;
+					
+					err = BASReadDescriptor(fd, &thisDesc);
+					if (err == 0) {
+						thisDescNum = CFNumberCreate(NULL, kCFNumberIntType, &thisDesc);
+						if (thisDescNum == NULL) {
+							err = BASOSStatusToErrno( coreFoundationUnknownErr );
+						}
+					}
+					if (err == 0) {
+						CFArrayAppendValue(translatedDescs, thisDescNum);
+						// The descriptor is now stashed in translatedDescs, 
+						// so this iteration of the loop is no longer responsible 
+						// for closing it.
+						thisDesc = -1;		
+					}
+					
+                    if (thisDescNum != NULL) {
+                        CFRelease(thisDescNum);
+                    }
+					if (thisDesc != -1) {
+						junk = close(thisDesc);
+						assert(junk == 0);
+					}
+					
+					if (err != 0) {
+						break;
+					}
+				}
+			}
+
+			// Clean up and establish output parameters.
+			
+			if (err == 0) {
+				CFDictionarySetValue(mutableDict, CFSTR(kBASDescriptorArrayKey), translatedDescs);
+				*dictPtr = mutableDict;
+			} else {
+				BASCloseDescriptorArray(translatedDescs);
+                if (mutableDict != NULL) {
+                    CFRelease(mutableDict);
+                }
+			}
+            if (translatedDescs != NULL) {
+                CFRelease(translatedDescs);
+            }
+		}
+	}
+	
+    if (dict != NULL) {
+        CFRelease(dict);
+    }
+	
+	assert( (err == 0) == (*dictPtr != NULL) );
+	
+	return err;
+}
+
+static int BASWriteDictionaryAndDescriptors(CFDictionaryRef dict, int fd)
+	// Writes a dictionary and its associated descriptors to fd.
+{
+	int 			err;
+	CFArrayRef 		descArray;
+	CFIndex			descCount;
+	CFIndex			descIndex;
+	
+    // Pre-conditions
+
+    assert(dict != NULL);
+    assert(fd >= 0);
+    
+	// Write the dictionary.
+	
+	err = BASWriteDictionary(dict, fd);
+	
+	// Process any descriptors.  The descriptors are indicated by 
+	// a special key in the dictionary.  If that key is present, 
+	// it's a CFArray of CFNumbers that present the descriptors to be 
+	// passed.
+	
+	if (err == 0) {
+		descArray = (CFArrayRef) CFDictionaryGetValue(dict, CFSTR(kBASDescriptorArrayKey));
+		
+		// We only do the following if the special key is present.
+		
+		if (descArray != NULL) {
+		
+			// If it's not an array, that's bad.
+			
+			if ( CFGetTypeID(descArray) != CFArrayGetTypeID() ) {
+				err = EINVAL;
+			}
+			
+			// Loop over the array, getting each descriptor and writing it.
+			
+			if (err == 0) {
+				descCount = CFArrayGetCount(descArray);
+				
+				for (descIndex = 0; descIndex < descCount; descIndex++) {
+					CFNumberRef thisDescNum;
+					int 		thisDesc;
+					
+					thisDescNum = (CFNumberRef) CFArrayGetValueAtIndex(descArray, descIndex);
+					if (   (thisDescNum == NULL) 
+						|| (CFGetTypeID(thisDescNum) != CFNumberGetTypeID()) 
+						|| ! CFNumberGetValue(thisDescNum, kCFNumberIntType, &thisDesc) ) {
+						err = EINVAL;
+					}
+					if (err == 0) {
+						err = BASWriteDescriptor(fd, thisDesc);
+					}
+
+					if (err != 0) {
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	return err;
+}
+
+static OSStatus FindCommand(
+	CFDictionaryRef             request,
+	const BASCommandSpec		commands[],
+    size_t *                    commandIndexPtr
+)
+    // FindCommand is a simple utility routine for checking that the 
+    // command name within a request is valid (that is, matches one of the command 
+    // names in the BASCommandSpec array).
+    // 
+    // On success, *commandIndexPtr will be the index of the requested command 
+    // in the commands array.  On error, the value in *commandIndexPtr is undefined.
+{
+	OSStatus					retval = noErr;
+    CFStringRef                 commandStr;
+    char *                      command;
+	UInt32						commandSize = 0;
+	size_t						index = 0;
+	
+	// Pre-conditions
+	
+	assert(request != NULL);
+	assert(commands != NULL);
+	assert(commands[0].commandName != NULL);        // there must be at least one command
+	assert(commandIndexPtr != NULL);
+    
+    command = NULL;
+
+    // Get the command as a C string.  To prevent untrusted command string from 
+	// trying to run us out of memory, we limit its length to 1024 UTF-16 values.
+    
+    commandStr = CFDictionaryGetValue(request, CFSTR(kBASCommandKey));
+    if ( (commandStr == NULL) || (CFGetTypeID(commandStr) != CFStringGetTypeID()) ) {
+        retval = paramErr;
+    }
+	commandSize = CFStringGetLength(commandStr);
+	if ( (retval == noErr) && (commandSize > 1024) ) {
+		retval = paramErr;
+	}
+    if (retval == noErr) {
+        size_t      bufSize;
+        
+        bufSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(commandStr), kCFStringEncodingUTF8) + 1;
+        command = malloc(bufSize);
+        
+        if (command == NULL) {
+            retval = memFullErr;
+        } else if ( ! CFStringGetCString(commandStr, command, bufSize, kCFStringEncodingUTF8) ) {
+            retval = coreFoundationUnknownErr;
+        }
+    }
+    
+    // Search the commands array for that command.
+    
+    if (retval == noErr) {
+        do {
+            if ( strcmp(commands[index].commandName, command) == 0 ) {
+                *commandIndexPtr = index;
+                break;
+            }
+            index += 1;
+            if (commands[index].commandName == NULL) {
+                retval = BASErrnoToOSStatus(ENOENT);
+                break;
+            }
+        } while (true);
+    }
+
+    free(command);
+
+	return retval;
+}
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Tool Code
+
+/*
+    Watchdog Timer
+    --------------
+    BetterAuthorizationSampleLib's privileged helper tool server is single threaded.  Thus, 
+    it's possible for a broken or malicious client to stop progress within the helper 
+    tool simply by sending the tool half a request.  The single thread of execution 
+    within the tool will wait forever for the rest of the request and, while it's 
+    waiting, it won't be able to service other requests.  Clearly this is not good.
+    
+    I contemplated a number of solutions to this problem, but eventually settled 
+    on a very simple solution.  When it starts processing a request, the tool 
+    starts a watchdog timer.  If the timer expires, the tool dies.  The single 
+    request that the tool is blocked on will fail (because our end of the per-connection 
+    socket for that request closed when we died) and subsequent requests will 
+    relaunch the tool on demand, courtesy of launchd.
+    
+    I use SIGALRM to implement this functionality.  As stated in our header, the 
+    BetterAuthorizationSampleLib code claims this signal and our clients are required not 
+    to use it.  Also, the default disposition for SIGALRM is to quit the process, 
+    which is exactly what I want.
+*/
+
+static void EnableWatchdog(void)
+    // Start the watchdog timer.  If you don't call DisableWatchdog before the 
+    // timer expires, the process will die with a SIGALRM.
+{
+    (void) alarm(kWatchdogTimeoutInSeconds);
+}
+
+static void DisableWatchdog(void)
+    // Disable the watchdog timer.
+{
+    (void) alarm(0);
+}
+
+#if ! defined(NDEBUG)
+    
+    static bool CommandArraySizeMatchesCommandProcArraySize(
+        const BASCommandSpec		commands[], 
+        const BASCommandProc		commandProcs[]
+    )
+    {
+        size_t  commandCount;
+        size_t  procCount;
+        
+        commandCount = 0;
+        while ( commands[commandCount].commandName != NULL ) {
+            commandCount += 1;
+        }
+        
+        procCount = 0;
+        while ( commandProcs[procCount] != NULL ) {
+            procCount += 1;
+        }
+        
+        return (commandCount == procCount);
+    }
+    
+#endif
+
+/*
+    On-The-'Wire' Protocol
+    ----------------------
+    The on-the-'wire' protocol for a BetterAuthorizationSampleLib connection (from the 
+    perspective of the client) is:
+    
+    connect
+    
+    send AuthorizationExternalForm (32 byte blob)
+    send request dictionary length (4 bytes, uint32_t, big endian)
+    send request dictionary (N bytes, flattened CFPropertyList)
+
+    read response dictionary length (4 bytes, uint32_t, big endian)
+    read response dictionary (N bytes, flattened CFPropertyList)
+    for each descriptor in dictionary
+        read 1 byte ('D') with attached descriptor
+        write 1 byte ('A')
+
+    close
+*/
+
+static int HandleConnection(
+    aslclient                   asl,
+    aslmsg                      aslMsg,
+	const BASCommandSpec		commands[], 
+	const BASCommandProc		commandProcs[],
+    int                         fd
+)
+    // This routine handles a single connection from a client.  This connection, in 
+    // turn, represents a single command (request/response pair).  commands is the 
+    // list of valid commands.  commandProc is a callback to call to actually 
+    // execute a command.  Finally, fd is the file descriptor from which the request 
+    // should be read, and to which the response should be sent.
+{
+    int                         retval;
+    OSStatus                    junk;
+    int                         junkInt;
+    AuthorizationExternalForm	extAuth;
+    AuthorizationRef			auth		= NULL;
+    CFDictionaryRef				request		= NULL;
+    size_t                      commandIndex;
+    CFMutableDictionaryRef		response	= NULL;
+    OSStatus                    commandProcStatus;
+    
+    // Pre-conditions
+
+    // asl may be NULL
+    // aslMsg may be NULL
+	assert(commands != NULL);
+	assert(commands[0].commandName != NULL);        // there must be at least one command
+    assert(commandProcs != NULL);
+    assert( CommandArraySizeMatchesCommandProcArraySize(commands, commandProcs) );
+    assert(fd >= 0);
+    
+    // Read in the external authorization reference.
+    retval = BASRead(fd, &extAuth, sizeof(extAuth), NULL);
+    
+    // Internalize external authorization reference.
+    if (retval == 0) {
+        retval = BASOSStatusToErrno( AuthorizationCreateFromExternalForm(&extAuth, &auth) );
+    }
+    
+    // Read in CFDictionaryRef request (the command and its arguments).
+    if (retval == 0) {
+        retval = BASReadDictionary(fd, &request);
+    }
+    
+    // Create a mutable response dictionary before calling the client.
+    if (retval == 0) {
+        response = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+        if (response == NULL) {
+            retval = BASOSStatusToErrno( coreFoundationUnknownErr );
+        }
+    }
+
+    // Errors that occur within this block are considered command errors, that is, they're 
+    // reported to the client in the kBASErrorKey value of the response dictionary 
+    // (that is, BASExecuteRequestInHelperTool returns noErr and valid response dictionary with 
+    // an error value in the kBASErrorKey entry of the dictionary).  In contrast, other errors 
+    // are considered IPC errors and generally result in a the client getting an error status 
+    // back from BASExecuteRequestInHelperTool.
+    //
+    // Notably a request with an unrecognised command string will return an error code 
+    // in the response, as opposed to an IPC error.  This means that a client can check 
+    // whether a tool supports a particular command without triggering an IPC teardown.
+    
+    if (retval == 0) {        
+        // Get the command name from the request dictionary and check to see whether or 
+        // not the command is valid by comparing with the BASCommandSpec array.  Also, 
+        // if the command is valid, return the associated right (if any).
+
+        commandProcStatus = FindCommand(request, commands, &commandIndex);
+        
+        // Acquire the associated right for the command.  If rightName is NULL, the 
+		// commandProc is required to do its own authorization.
+        
+        if ( (commandProcStatus == noErr) && (commands[commandIndex].rightName != NULL) ) {
+            AuthorizationItem   item   = { commands[commandIndex].rightName, 0, NULL, 0 };
+            AuthorizationRights rights = { 1, &item };
+            
+            commandProcStatus = AuthorizationCopyRights(
+                auth, 
+                &rights, 
+                kAuthorizationEmptyEnvironment, 
+                kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed, 
+                NULL
+            );
+        }
+    
+        // Call callback to execute command based on the request.
+        
+        if (commandProcStatus == noErr) {
+            commandProcStatus = commandProcs[commandIndex](auth, commands[commandIndex].userData, request, response, asl, aslMsg);
+
+            if (commandProcStatus == noErr) {
+                junkInt = asl_log(asl, aslMsg, ASL_LEVEL_DEBUG, "Command callback succeeded");
+                assert(junkInt == 0);
+            } else {
+                junkInt = asl_log(asl, aslMsg, ASL_LEVEL_DEBUG, "Command callback failed: %ld", (long) commandProcStatus);
+                assert(junkInt == 0);
+            }
+        }
+
+        // If the command didn't insert its own error value, we use its function 
+        // result as the error value.
+        
+        if ( ! CFDictionaryContainsKey(response, CFSTR(kBASErrorKey)) ) {
+            CFNumberRef     numRef;
+            
+            numRef = CFNumberCreate(NULL, kCFNumberSInt32Type, &commandProcStatus);
+            if (numRef == NULL) {
+                retval = BASOSStatusToErrno( coreFoundationUnknownErr );
+            } else {
+                CFDictionaryAddValue(response, CFSTR(kBASErrorKey), numRef);
+                CFRelease(numRef);
+            }
+        }
+    }
+                                                                    
+    // Write response back to the client.
+    if (retval == 0) {
+        retval = BASWriteDictionaryAndDescriptors(response, fd);
+    }
+    
+    // Clean up.
+    
+    if (response != NULL) {
+        // If there are any descriptors in response, we've now passed them off to the client, 
+        // so we can (and must) close our references to them.
+        BASCloseDescriptorArray( CFDictionaryGetValue(response, CFSTR(kBASDescriptorArrayKey)) );
+        CFRelease(response);
+    }
+    if (request != NULL) {
+        CFRelease(request);
+    }
+    if (auth != NULL) {
+        junk = AuthorizationFree(auth, kAuthorizationFlagDefaults);
+        assert(junk == noErr);
+    }
+    
+    return retval;
+}
+
+#if !defined(NDEBUG)
+
+    static void WaitForDebugger(aslclient asl, aslmsg aslMsg)
+        // You can force a debug version of the tool to stop and wait on 
+        // launch using the following Terminal command:
+        //
+        // $ sudo launchctl stop com.example.BetterAuthorizationSample
+        // $ sudo launchctl setenv BASWaitForDebugger 1
+    {
+        int         err;
+        const char *value;
+        
+        // asl may be NULL
+        // aslMsg may be NULL
+        
+        value = getenv("BASWaitForDebugger");
+        if ( ((value != NULL) && (atoi(value) != 0)) ) {
+            err = asl_log(asl, aslMsg, ASL_LEVEL_DEBUG, "Waiting for debugger");
+            assert(err == 0);
+            (void) pause();
+        }
+    }
+
+#endif
+
+static int CheckInWithLaunchd(aslclient asl, aslmsg aslMsg, const char **errStrPtr)
+    // Checks in with launchd and gets back our listening socket.  
+    // Returns the socket as the function result (or -1 on error). 
+    // Also, on error, set *errStrPtr to a error string suitable 
+    // for logging with ASL.  If the message contains a %m, which 
+	// causes ASL to log errno, errno will be set appropriately.
+{
+    int             err;
+	launch_data_t   checkinRequest = NULL;
+	launch_data_t   checkinResponse = NULL;
+	launch_data_t   socketsDict;
+	launch_data_t   fdArray;
+    launch_data_t   fdData;
+    int             fd = -1;
+    
+    // Pre-conditions
+
+    // asl may be NULL
+    // aslMsg may be NULL
+    assert( errStrPtr != NULL);
+    assert(*errStrPtr == NULL);
+    
+	// Check in with launchd.  Create a checkin request, then run it, then 
+    // check if we got an error.
+    
+    checkinRequest = launch_data_new_string(LAUNCH_KEY_CHECKIN);
+	if (checkinRequest == NULL) {
+        *errStrPtr = "Could not create checkin request: %m";
+		goto done;
+	}
+    checkinResponse = launch_msg(checkinRequest);
+	if (checkinResponse == NULL) {
+        *errStrPtr = "Error checking in: %m";
+		goto done;
+	}
+	if (launch_data_get_type(checkinResponse) == LAUNCH_DATA_ERRNO) {
+		errno = launch_data_get_errno(checkinResponse);            // set errno so %m picks it up
+		*errStrPtr = "Checkin failed: %m";
+		goto done;
+	}
+	
+	// Retrieve the dictionary of sockets entries from the job.  This corresponds to the 
+    // value of the "Sockets" key in our plist file.
+
+	socketsDict = launch_data_dict_lookup(checkinResponse, LAUNCH_JOBKEY_SOCKETS);
+	if (socketsDict == NULL) {
+        *errStrPtr = "Could not get socket dictionary from checkin response: %m";
+		goto done;
+	}
+	if (launch_data_get_type(socketsDict) != LAUNCH_DATA_DICTIONARY) {
+        *errStrPtr = "Could not get socket dictionary from checkin response: Type mismatch";
+		goto done;
+	}
+	if (launch_data_dict_get_count(socketsDict) > 1) {
+		err = asl_log(asl, aslMsg, ASL_LEVEL_WARNING, "Some sockets in dictionary will be ignored");
+        assert(err == 0);
+	}
+	
+	// Get the dictionary value from the key "MasterSocket", as defined in the launchd 
+	// property list file.
+
+	fdArray = launch_data_dict_lookup(socketsDict, kLaunchDSocketDictKey);
+	if (fdArray == NULL) {
+        *errStrPtr = "Could not get file descriptor array: %m";
+		goto done;
+	}
+	if (launch_data_get_type(fdArray) != LAUNCH_DATA_ARRAY) {
+        *errStrPtr = "Could not get file descriptor array: Type mismatch";
+		goto done;
+	}
+	if (launch_data_array_get_count(fdArray) > 1) {
+		err = asl_log(asl, aslMsg, ASL_LEVEL_WARNING, "Some sockets in array will be ignored");
+        assert(err == 0);
+	}
+	
+	// Get the socket file descriptor from the array.
+
+    fdData = launch_data_array_get_index(fdArray, 0);
+    if (fdData == NULL) {
+        *errStrPtr = "Could not get file descriptor array entry: %m";
+		goto done;
+    }
+    if (launch_data_get_type(fdData) != LAUNCH_DATA_FD) {
+        *errStrPtr = "Could not get file descriptor array entry: Type mismatch";
+		goto done;
+    }
+    fd = launch_data_get_fd(fdData);
+    assert(fd >= 0);
+
+    // The following was used to debug a problem with launchd <rdar://problem/5410487>.  
+    // I'm going to leave it in, disabled, until that problem is resolved.
+    
+    if (false) {
+        err = asl_log(asl, aslMsg, ASL_LEVEL_INFO, "Listening descriptor is %d", fd);
+        assert(err == 0);
+    }
+    
+done:
+    if (checkinResponse != NULL) {
+        launch_data_free(checkinResponse);
+    }
+    if (checkinRequest != NULL) {
+        launch_data_free(checkinRequest);
+    }
+    
+    return fd;
+}
+
+static int SetNonBlocking(int fd, Boolean nonBlocking)
+    // Sets the non-blocking state of fd.
+{
+    int     err;
+    int     flags;
+
+    // Pre-conditions
+
+    assert(fd >= 0);
+
+    // Get the flags.
+    
+    err = 0;
+    flags = fcntl(fd, F_GETFL);
+    if (flags < 0) {
+        err = errno;
+    }
+    
+    // If the current state of O_NONBLOCK doesn't match the required 
+    // state, toggle that flag and set it back.
+    
+    if ( (err == 0) && (((flags & O_NONBLOCK) != 0) != nonBlocking) ) {
+        flags ^= O_NONBLOCK;
+        err = fcntl(fd, F_SETFL, flags);
+        if (err < 0) {
+            err = errno;
+        }
+    }
+    
+    return err;
+}
+
+extern int BASHelperToolMain(
+	const BASCommandSpec		commands[], 
+	const BASCommandProc		commandProcs[]
+)
+    // See comment in header.
+{
+    const char *                errStr = NULL;
+    int                         err;
+	aslclient					asl = NULL;
+	aslmsg						aslMsg = NULL;
+	sig_t						pipeSet;
+    int                         listener;
+	int							kq;
+	struct kevent				initEvent;
+	
+	// Pre-conditions
+	
+	assert(commands != NULL);
+	assert(commands[0].commandName != NULL);        // there must be at least one command
+	assert(commandProcs != NULL);
+    assert( CommandArraySizeMatchesCommandProcArraySize(commands, commandProcs) );
+	
+	// Create a new ASL client object, and a template message for any messages that 
+    // we log.  We don't care if these fail because ASL will do the right thing 
+    // if you pass it NULL (that is, nothing).
+    
+	asl     = asl_open(NULL, "HelperTools", ASL_OPT_STDERR);
+    assert(asl != NULL);
+    
+	aslMsg = asl_new(ASL_TYPE_MSG);
+    assert(aslMsg != NULL);
+
+    err = asl_log(asl, aslMsg, ASL_LEVEL_INFO, "Starting up");
+    assert(err == 0);
+
+    #if !defined(NDEBUG)
+        WaitForDebugger(asl, aslMsg);
+    #endif
+    	
+	// Set up the signal handlers we are interested in.
+    //
+    // o SIGTERM -- launchd sends us this when it wants us to quit.  We don't 
+	//   actually need to set up a handler because the default behaviour (process 
+    //   termination) is fine.
+    //
+    // o SIGALRM -- No need to set it up because the default behaviour (process 
+    //   termination) is fine.  See the "Watchdog Timer" comment (above) for details.
+    //
+    // o SIGPIPE -- We don't want to quit when write to a dead socket, so we 
+    //   ignore this signal.
+	
+    pipeSet = signal(SIGPIPE, SIG_IGN);
+    if (pipeSet == SIG_ERR) {
+        errStr = "Could not ignore SIGPIPE: %m";
+        goto done;
+    }
+	
+    // Check in with launchd and get our listening socket.
+    
+    listener = CheckInWithLaunchd(asl, aslMsg, &errStr);
+    if (listener < 0) {
+        assert(errStr != NULL);
+        goto done;
+    }
+
+    // Create a kqueue and wrap the listening socket in it.
+
+    kq = kqueue();
+	if (kq < 0) {
+        errStr = "Could not create kqueue: %m";
+		goto done;
+	}
+
+    EV_SET(&initEvent, listener, EVFILT_READ, EV_ADD, 0, 0, NULL);
+    err = kevent(kq, &initEvent, 1, NULL, 0, NULL);
+    if (err < 0) {
+        errStr = "Could not add listening socket to kqueue: %m";
+        goto done;
+    }
+	
+    // Force the listening socket to non-blocking mode.  Without this, our timeout 
+    // handling won't work properly.  Specifically, we could get stuck in an accept 
+    // if a connection request appears and then disappears.  Eventually the watchdog 
+    // would clean up, but that's not a great solution.
+
+    err = SetNonBlocking(listener, true);
+    if (err != 0) {
+        errno = err;            // for %m
+        errStr = "Could not check/set socket flags: %m";
+        goto done;
+    }
+    
+	// Loop servicing connection requests one at a time.
+    
+    while (true) {
+        int                         eventCount;
+        struct kevent               thisEvent;
+		int                         thisConnection;
+        int                         thisConnectionError;
+        struct sockaddr_storage     clientAddr;         // we don't need this info, but accept won't let us ignore it
+        socklen_t                   clientAddrLen = sizeof(clientAddr);
+        static const struct timespec kIdleTimeout = { kIdleTimeoutInSeconds , 0 };
+        
+		// Wait on the kqueue for a connection request.
+
+        eventCount = kevent(kq, NULL, 0, &thisEvent, 1, &kIdleTimeout);
+        if (eventCount == 0) {
+            // We've hit our idle timer.  Just break out of the connection loop.
+            break;
+        } else if (eventCount == -1) {
+            // We got some sort of error from kevent; quit with an error.
+            errStr = "Unexpected error while listening for connections: %m";
+            goto done;
+        }
+
+        // From this point on, we're running on the watchdog timer.  If we get 
+        // stuck anywhere, the watchdog will fire eventually and we'll quit.
+        
+        EnableWatchdog();
+		
+        // The accept should never get stuck because this is a non-blocking 
+        // socket.
+        
+        thisConnection = accept(thisEvent.ident, (struct sockaddr *) &clientAddr, &clientAddrLen);
+        if (thisConnection == -1) {
+            if (errno == EWOULDBLOCK) {
+                // If the incoming connection just disappeared (perhaps the client 
+                // died before we accepted the connection), don't log that as an error 
+                // and don't quit.
+                err = asl_log(asl, aslMsg, ASL_LEVEL_INFO, "Connection disappeared before we could accept it: %m");
+                assert(err == 0);
+            } else {
+                // Other errors mean that we're in a very weird state; we respond by 
+                // failing out with an error.
+                errStr = "Unexpected error while accepting a connection: %m";
+                goto done;
+            }
+        }
+
+        // Because the accept can fail in a non-fatal fashion, thisConnection can be 
+        // -1 here.  In that case, we just skip the next step.
+
+        if (thisConnection != -1) {
+            err = asl_log(asl, aslMsg, ASL_LEVEL_DEBUG, "Request started");
+            assert(err == 0);
+            
+            // thisConnection inherits its non-blocking setting from listener, but 
+            // we want it to be blocking from here on in, so we switch the status.  
+            // We're now relying on the watchdog to kill us if we get stuck.
+            
+            thisConnectionError = BASErrnoToOSStatus( SetNonBlocking(thisConnection, false) );
+
+            // Entering heavy liftiing.  We have a separate routine to actually 
+            // read the request from the connection, call the client, and send 
+            // the reply.
+
+            if (thisConnectionError == noErr) {
+                thisConnectionError = HandleConnection(asl, aslMsg, commands, commandProcs, thisConnection);
+            }
+
+            err = close(thisConnection);
+            assert(err == 0);
+
+            if (thisConnectionError == 0) {
+                err = asl_log(asl, aslMsg, ASL_LEVEL_DEBUG, "Request finished");
+            } else {
+                errno = thisConnectionError;            // so it can be picked up by %m
+                err = asl_log(asl, aslMsg, ASL_LEVEL_ERR, "Request failed: %m");
+            }
+            assert(err == 0);
+        }
+
+        DisableWatchdog();
+	}
+	
+done:
+    // At this point, errStr is either NULL, in which case we're quitting because 
+    // of our idle timer, or non-NULL, in which case we're dying with an error.
+    
+    // We expect the caller to immediately quit once we return.  Thus, we 
+    // don't bother cleaning up any resources we have allocated here, including 
+    // asl, aslMsg, and kq.
+    
+    if (errStr != NULL) {
+        err = asl_log(asl, aslMsg, ASL_LEVEL_ERR, errStr);
+        assert(err == 0);
+    }
+    err = asl_log(asl, aslMsg, ASL_LEVEL_INFO, "Shutting down");
+    assert(err == 0);
+    return (errStr == NULL) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** App Code
+
+extern void BASSetDefaultRules(
+	AuthorizationRef			auth,
+	const BASCommandSpec		commands[],
+	CFStringRef					bundleID,
+	CFStringRef					descriptionStringTableName
+)
+    // See comment in header.
+{	
+	OSStatus					err;
+    CFBundleRef                 bundle;
+	size_t						commandIndex;
+	
+	// Pre-conditions
+	
+	assert(auth != NULL);
+	assert(commands != NULL);
+	assert(commands[0].commandName != NULL);        // there must be at least one command
+	assert(bundleID != NULL);
+    // descriptionStringTableName may be NULL
+    
+    bundle = CFBundleGetBundleWithIdentifier(bundleID);
+    assert(bundle != NULL);
+	
+    // For each command, set up the default authorization right specification, as 
+    // indicated by the command specification.
+    
+    commandIndex = 0;
+    while (commands[commandIndex].commandName != NULL) {
+        // Some no-obvious assertions:
+        
+        // If you have a right name, you must supply a default rule.
+        // If you have no right name, you can't supply a default rule.
+
+        assert( (commands[commandIndex].rightName == NULL) == (commands[commandIndex].rightDefaultRule == NULL) );
+
+        // If you have no right name, you can't supply a right description.
+        // OTOH, if you have a right name, you may supply a NULL right description 
+        // (in which case you get no custom prompt).
+
+        assert( (commands[commandIndex].rightName != NULL) || (commands[commandIndex].rightDescriptionKey == NULL) );
+        
+        // If there's a right name but no current right specification, set up the 
+        // right specification.
+        
+        if (commands[commandIndex].rightName != NULL) {
+            err = AuthorizationRightGet(commands[commandIndex].rightName, (CFDictionaryRef*) NULL);
+            if (err == errAuthorizationDenied) {
+                CFStringRef thisDescription;
+                CFStringRef	thisRule;
+                
+                // The right is not already defined.  Set up a definition based on 
+                // the fields in the command specification.
+                
+                thisRule = CFStringCreateWithCString(
+                    kCFAllocatorDefault, 
+                    commands[commandIndex].rightDefaultRule, 
+                    kCFStringEncodingUTF8
+                );
+                assert(thisRule != NULL);
+                
+                thisDescription = NULL;
+                if (commands[commandIndex].rightDescriptionKey != NULL) {
+                    thisDescription = CFStringCreateWithCString (
+                        kCFAllocatorDefault, 
+                        commands[commandIndex].rightDescriptionKey, 
+                        kCFStringEncodingUTF8
+                    );
+                    assert(thisDescription != NULL);
+                }
+                
+                err = AuthorizationRightSet(
+                    auth,										// authRef
+                    commands[commandIndex].rightName,           // rightName
+                    thisRule,                                   // rightDefinition
+                    thisDescription,							// descriptionKey
+                    bundle,                                     // bundle
+                    descriptionStringTableName					// localeTableName
+                );												// NULL indicates "Localizable.strings"
+                assert(err == noErr);
+                
+                if (thisDescription != NULL) {
+					CFRelease(thisDescription);
+				}
+                if (thisRule != NULL) {
+					CFRelease(thisRule);
+				}
+            } else { 
+                // A right already exists (err == noErr) or any other error occurs, we 
+                // assume that it has been set up in advance by the system administrator or
+                // this is the second time we've run.  Either way, there's nothing more for 
+                // us to do.
+            }
+        }
+        commandIndex += 1;
+	}
+}
+
+extern OSStatus BASExecuteRequestInHelperTool(
+	AuthorizationRef			auth,
+	const BASCommandSpec		commands[],
+	CFStringRef					bundleID,
+	CFDictionaryRef				request,
+	CFDictionaryRef *			response
+)
+    // See comment in header.
+{	
+	OSStatus					retval = noErr;
+    int                         junk;
+    size_t                      commandIndex;
+    char                        bundleIDC[PATH_MAX];
+	int							fd = -1;
+	struct sockaddr_un			addr;
+	AuthorizationExternalForm	extAuth;
+	
+	// Pre-conditions
+	
+	assert(auth != NULL);
+	assert(commands != NULL);
+	assert(commands[0].commandName != NULL);        // there must be at least one command
+	assert(bundleID != NULL);
+	assert(request != NULL);
+	assert( response != NULL);
+	assert(*response == NULL);
+    
+	// For debugging.
+
+	assert(CFDictionaryContainsKey(request, CFSTR(kBASCommandKey)));
+	assert(CFGetTypeID(CFDictionaryGetValue(request, CFSTR(kBASCommandKey))) == CFStringGetTypeID());
+
+    // Look up the command and preauthorize.  This has the nice side effect that 
+    // the authentication dialog comes up, in the typical case, here, rather than 
+    // in the helper tool.  This is good because the helper tool is global /and/ 
+    // single threaded, so if it's waiting for an authentication dialog for user A 
+    // it can't handle requests from user B.
+    
+    retval = FindCommand(request, commands, &commandIndex);
+
+    #if !defined(BAS_PREAUTHORIZE)
+        #define BAS_PREAUTHORIZE 1
+    #endif
+    #if BAS_PREAUTHORIZE
+        if ( (retval == noErr) && (commands[commandIndex].rightName != NULL) ) {
+            AuthorizationItem   item   = { commands[commandIndex].rightName, 0, NULL, 0 };
+            AuthorizationRights rights = { 1, &item };
+            
+            retval = AuthorizationCopyRights(auth, &rights, kAuthorizationEmptyEnvironment, kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize, NULL);
+        }
+    #endif
+
+    // Create the socket and tell it to not generate SIGPIPE.
+    
+	if (retval == noErr) {
+		fd = socket(AF_UNIX, SOCK_STREAM, 0);
+		if (fd == -1) { 
+			retval = BASErrnoToOSStatus(errno);
+		}
+	}
+	if (retval == noErr) {
+		static const int kOne = 1;
+		
+		if ( setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &kOne, sizeof(kOne)) < 0 ) {
+			retval = BASErrnoToOSStatus(errno);
+		}
+	}
+    
+    // Form the socket address, including a path based on the bundle ID.
+	
+	if (retval == noErr) {
+        if ( ! CFStringGetFileSystemRepresentation(bundleID, bundleIDC, sizeof(bundleIDC)) ) {
+            retval = coreFoundationUnknownErr;
+        }
+    }
+    if (retval == noErr) {
+        int         pathLen;
+        
+		memset(&addr, 0, sizeof(addr));
+	
+		addr.sun_family = AF_UNIX;
+        pathLen = snprintf(addr.sun_path, sizeof(addr.sun_path), kBASSocketPathFormat, bundleIDC);
+        if (pathLen >= sizeof(addr.sun_path)) {
+            retval = paramErr;                  // length of bundle pushed us over the UNIX domain socket path length limit
+        } else {
+			addr.sun_len = SUN_LEN(&addr);
+		}
+    }
+    
+    // Attempt to connect.
+    
+    if (retval == noErr) {
+		if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
+			retval = BASErrnoToOSStatus(errno);
+		}
+	}
+	
+    // Send the flattened AuthorizationRef to the tool.
+    
+    if (retval == noErr) {
+        retval = AuthorizationMakeExternalForm(auth, &extAuth);
+    }
+	if (retval == noErr) {	
+		retval = BASErrnoToOSStatus( BASWrite(fd, &extAuth, sizeof(extAuth), NULL) );
+	}
+	
+    // Write the request.
+    
+	if (retval == noErr) {	
+		retval = BASErrnoToOSStatus( BASWriteDictionary(request, fd) );
+	}
+	
+    // Read response, including any descriptors.
+    
+	if (retval == noErr) {
+		retval = BASErrnoToOSStatus( BASReadDictioanaryTranslatingDescriptors(fd, response) );
+    }
+    
+    // Clean up.
+
+    if (fd != -1) {
+        junk = close(fd);
+        assert(junk == 0);
+    }
+    NormaliseOSStatusErrorCode(&retval);
+    
+    assert( (retval == noErr) == (*response != NULL) );
+    
+	return retval;
+}
+
+extern OSStatus BASGetErrorFromResponse(CFDictionaryRef response)
+    // See comment in header.
+{
+	OSStatus	err;
+	CFNumberRef num;
+	
+	assert(response != NULL);
+	
+	num = (CFNumberRef) CFDictionaryGetValue(response, CFSTR(kBASErrorKey));
+    err = noErr;
+    if ( (num == NULL) || (CFGetTypeID(num) != CFNumberGetTypeID()) ) {
+        err = coreFoundationUnknownErr;
+    }
+	if (err == noErr) {
+		if ( ! CFNumberGetValue(num, kCFNumberSInt32Type, &err) ) {
+            err = coreFoundationUnknownErr;
+        }
+	}
+	
+    NormaliseOSStatusErrorCode(&err);
+	return err;
+}
+
+extern BASFailCode BASDiagnoseFailure(
+	AuthorizationRef			auth,
+	CFStringRef					bundleID
+)
+    // See comment in header.
+{	
+    BASFailCode                 retval = kBASFailUnknown;
+    int                         err;
+    int                         pathLen;
+    char                        bundleIDC   [ PATH_MAX ];
+	char						toolPath	[ PATH_MAX ];
+	char						plistPath	[ PATH_MAX ];
+		
+	struct stat					fileStatus;
+	int							toolErr; 
+	int							plistErr;
+	int							fd;
+	struct sockaddr_un			addr;
+	
+	// Pre-conditions
+	
+	assert(auth != NULL);
+	assert(bundleID != NULL);
+	
+    // Construct paths to the tool and plist.
+    
+    if ( CFStringGetFileSystemRepresentation(bundleID, bundleIDC, sizeof(bundleIDC)) ) {
+
+        pathLen = snprintf(toolPath,  sizeof(toolPath),  kBASToolPathFormat,  bundleIDC);
+        assert(pathLen < PATH_MAX);         // snprintf truncated the string; won't crash us, but we want to know
+
+        pathLen = snprintf(plistPath, sizeof(plistPath), kBASPlistPathFormat, bundleIDC);
+        assert(pathLen < PATH_MAX);         // snprintf truncated the string; won't crash us, but we want to know
+        
+        // Check if files exist at those paths.
+        
+        toolErr  = stat(toolPath,  &fileStatus);
+        plistErr = stat(plistPath, &fileStatus);
+        
+        if ( (toolErr == 0) && (plistErr == 0) ) {
+            // If both items are present, try to connect and see what we get.
+            
+            fd = socket(AF_UNIX, SOCK_STREAM, 0);
+            if (fd != -1) { 
+                memset(&addr, 0, sizeof(addr));
+            
+                addr.sun_family = AF_UNIX;
+                (void) snprintf(addr.sun_path, sizeof(addr.sun_path), kBASSocketPathFormat, bundleIDC);
+				addr.sun_len    = SUN_LEN(&addr);
+            
+                // Attempt to connect to the socket.  If we get ECONNREFUSED, it means no one is 
+                // listening.
+                
+                if ( (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) && (errno == ECONNREFUSED) ) {
+                    retval = kBASFailDisabled;
+                }
+                err = close(fd);
+                assert(err == 0);
+            }
+        } else {
+            if ( (toolErr == 0) || (plistErr == 0) ) {
+                retval = kBASFailPartiallyInstalled;
+            } else {
+                retval = kBASFailNotInstalled;
+            }
+        }
+    }
+	
+	return retval;
+}
+
+// kPlistTemplate is a template for our launchd.plist file.
+
+static const char * kPlistTemplate =
+    // The standard plist header.
+    
+    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+    "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+    "<plist version=\"1.0\">\n"
+    "<dict>\n"
+
+    // We install the job disabled, then enable it as the last step.
+
+    "	<key>Disabled</key>\n"
+    "	<true/>\n"
+
+    // Use the bundle identifier as the job label.
+
+    "	<key>Label</key>\n"
+    "	<string>%s</string>\n"
+
+    // Use launch on demaind.
+
+    "	<key>OnDemand</key>\n"
+    "	<true/>\n"
+
+    // There are no program arguments, other that the path to the helper tool itself.
+    //
+    // IMPORTANT
+    // kBASToolPathFormat embeds a %s
+
+    "	<key>ProgramArguments</key>\n"
+    "	<array>\n"
+    "		<string>" kBASToolPathFormat "</string>\n"
+    "	</array>\n"
+
+    // The tool is required to check in with launchd.
+
+    "	<key>ServiceIPC</key>\n"
+    "	<true/>\n"
+
+    // This specifies the UNIX domain socket used to launch the tool, including 
+    // the permissions on the socket (438 is 0666).
+    //
+    // IMPORTANT
+    // kBASSocketPathFormat embeds a %s
+
+    "	<key>Sockets</key>\n"
+    "	<dict>\n"
+    "		<key>" kLaunchDSocketDictKey "</key>\n"
+    "		<dict>\n"
+    "			<key>SockFamily</key>\n"
+    "			<string>Unix</string>\n"
+    "			<key>SockPathMode</key>\n"
+    "			<integer>438</integer>\n"
+    "			<key>SockPathName</key>\n"
+    "			<string>" kBASSocketPathFormat "</string>\n"
+    "			<key>SockType</key>\n"
+    "			<string>Stream</string>\n"
+    "		</dict>\n"
+    "	</dict>\n"
+    "</dict>\n"
+    "</plist>\n"
+    ;
+	
+
+//  Installation
+//  ------------
+//  We install by running our "InstallTool" using AuthorizationExecuteWithPrivileges 
+//  (AEWP) and passing the relevant parameters to it through AEWP.
+//    
+//  There is an obvious issue with the way we are handling installation as the user
+//  is executing some non-privileged code by way of AEWP. The scenario could exist 
+//  that the code is malicious (or they have other malicious code running at the 
+//  same time) and it could swap in any other tool that it would want executed as 
+//  EUID == 0.
+//    
+//  We decided on this design primarily because the only other option was to run a 
+//  shell via AEWP and pipe a script to it. That would have given us the nice 
+//  properties of not having to have a separate installer on disk and the script 
+//  could be embedded within the executable making it a little more difficult for 
+//  casual hacking. 
+//    
+//  However, running a shell as root is /not/ a very good paradigm to follow, thus, 
+//  weighing the cost-benefits from a security perspective impelled us to just use 
+//  a separate installer tool. The assumption being that, no matter what, if a user 
+//  has malicious code running on their system the added security of having an 
+//  embedded script is negligible and not worth pulling in an entire shell 
+//  environment as root.
+//  
+//  The obvious disadvantages stem from the first advantage of the former, namely,
+//  it's a little more coding and accounting effort (-:
+//
+//
+//  What's This About Zombies?
+//  --------------------------
+//  AuthorizationExecuteWithPrivileges creates a process that runs with privileges. 
+//  This process is a child of our process.  Thus, we need to reap the process 
+//  (by calling <x-man-page://2/waitpid>).  If we don't do this, we create a 'zombie' 
+//  process (<x-man-page://1/ps> displays its status as "Z") that persists until 
+//  our process quits (at which point the zombie gets reparented to launchd, and 
+//  launchd automatically reaps it).  Zombies are generally considered poor form. 
+//  Thus, we want to avoid creating them.
+//
+//  Unfortunately, AEWP doesn't return the process ID of the child process 
+//  <rdar://problem/3090277>, which makes it challenging for us to reap it.  We could 
+//  reap all children (by passing -1 to waitpid) but that's not cool for library code 
+//  (we could end up reaping a child process that's completely unrelated to this 
+//  code, perhaps created by some other part of the host application).  Thus, we need 
+//  to find the child process's PID.  And the only way to do that is for the child 
+//  process to tell us.
+//
+//  So, in the child process (the install tool) we echo the process ID and in the 
+//  parent we look for that in the returned text.  *sigh*  It's pretty ugly, but 
+//  that's the best I can come up with.  We delimit the process ID with some 
+//  pretty distinctive text to make it clear that we've got the right thing.
+
+#if !defined(NDEBUG)
+
+    static Boolean gBASLogInteractions = false;
+        // Set gBASLogInteractions to have BASFixFailure log its interactions with 
+        // the installation tool to stderr.
+
+    static Boolean gBASLogInteractionsInitialised = false;
+        // This indicates whether we've initialised gBASLogInteractions from the 
+		// environment variable.
+
+#endif
+
+static OSStatus RunInstallToolAsRoot(
+	AuthorizationRef			auth, 
+    const char *                installToolPath,
+	const char *				command, 
+								...
+)
+    // Run the specified install tool as root.  The arguments to the tool are 
+    // given as a sequence of (char *)s, terminated be a NULL.  The tool is 
+    // expected to output special tokens to indicate success or failure.
+{
+    OSStatus    retval;
+    size_t      argCount;
+    size_t      argIndex;
+    va_list     ap;
+    char **     args;
+    Boolean     success;
+    FILE *      channel;
+    int         junk;
+	pid_t		childPID;
+
+    // Pre-conditions
+    
+    assert(auth != NULL);
+    assert(installToolPath != NULL);
+    assert(command != NULL);
+    
+    channel = NULL;
+    args    = NULL;
+	childPID = -1;
+    
+    // Count the number of arguments.
+    
+    argCount = 0;
+    va_start(ap, command);
+    while ( va_arg(ap, char *) != NULL ) {
+        argCount += 1;
+    }
+    va_end(ap);
+
+    // Allocate an argument array and populate it, checking each argument along the way.
+    
+    retval = noErr;
+    args = calloc(argCount + 3, sizeof(char *));        // +3 for installToolPath, command and trailing NULL
+    if (args == NULL) {
+        retval = memFullErr;
+    }
+    if (retval == noErr) {
+        argIndex = 0;
+
+        args[argIndex] = (char *) installToolPath;  // Annoyingly, AEWP (and exec) takes a (char * const *) 
+        argIndex += 1;                              // argument, implying that it might modify the individual 
+        args[argIndex] = (char *) command;          // strings.  That means you can't pass a (const char *) to 
+        argIndex += 1;                              // the routine.  However, AEWP never modifies its input 
+                                                    // arguments, so we just cast away the const.
+                                                    // *sigh* <rdar://problem/3090294>
+        va_start(ap, command);
+        do {
+            args[argIndex] = va_arg(ap, char *);
+            if (args[argIndex] == NULL) {
+                break;
+            }
+            argIndex += 1;
+        } while (true);
+        va_end(ap);
+    }
+    
+    // Go go gadget AEWP!
+    
+    if (retval == noErr) {
+        #if !defined(NDEBUG)
+			if ( ! gBASLogInteractionsInitialised ) {
+				const char *	value;
+				
+				value = getenv("BASLogInteractions");
+				gBASLogInteractions = ( ((value != NULL) && (atoi(value) != 0)) );
+				
+				gBASLogInteractionsInitialised = true;
+			}
+		
+            if (gBASLogInteractions) {
+                argIndex = 0;
+                while (args[argIndex] != NULL) {
+                    fprintf(stderr, "args[%zd] = %s\n", argIndex, args[argIndex]);
+                    argIndex += 1;
+                }
+            }
+        #endif
+        retval = AuthorizationExecuteWithPrivileges(auth, args[0], kAuthorizationFlagDefaults, &args[1], &channel);
+    }
+    
+    // Process the tool's output.  We read every line of output from the tool until
+	// we receive either an EOF or the success or failure tokens.
+    //
+    // AEWP provides us with no way to get to the tool's stderr or exit status, 
+    // so we rely on the tool to send us this "oK" to indicate successful completion.
+
+    if (retval == noErr) {
+        char	thisLine[1024];
+		long	tmpLong;
+        int     tmpInt;
+
+        // This loops is a little more complex than you might expect.  There are 
+        // a number of reasons for this:
+        //
+        // o AEWP does not return us the child PID, so we have to scan the tool's 
+        //   output look for a line that contains that information (surrounded 
+        //   by special tokens).
+        //
+        // o Because we can't be guaranteed to get the child PID, we can't be 
+        //   guaranteed to get the child's exit status.  Thus, rather than relying 
+        //   on the exit status, we have the child explicitly print special tokens 
+        //   on success and failure.
+        //
+        // o Because we're parsing special tokens anyway, we might as well extract 
+        //   the real error code from the failure token.
+        //
+        // o A change made to launchctl in Mac OS X 10.4.7 <rdar://problem/4389914> 
+        //   causes it to fork a copy of itself.  The forked copy then delays 
+        //   for 30 seconds before doing some stuff, eventually printing a message 
+        //   like "Workaround Bonjour: 0".  This causes us two problems.
+        //
+        //	 1.	The second copy of launchd still has our communications channel 
+        //		(that is, the other end of "channel") as its stdin/stdout. 
+        //		Thus, we don't get an EOF on channel until that copy quits. 
+        //		This causes a 30 second delay in installation.
+        //
+        //	 2.	The second copy of launchd prints its status line (that is, 
+        //		"Workaround Bonjour: 0") well after the tool prints the success 
+        //      token.
+        //
+        //   I solved these problems by parsing each line for the success or failure 
+        //   token and ignoring any output after that.
+        //
+        // To minimise the danger of interpreting one of the tool's commands 
+        // output as one of our tokens, I've given them a wacky case (for example, 
+        // "oK", not "ok" or "OK" or "Ok").
+        
+        do {
+            success = (fgets(thisLine, sizeof(thisLine), channel) != NULL);
+            if ( ! success ) {
+                // We hit the end of the output without seeing a success or failure 
+                // token.  Note good.  errState is an ADSP error code, but it says 
+                // exactly what I want to say and it's not likely to crop up any 
+                // other way.
+                retval = errState;
+                break;
+            }
+            
+            // This echo doesn't work properly if the line coming back from the tool 
+            // is longer than the line buffer.  However, as the echo is only relevant for 
+            // debugging, and the detection of the "oK" isn't affected by this problem, 
+            // I'm going to leave it as it is.
+            
+            #if !defined(NDEBUG)
+                if (gBASLogInteractions) {
+                    fprintf(stderr, ">%s", thisLine);
+                }
+            #endif
+			
+            // Look for the success token and terminate with no error in that case.
+            
+			if (strcmp(thisLine, kBASInstallToolSuccess "\n") == 0) {
+                assert(retval == noErr);
+				break;
+			}
+            
+            // Look for the failure token and extract the error result from that.
+            
+            if ( sscanf(thisLine, kBASInstallToolFailure "\n", &tmpInt) == 1 ) {
+                retval = BASErrnoToOSStatus( tmpInt );
+                if (retval == noErr) {
+                    assert(false);
+                    retval = errState;
+                }
+                break;
+            }
+			
+			// If we haven't already found a child process ID, look for a line 
+            // that contains it (surrounded by special tokens).  For details, see 
+            // the discussion of zombies above.
+			
+			if ( (childPID == -1) && (sscanf(thisLine, kBASAntiZombiePIDToken1 "%ld" kBASAntiZombiePIDToken2 "\n", &tmpLong) == 1) ) {
+				childPID = (pid_t) tmpLong;
+			}
+        } while (true);
+    }
+	
+	// If we successfully managed to determine the PID of our child process, reap 
+	// that child.  Note that we ignore any errors from this step.  If an error 
+	// occurs, we end up creating a zombie, which isn't too big a deal.  We also 
+    // junk the status result from the tool, relying exclusively on the presence 
+    // of the "oK" in the output.
+	
+	#if !defined(NDEBUG)
+		if (gBASLogInteractions) {
+			fprintf(stderr, "childPID=%ld\n", (long) childPID);
+		}
+	#endif
+	if (childPID != -1) {
+		pid_t	waitResult;
+		int		junkStatus;
+		
+		do {
+			waitResult = waitpid(childPID, &junkStatus, 0);
+		} while ( (waitResult < 0) && (errno == EINTR) );
+	}
+    
+    // Clean up.
+    
+    if (channel != NULL) {
+        junk = fclose(channel);
+        assert(junk == 0);
+    }
+    free(args);
+
+    NormaliseOSStatusErrorCode(&retval);
+    return retval;
+}
+
+static OSStatus BASInstall(
+	AuthorizationRef			auth, 
+	const char *				bundleID, 
+    const char *                installToolPath,
+    const char *                helperToolPath
+)
+	// Do an install from scratch.  Get the specified tool from the bundle 
+    // and install it in the "/Library/PrivilegedHelperTools" directory, 
+	// along with a plist in "/Library/LaunchDaemons".
+{
+    OSStatus    retval;
+    int         junk;
+    char *      plistText;
+    int         fd;
+    char        plistPath[PATH_MAX];
+
+    // Pre-conditions
+    
+    assert(auth != NULL);
+    assert(bundleID != NULL);
+    assert(installToolPath != NULL);
+    assert(helperToolPath != NULL);
+
+    // Prepare for failure
+    
+    plistText = NULL;
+    fd = -1;
+    plistPath[0] = 0;
+
+    // Create the property list from the template, substituting the bundle identifier in 
+    // three different places.  I realise that this isn't very robust (if you change 
+    // the template you have to change this code), but it is /very/ easy.
+    
+    retval = asprintf(&plistText, kPlistTemplate, bundleID, bundleID, bundleID);
+    if (retval < 0) {
+        retval = memFullErr;
+    } else {
+        retval = noErr;
+    }
+    
+    // Write the plist to a temporary file.
+
+    if (retval == noErr) {
+        strlcpy(plistPath, "/tmp/BASTemp-XXXXXXXX.plist", sizeof(plistPath));
+        
+        fd = mkstemps(plistPath, strlen( strrchr(plistPath, '.') ) );
+        if (fd < 0) {
+            retval = BASErrnoToOSStatus( errno );
+        }
+    }
+    if (retval == noErr) {
+        retval = BASErrnoToOSStatus( BASWrite(fd, plistText, strlen(plistText), NULL) );
+    }
+    
+    // Run the tool as root using AuthorizationExecuteWithPrivileges.
+    
+    if (retval == noErr) {
+        retval = RunInstallToolAsRoot(auth, installToolPath, kBASInstallToolInstallCommand, bundleID, helperToolPath, plistPath, NULL);
+    }
+
+    // Clean up.
+    
+    free(plistText);
+    if (fd != -1) {
+        junk = close(fd);
+        assert(junk == 0);
+        
+        junk = unlink(plistPath);
+        assert(junk == 0);
+    }
+
+    return retval;
+}
+
+static OSStatus GetToolPath(CFStringRef bundleID, CFStringRef toolName, char *toolPath, size_t toolPathSize)
+    // Given a bundle identifier and the name of a tool embedded within that bundle, 
+    // get a file system path to the tool.
+{
+    OSStatus    err;
+    CFBundleRef bundle;
+    Boolean     success;
+    CFURLRef    toolURL;
+    
+    assert(bundleID != NULL);
+    assert(toolName != NULL);
+    assert(toolPath != NULL);
+    assert(toolPathSize > 0);
+    
+    toolURL = NULL;
+    
+    err = noErr;
+    bundle = CFBundleGetBundleWithIdentifier(bundleID);
+    if (bundle == NULL) {
+        err = coreFoundationUnknownErr;
+    }
+    if (err == noErr) {
+        toolURL = CFBundleCopyAuxiliaryExecutableURL(bundle, toolName);
+        if (toolURL == NULL) {
+            err = coreFoundationUnknownErr;
+        }
+    }
+    if (err == noErr) {
+        success = CFURLGetFileSystemRepresentation(toolURL, true, (UInt8 *) toolPath, toolPathSize);
+        if ( ! success ) {
+            err = coreFoundationUnknownErr;
+        }
+    }
+    
+    if (toolURL != NULL) {
+        CFRelease(toolURL);
+    }
+    
+    return err;
+}
+
+extern OSStatus BASFixFailure(
+	AuthorizationRef			auth,
+	CFStringRef					bundleID,
+	CFStringRef					installToolName,
+	CFStringRef					helperToolName,
+	BASFailCode					failCode
+)
+    // See comment in header.
+{	
+	OSStatus    retval;
+    Boolean     success;
+    char        bundleIDC[PATH_MAX];
+    char        installToolPath[PATH_MAX];
+    char        helperToolPath[PATH_MAX];
+
+    // Pre-conditions
+    
+    assert(auth != NULL);
+    assert(bundleID != NULL);
+    assert(installToolName != NULL);
+    assert(helperToolName  != NULL);
+    
+    // Get the bundle identifier as a UTF-8 C string.  Also, get paths for both of 
+    // the tools.
+    
+    retval = noErr;
+    success = CFStringGetFileSystemRepresentation(bundleID, bundleIDC, sizeof(bundleIDC));
+    if ( ! success ) {
+        retval = coreFoundationUnknownErr;
+    }
+    if (retval == noErr) {
+        retval = GetToolPath(bundleID, installToolName, installToolPath, sizeof(installToolPath));
+    }
+    if (retval == noErr) {
+        retval = GetToolPath(bundleID, helperToolName,  helperToolPath,  sizeof(helperToolPath));
+    }
+    
+    // Depending on the failure code, either run the enable command or the install 
+    // from scratch command.
+    
+    if (retval == noErr) {
+        if (failCode == kBASFailDisabled) {
+            retval = RunInstallToolAsRoot(auth, installToolPath, kBASInstallToolEnableCommand, bundleIDC, NULL);
+        } else {
+            retval = BASInstall(auth, bundleIDC, installToolPath, helperToolPath);
+        }
+    }
+
+    return retval;
+}


Property changes on: branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.c
___________________________________________________________________
Name: svn:executable
   + *

Added: branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.h
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.h	                        (rev 0)
+++ branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.h	2008-08-01 14:07:22 UTC (rev 38876)
@@ -0,0 +1,783 @@
+/*
+	File:       BetterAuthorizationSampleLib.h
+
+    Contains:   Interface to reusable code for privileged helper tools.
+
+    Written by: DTS
+
+    Copyright:  Copyright (c) 2007 Apple Inc. All Rights Reserved.
+
+    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple, Inc.
+                ("Apple") in consideration of your agreement to the following terms, and your
+                use, installation, modification or redistribution of this Apple software
+                constitutes acceptance of these terms.  If you do not agree with these terms,
+                please do not use, install, modify or redistribute this Apple software.
+
+                In consideration of your agreement to abide by the following terms, and subject
+                to these terms, Apple grants you a personal, non-exclusive license, under Apple's
+                copyrights in this original Apple software (the "Apple Software"), to use,
+                reproduce, modify and redistribute the Apple Software, with or without
+                modifications, in source and/or binary forms; provided that if you redistribute
+                the Apple Software in its entirety and without modifications, you must retain
+                this notice and the following text and disclaimers in all such redistributions of
+                the Apple Software.  Neither the name, trademarks, service marks or logos of
+                Apple, Inc. may be used to endorse or promote products derived from the
+                Apple Software without specific prior written permission from Apple.  Except as
+                expressly stated in this notice, no other rights or licenses, express or implied,
+                are granted by Apple herein, including but not limited to any patent rights that
+                may be infringed by your derivative works or by other works in which the Apple
+                Software may be incorporated.
+
+                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+                COMBINATION WITH YOUR PRODUCTS.
+
+                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef _BetterAuthorizationSampleLIB_H
+#define _BetterAuthorizationSampleLIB_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <asl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/////////////////////////////////////////////////////////////////
+
+/*
+    This header has extensive HeaderDoc comments.  To see these comments in a more 
+    felicitous form, you can generate HTML from the HeaderDoc comments using the 
+    following command:
+    
+    $ headerdoc2html BetterAuthorizationSampleLib.h
+    $ open BetterAuthorizationSampleLib/index.html
+*/
+
+/*!
+    @header         BetterAuthorizationSampleLib
+    
+    @abstract       Reusable library for creating helper tools that perform privileged 
+                    operations on behalf of your application.
+
+    @discussion     BetterAuthorizationSampleLib allows you to perform privileged operations 
+                    in a helper tool. In this model, your application runs with standard 
+                    privileges and, when it needs to do a privileged operation, it makes a 
+                    request to the helper tool.  The helper tool uses Authorization Services 
+                    to ensure that the user is authorized to perform that operation.
+                    
+                    BetterAuthorizationSampleLib takes care of all of the mechanics of 
+                    installing the helper tool and communicating with it.  Specifically, it 
+                    has routines that your application can call to:
+                    
+                     1. send requests to a helper tool (BASExecuteRequestInHelperTool) 
+                      
+                     2. install the helper tool if it's not installed, or fix an installation if 
+                        it's broken (BASDiagnoseFailure and BASFixFailure)
+                      
+                    BetterAuthorizationSampleLib also helps you implement the helper tool.  
+					Specifically, you call the routine BASHelperToolMain in the main entry 
+					point for your helper tool, passing it an array of command callbacks (of 
+                    type BASCommandProc).  BASHelperToolMain will take care of all the details 
+                    of communication with the application and only call your callback to 
+                    execute the actual command.
+                    
+                    A command consists of request and response CFDictionaries (or, equivalently, 
+                    NSDictionaries).  BetterAuthorizationSampleLib defines three special keys for 
+                    these dictionaries:
+                    
+                     1. kBASCommandKey -- In the request dictionary, this is the name of the 
+                        command. Its value is a string that uniquely identifies the command within 
+                        your program.
+                    
+                     2. kBASErrorKey -- In the response dictionary, this is the error result for 
+                        the request. Its value is an OSStatus-style error code.
+                    
+                     3. kBASDescriptorArrayKey -- In the response dictionary, if present, this is 
+                        an array of file descriptors being returned from the helper tool.
+
+                    You can use any other key to represent addition parameters (or return values) 
+                    for the command.  The only constraints that BetterAuthorizationSampleLib applies 
+                    to these extra parameters is that they must be serialisable as a CFPropertyList.
+                    
+                    BetterAuthorizationSampleLib requires that you tell it about the list of commands 
+                    that you support.  Each command is represented by a command specification 
+                    (BASCommandSpec).  The command specification includes the following information:
+                    
+                     1. The name of the command.  This is the same as the kBASCommandKey value in 
+                        the request dictionary.
+                      
+                     2. The authorization right associated with the command.  BetterAuthorizationSampleLib 
+						uses this to ensure that the user is authorized to use the command before 
+                        it calls your command callback in the privileged helper tool.
+                        
+                     3. Information to create the command's authorization right specification in the 
+                        policy database.  The is used by the BASSetDefaultRules function.
+                    
+                    Finally, BetterAuthorizationSampleLib includes a number of utilities routines to help 
+                    wrangle error codes (BASErrnoToOSStatus, BASOSStatusToErrno, and BASGetErrorFromResponse) 
+                    and file descriptors (BASCloseDescriptorArray).
+*/
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Command Description
+
+/*!
+    @struct         BASCommandSpec
+    
+    @abstract       Describes a privileged operation to BetterAuthorizationSampleLib.
+    
+    @discussion     Both the application and the tool must tell BetterAuthorizationSampleLib about 
+                    the operations (that is, commands) that they support.  They do this by passing 
+                    in an array of BASCommandSpec structures.  Each element describes one command.  
+                    The array is terminated by a command whose commandName field is NULL.
+                    
+                    In general the application and tool should use the same array definition.  
+                    However, there are cases where these might be out of sync.  For example, if you 
+                    have an older version of the application talking to a newer version of the tool, 
+                    the tool might know about more commands than the application (and thus provide a 
+                    longer array), and that's OK.
+                    
+    @field commandName
+                    A identifier for this command.  This can be any string that is unique within 
+                    the context of your programs.  A NULL value in this field terminates the array.
+					
+					The length of the command name must not be greater than 1024 UTF-16 values.
+
+    @field rightName
+                    This is the name of the authorization right associated with the 
+                    command.  This can be NULL if you don't want any right associated with the 
+                    command.  If it's not NULL, BetterAuthorizationSampleLib will acquire that right 
+                    before allowing the command to execute.
+    
+    @field rightDefaultRule
+                    This is the name of an authorization rule that should be used in 
+                    the default right specification for the right.  To see a full list of these rules, 
+                    look at the "rules" dictionary within the policy database (currently 
+					"/etc/authorization").  Common values include "default" (which requires that the user 
+					hold credentials that authenticate them as an admin user) and "allow" (which will let 
+					anyone acquire the right).
+                    
+                    This must be NULL if (and only if) rightName is NULL.
+
+    @field rightDescriptionKey
+                    This is a key used to form a custom prompt for the right.  The value of this 
+                    string should be a key into a .strings file whose name you supply to 
+                    BASSetDefaultRules.  When BetterAuthorizationSampleLib creates the right specification, 
+                    it uses this key to get all of the localised prompt strings for the right.
+
+                    This must be NULL if rightName is NULL.  Otherwise, this may be NULL if you 
+                    don't want a custom prompt for your right.
+
+    @field userData
+                    This field is is for the benefit of the client; BetterAuthorizationSampleLib 
+                    does not use it in any way.
+*/
+
+struct BASCommandSpec {
+	const char *	commandName;
+	const char *	rightName;
+	const char *	rightDefaultRule;
+	const char *	rightDescriptionKey;
+    const void *    userData;
+};
+typedef struct BASCommandSpec BASCommandSpec;
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Request/Response Keys
+
+// Standard keys for the request dictionary
+
+/*!
+    @define         kBASCommandKey
+    
+    @abstract       Key for the command string within the request dictionary.
+    
+    @discussion     Within a request, this key must reference a string that is the name of the 
+                    command to execute.  This must match one of the commands in the 
+                    BASCommandSpec array.
+					
+					The length of a command name must not be greater than 1024 UTF-16 values.
+*/
+
+#define kBASCommandKey      "com.apple.dts.BetterAuthorizationSample.command"			// CFString
+
+// Standard keys for the response dictionary
+
+/*!
+    @define         kBASErrorKey
+    
+    @abstract       Key for the error result within the response dictionary.
+    
+    @discussion     Within a response, this key must reference a number that is the error result 
+                    for the response, interpreted as an OSStatus.
+*/
+
+#define kBASErrorKey        "com.apple.dts.BetterAuthorizationSample.error"				// CFNumber
+
+/*!
+    @define         kBASDescriptorArrayKey
+    
+    @abstract       Key for a file descriptor array within the response dictionary.
+    
+    @discussion     Within a response, this key, if present, must reference an array 
+					of numbers, which are the file descriptors being returned with 
+					the response.  The numbers are interpreted as ints.
+*/
+
+#define kBASDescriptorArrayKey "com.apple.dts.BetterAuthorizationSample.descriptors"	// CFArray of CFNumber
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Helper Tool Routines
+
+/*!
+    @functiongroup  Helper Tool Routines
+*/
+
+/*!
+    @typedef        BASCommandProc
+    
+    @abstract       Command processing callback.
+    
+    @discussion     When your helper tool calls BASHelperToolMain, it passes in a pointer to an 
+                    array of callback functions of this type.  When BASHelperToolMain receives a 
+                    valid command, it calls one of these function so that your program-specific 
+                    code can process the request.  BAS guarantees that the effective, save and 
+                    real user IDs (EUID, SUID, RUID) will all be zero at this point (that is, 
+                    you're "running as root").
+                    
+                    By the time this callback is called, BASHelperToolMain has already verified that 
+                    this is a known command.  It also acquires the authorization right associated 
+                    with the command, if any.  However, it does nothing to validate the other 
+                    parameters in the request.  These parameters come from a non-privileged source 
+                    and you should verify them carefully.
+                    
+                    Your implementation should get any input parameters from the request and place 
+                    any output parameters in the response.  It can also put an array of file 
+                    descriptors into the response using the kBASDescriptorArrayKey key.
+                    
+                    If an error occurs, you should just return an appropriate error code.  
+                    BASHelperToolMain will ensure that this gets placed in the response.
+                    
+                    You should attempt to fail before adding any file descriptors to the response, 
+                    or remove them once you know that you're going to fail.  If you put file 
+                    descriptors into the response and then return an error, those descriptors will 
+                    still be passed back to the client.  It's likely the client isn't expecting this.
+
+                    Calls to this function will be serialised; that is, once your callback is 
+                    running, BASHelperToolMain won't call you again until you return.  Your callback 
+                    should avoid blocking for long periods of time.  If you block for too long, the 
+                    BAS watchdog will kill the entire helper tool process.
+                    
+                    This callback runs in a daemon context; you must avoid doing things that require the 
+                    user's context.  For example, launching a GUI application would be bad.  See 
+                    Technote 2083 "Daemons and Agents" for more information about execution contexts.
+                    
+    @param auth     This is a reference to the authorization instance associated with the original 
+                    application that made the request.
+                    
+                    This will never be NULL.
+
+    @param userData This is the value from the userData field of the corresponding entry in the 
+                    BASCommandSpec array that you passed to BASHelperToolMain.
+
+    @param request  This dictionary contains the request.  It will have, at a bare minimum, a 
+                    kBASCommandKey item whose value matches one of the commands in the 
+                    BASCommandSpec array you passed to BASHelperToolMain.  It may also have 
+					other, command-specific parameters.
+
+                    This will never be NULL.
+
+    @param response This is a dictionary into which you can place the response.  It will start out 
+                    empty, and you can add any results you please to it.
+
+                    If you need to return file descriptors, place them in an array and place that 
+                    array in the response using the kBASDescriptorArrayKey key.
+                    
+                    There's no need to set the error result in the response.  BASHelperToolMain will 
+                    do that for you.  However, if you do set a value for the kBASErrorKey key, 
+                    that value will take precedence; in this case, the function result is ignored.
+
+                    This will never be NULL.
+
+    @param asl      A reference to the ASL client handle for logging.
+
+                    This may be NULL.  However, ASL handles a NULL input, so you don't need to 
+                    conditionalise your code.
+
+    @param aslMsg   A reference to a ASL message template for logging.
+
+                    This may be NULL.  However, ASL handles a NULL input, so you don't need to 
+                    conditionalise your code.
+*/
+
+typedef OSStatus (*BASCommandProc)(
+	AuthorizationRef			auth,
+    const void *                userData,
+	CFDictionaryRef				request,
+	CFMutableDictionaryRef      response,
+    aslclient                   asl,
+    aslmsg                      aslMsg
+);
+
+/*!
+    @function       BASHelperToolMain
+    
+    @abstract       Entry point for a privileged helper tool.
+    
+    @discussion     You should call this function from the main function of your helper tool.  It takes 
+                    care of all of the details of receiving and processing commands.  It will call you 
+                    back (via one of the commandProcs callbacks) when a valid request arrives.
+                    
+                    This function assumes acts like a replacement for main.  Thus, it assumes that 
+                    it owns various process-wide resources (like SIGALRM and the disposition of 
+                    SIGPIPE).  You should not use those resources, either in your main function or 
+                    in your callback function.  Also, you should not call this function on a thread, 
+					or start any other threads in the process.  Finally, this function has a habit of 
+					exiting the entire process if something goes wrong.  You should not expect the 
+					function to always return.
+                    
+                    This function does not clean up after itself.  When this function returns, you 
+                    are expected to exit.  If the function result is noErr, the command processing 
+                    loop quit in an expected manner (typically because of an idle timeout).  Otherwise 
+                    it quit because of an error.
+
+    @param commands An array that describes the commands that you implement, and their associated 
+                    rights.  The array is terminated by a command with a NULL name.  There must be 
+                    at least one valid command.
+
+    @param commandProcs
+                    An array of callback routines that are called when a valid request arrives.  The 
+                    array is expected to perform the operation associated with the corresponding 
+                    command and set up the response values, if any.  The array is terminated by a 
+                    NULL pointer.
+                    
+                    IMPORTANT: The array must have exactly the same number of entries as the 
+                    commands array.
+					
+	@result			An integer representing EXIT_SUCCESS or EXIT_FAILURE.
+*/
+
+extern int BASHelperToolMain(
+	const BASCommandSpec		commands[], 
+	const BASCommandProc		commandProcs[]
+);
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Application Routines
+
+/*!
+    @functiongroup  Application Routines
+*/
+
+/*!
+    @function       BASSetDefaultRules
+    
+    @abstract       Creates default right specifications in the policy database.
+    
+    @discussion     This routine ensures that the policy database (currently 
+                    "/etc/authorization") contains right specifications for all of the rights 
+                    that you use (as specified by the commands array).  This has two important 
+                    consequences:
+
+                     1. It makes the rights that you use visible to the system administrator.  
+                        All they have to do is run your program once and they can see your default 
+                        right specifications in the policy database. 
+
+                     2. It means that, when the privileged helper tool tries to acquire the right, 
+                        it will use your specification of the right (as modified by the system 
+                        administrator) rather than the default right specification. 
+
+                    You must call this function before calling BASExecuteRequestInHelperTool.  
+                    Typically you would call it at application startup time, or lazily, immediately 
+                    before calling BASExecuteRequestInHelperTool.
+
+    @param auth     A reference to your program's authorization instance; you typically get this 
+                    by calling AuthorizationCreate.
+    
+                    This must not be NULL.
+
+    @param commands An array that describes the commands that you implement, and their associated 
+                    rights.  There must be at least one valid command.
+
+    @param bundleID The bundle identifier for your program.
+
+                    This must not be NULL.
+
+    @param descriptionStringTableName
+                    The name of the .strings file from which to fetch the localised custom 
+                    prompts for the rights in the commands array (if any).  A NULL value is 
+                    equivalent to passing "Localizable" (that is, it gets the prompts from 
+                    "Localizable.strings").
+                    
+                    For example, imagine you have a command for which you require a custom prompt.  
+                    You should put the custom prompt in a .strings file, let's call it 
+                    "AuthPrompts.strings".  You should then pass "AuthPrompts" to this parameter 
+                    and put the key that gets the prompt into the rightDescriptionKey of the command.
+*/
+
+extern void BASSetDefaultRules(
+	AuthorizationRef			auth,
+	const BASCommandSpec		commands[],
+	CFStringRef					bundleID,
+	CFStringRef					descriptionStringTableName
+);
+
+/*!
+    @function       BASExecuteRequestInHelperTool
+    
+    @abstract       Executes a request in the privileged helper tool, returning the response.
+    
+    @discussion     This routine synchronously executes a request in the privileged helper tool and 
+                    returns the response.
+    
+                    If the function returns an error, the IPC between your application and the helper tool 
+                    failed.  Unfortunately it's not possible to tell whether this failure occurred while 
+                    sending the request or receiving the response, thus it's not possible to know whether 
+                    the privileged operation was done or not. 
+
+                    If the functions returns no error, the IPC between your application and the helper tool 
+                    was successful.  However, the command may still have failed.  You must get the error 
+                    value from the response (typically using BASGetErrorFromResponse) to see if the 
+                    command succeeded or not.
+                    
+                    On success the response dictionary may contain a value for the kBASDescriptorArrayKey key.  
+                    If so, that will be a non-empty CFArray of CFNumbers, each of which can be accessed as an int.  
+                    Each value is a descriptor that is being returned to you from the helper tool.  You are 
+					responsible for closing these descriptors when you're done with them. 
+
+    @param auth     A reference to your program's authorization instance; you typically get this 
+                    by calling AuthorizationCreate.
+    
+                    This must not be NULL.
+
+    @param commands An array that describes the commands that you implement, and their associated 
+                    rights.  There must be at least one valid command.
+
+    @param bundleID The bundle identifier for your program.
+
+                    This must not be NULL.
+
+    @param request  A dictionary describing the requested operation.  This must, at least, contain 
+                    a string value for the kBASCommandKey.  Furthermore, this string must match 
+                    one of the commands in the array.
+                    
+                    The dictionary may also contain other values.  These are passed to the helper 
+                    tool unintepreted.  All values must be serialisable using the CFPropertyList 
+                    API.
+
+                    This must not be NULL.
+
+    @param response This must not be NULL.  On entry, *response must be NULL.  On success, *response 
+                    will not be NULL.  On error, *response will be NULL.
+                    
+                    On success, you are responsible for disposing of *response.  You are also 
+                    responsible for closing any descriptors returned in the response.
+	
+	@result			An OSStatus code (see BASErrnoToOSStatus and BASOSStatusToErrno).
+*/
+
+extern OSStatus BASExecuteRequestInHelperTool(
+	AuthorizationRef			auth,
+	const BASCommandSpec		commands[],
+	CFStringRef					bundleID,
+	CFDictionaryRef				request,
+	CFDictionaryRef *			response
+);
+
+/*!
+    @enum           BASFailCode
+    
+    @abstract       Indicates why a request failed.
+    
+    @discussion     If BASExecuteRequestInHelperTool fails with an error (indicating 
+					an IPC failure), you can call BASDiagnoseFailure to determine what 
+					went wrong.  BASDiagnoseFailure will return the value of this 
+					type that best describes the failure.
+
+    @constant kBASFailUnknown
+                    Indicates that BASDiagnoseFailure could not accurately determine the cause of the 
+                    failure.
+
+    @constant kBASFailDisabled
+                    The request failed because the helper tool is installed but disabled.
+
+    @constant kBASFailPartiallyInstalled
+                    The request failed because the helper tool is only partially installed.
+
+    @constant kBASFailNotInstalled 
+                    The request failed because the helper tool is not installed at all.
+
+    @constant kBASFailNeedsUpdate
+                    The request failed because the helper tool is installed but out of date. 
+                    BASDiagnoseFailure will never return this value.  However, if you detect that 
+                    the helper tool is out of date (typically by sending it a "get version" request) 
+                    you can pass this value to BASFixFailure to force it to update the tool.
+*/
+
+enum {
+	kBASFailUnknown,
+	kBASFailDisabled,
+	kBASFailPartiallyInstalled,
+	kBASFailNotInstalled,
+	kBASFailNeedsUpdate
+};
+typedef uint32_t BASFailCode;
+
+/*!
+    @function       BASDiagnoseFailure
+
+    @abstract       Determines the cause of a failed request.
+    
+    @discussion     If BASExecuteRequestInHelperTool fails with an error (indicating an 
+					IPC failure), you can call this routine to determine what went wrong.  
+					It returns a BASFailCode value indicating the cause of the failure.  
+					You should use this value to tell the user what's going on and what 
+					you intend to do about it.  Once you get the user's consent, you can 
+                    call BASFixFailure to fix the problem.
+                    
+                    For example, if this function result is kBASFailDisabled, you could put up the 
+                    dialog saying:
+                    
+                        My privileged helper tool is disabled.  Would you like to enable it?
+                        This operation may require you to authorize as an admin user.
+                        [Cancel] [[Enable]]
+
+                    On the other hand, if this function result is kBASFailNotInstalled, the dialog might be:
+                    
+                        My privileged helper tool is not installed.  Would you like to install it?
+                        This operation may require you to authorize as an admin user.
+                        [Cancel] [[Install]]
+                    
+                    BASDiagnoseFailure will never return kBASFailNeedsUpdate.  It's your responsibility 
+                    to detect version conflicts (a good way to do this is by sending a "get version" request 
+                    to the helper tool).  However, once you've detected a version conflict, you can pass 
+                    kBASFailNeedsUpdate to BASFixFailure to get it to install the latest version of your 
+                    helper tool.
+
+                    If you call this routine when everything is working properly, you're likely to get 
+                    a result of kBASFailUnknown.
+
+    @param auth     A reference to your program's authorization instance; you typically get this 
+                    by calling AuthorizationCreate.
+    
+                    This must not be NULL.
+
+    @param bundleID The bundle identifier for your program.
+
+                    This must not be NULL.
+    
+    @result         A BASFailCode value indicating the cause of the failure.  This will never be 
+                    kBASFailNeedsUpdate.
+*/
+
+extern BASFailCode BASDiagnoseFailure(
+	AuthorizationRef			auth,
+	CFStringRef					bundleID
+);
+
+/*!
+    @function       BASFixFailure
+
+    @abstract       Installs, or reinstalls, the privileged helper tool.
+    
+    @discussion     This routine installs or reinstalls the privileged helper tool.  Typically 
+                    you call this in response to an IPC failure talking to the tool.  You first 
+                    diagnose the failure using BASDiagnoseFailure and then call this routine to 
+					fix the failure by installing (or reinstalling) the tool.
+                    
+                    Because the helper tool is privileged, installing it is a privileged 
+                    operation.  This routine will do its work by calling 
+                    AuthorizationExecuteWithPrivileges, which is likely to prompt the user 
+                    for an admin name and password.
+
+    @param auth     A reference to your program's authorization instance; you typically get this 
+                    by calling AuthorizationCreate.
+    
+                    This must not be NULL.
+
+    @param bundleID The bundle identifier for your program.
+
+                    This must not be NULL.
+
+    @param installToolName
+                    The name of the install tool within your bundle.  You should place the tool 
+                    in the executable directory within the bundle.  Specifically, the tool must be 
+                    available by passing this name to CFBundleCopyAuxiliaryExecutableURL.
+
+                    This must not be NULL.
+
+    @param helperToolName
+                    The name of the helper tool within your bundle.  You should place the tool 
+                    in the executable directory within the bundle.  Specifically, the tool must be 
+                    available by passing this name to CFBundleCopyAuxiliaryExecutableURL.
+
+                    This must not be NULL.
+
+    @param failCode A value indicating the type of failure that's occurred.  In most cases you get this 
+                    value by calling BASDiagnoseFailure.
+	
+	@result			An OSStatus code (see BASErrnoToOSStatus and BASOSStatusToErrno).			
+*/
+
+extern OSStatus BASFixFailure(
+	AuthorizationRef			auth,
+	CFStringRef					bundleID,
+	CFStringRef					installToolName,
+	CFStringRef					helperToolName,
+	BASFailCode					failCode
+);
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Utility Routines
+
+/*!
+    @functiongroup  Utilities
+*/
+
+/*!
+    @function       BASErrnoToOSStatus
+
+    @abstract       Convert an errno value to an OSStatus value.
+    
+    @discussion     All errno values have accepted alternatives in the errSecErrnoBase 
+					OSStatus range, and this routine does the conversion. For example, 
+					ENOENT becomes errSecErrnoBase + ENOENT. Any value that's not 
+					recognised just gets passed through unmodified.
+                    
+                    A value of 0 becomes noErr.
+
+					For more information about errSecErrnoBase, see DTS Q&A 1499 
+					<http://developer.apple.com/qa/qa2006/qa1499.html>.
+					
+    @param errNum   The errno value to convert.
+	
+	@result			An OSStatus code representing the errno equivalent.
+*/
+
+extern OSStatus BASErrnoToOSStatus(int errNum);
+
+/*!
+    @function       BASOSStatusToErrno
+
+    @abstract       Convert an OSStatus value to an errno value.
+    
+    @discussion     This function converts some specific OSStatus values (Open Transport and
+					errSecErrnoBase ranges) to their corresponding errno values.  It more-or-less 
+					undoes the conversion done by BASErrnoToOSStatus, including a pass 
+					through for unrecognised values.
+                    
+                    It's worth noting that there are many more defined OSStatus error codes 
+                    than errno error codes, so you're more likely to encounter a passed 
+                    through value when going in this direction.
+
+                    A value of noErr becomes 0.
+
+					For more information about errSecErrnoBase, see DTS Q&A 1499 
+					<http://developer.apple.com/qa/qa2006/qa1499.html>.
+
+    @param errNum   The OSStatus value to convert.
+	
+	@result			An integer code representing the OSStatus equivalent.
+*/
+
+extern int      BASOSStatusToErrno(OSStatus errNum);
+
+/*!
+    @function       BASGetErrorFromResponse
+
+    @abstract       Extracts the error status from a helper tool response.
+
+    @discussion     This function extracts the error status from a helper tool response. 
+                    Specifically, its uses the kBASErrorKey key to get a CFNumber and 
+                    it gets the resulting value from that number.
+
+    @param response A helper tool response, typically acquired by calling BASExecuteRequestInHelperTool.
+    
+                    This must not be NULL
+	
+	@result			An OSStatus code (see BASErrnoToOSStatus and BASOSStatusToErrno).
+*/
+
+extern OSStatus BASGetErrorFromResponse(CFDictionaryRef response);
+
+/*!
+    @function       BASCloseDescriptorArray
+
+    @abstract       Closes all of the file descriptors referenced by a CFArray.
+
+    @discussion     Given a CFArray of CFNumbers, treat each number as a file descriptor 
+                    and close it.
+
+                    The most common reason to use this routine is that you've executed, 
+                    using BASExecuteRequestInHelperTool, a request that returns a response 
+                    with embedded file descriptors, and you want to close those descriptors. 
+                    In that case, you typically call this as:
+
+                    BASCloseDescriptorArray( CFDictionaryGetValue(response, CFSTR(kBASDescriptorArrayKey)) );
+
+    @param descArray
+                    The array containing the descriptors to close.
+    
+                    This may be NULL, in which case the routine does nothing.
+*/
+
+extern void BASCloseDescriptorArray(
+	CFArrayRef					descArray
+);
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Utility Routines
+
+// The following definitions are exported purely for the convenience of the 
+// install tool ("BetterAuthorizationSampleLibInstallTool.c").  You must not 
+// use them in your own code.
+
+#if !defined(BAS_PRIVATE)
+    #define BAS_PRIVATE 0
+#endif
+#if BAS_PRIVATE
+
+	// Hard-wired file system paths for the launchd property list file and 
+	// the privileged helper tool.  In all cases, %s is a placeholder 
+	// for the bundle ID (in file system representation).
+	
+    #define kBASPlistPathFormat             "/Library/LaunchDaemons/%s.plist"
+
+    #define kBASToolDirPath                 "/Library/PrivilegedHelperTools"			// KEEP IN SYNC!
+    #define kBASToolPathFormat              "/Library/PrivilegedHelperTools/%s"			// KEEP IN SYNC!
+	
+	// Commands strings for the install tool.
+
+    #define kBASInstallToolInstallCommand "install"
+    #define kBASInstallToolEnableCommand  "enable"
+
+	// Magic values used to bracket the process ID returned by the install tool.
+	
+    #define kBASAntiZombiePIDToken1 "cricket<"
+    #define kBASAntiZombiePIDToken2 ">bat"
+    
+    // Magic value used to indicate success or failure from the install tool.
+    
+    #define kBASInstallToolSuccess "oK"
+    #define kBASInstallToolFailure "FailUrE %d"
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif


Property changes on: branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLib.h
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLibInstallTool.c
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLibInstallTool.c	                        (rev 0)
+++ branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLibInstallTool.c	2008-08-01 14:07:22 UTC (rev 38876)
@@ -0,0 +1,472 @@
+/*
+	File:       BetterAuthorizationSampleLibInstallTool.c
+
+    Contains:   Tool to install BetterAuthorizationSampleLib-based privileged helper tools.
+
+    Written by: DTS
+
+    Copyright:  Copyright (c) 2007 Apple Inc. All Rights Reserved.
+
+    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple, Inc.
+                ("Apple") in consideration of your agreement to the following terms, and your
+                use, installation, modification or redistribution of this Apple software
+                constitutes acceptance of these terms.  If you do not agree with these terms,
+                please do not use, install, modify or redistribute this Apple software.
+
+                In consideration of your agreement to abide by the following terms, and subject
+                to these terms, Apple grants you a personal, non-exclusive license, under Apple's
+                copyrights in this original Apple software (the "Apple Software"), to use,
+                reproduce, modify and redistribute the Apple Software, with or without
+                modifications, in source and/or binary forms; provided that if you redistribute
+                the Apple Software in its entirety and without modifications, you must retain
+                this notice and the following text and disclaimers in all such redistributions of
+                the Apple Software.  Neither the name, trademarks, service marks or logos of
+                Apple, Inc. may be used to endorse or promote products derived from the
+                Apple Software without specific prior written permission from Apple.  Except as
+                expressly stated in this notice, no other rights or licenses, express or implied,
+                are granted by Apple herein, including but not limited to any patent rights that
+                may be infringed by your derivative works or by other works in which the Apple
+                Software may be incorporated.
+
+                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+                COMBINATION WITH YOUR PRODUCTS.
+
+                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+// Allows access to path information associated with tool and plist installation
+// from BetterAuthorizationSampleLib.h
+#define BAS_PRIVATE 1		
+
+#include "BetterAuthorizationSampleLib.h"
+
+extern char **environ;
+
+static int RunLaunchCtl(
+	bool						junkStdIO, 
+	const char					*command, 
+	const char					*plistPath
+)
+	// Handles all the invocations of launchctl by doing the fork() + execve()
+	// for proper clean-up. Only two commands are really supported by our
+	// implementation; loading and unloading of a job via the plist pointed at 
+	// (const char *) plistPath.
+{	
+	int				err;
+	const char *	args[5];
+	pid_t			childPID;
+	pid_t			waitResult;
+	int				status;
+	
+	// Pre-conditions.
+	assert(command != NULL);
+	assert(plistPath != NULL);
+	
+    // Make sure we get sensible logging even if we never get to the waitpid.
+    
+    status = 0;
+    
+    // Set up the launchctl arguments.  We run launchctl using StartupItemContext 
+	// because, in future system software, launchctl may decide on the launchd 
+	// to talk to based on your Mach bootstrap namespace rather than your RUID.
+    
+	args[0] = "/bin/launchctl";
+	args[1] = command;				// "load" or "unload"
+	args[2] = "-w";
+	args[3] = plistPath;			// path to plist
+	args[4] = NULL;
+
+    fprintf(stderr, "launchctl %s %s '%s'\n", args[1], args[2], args[3]);
+	
+    // Do the standard fork/exec dance.
+    
+	childPID = fork();
+	switch (childPID) {
+		case 0:
+			// child
+			err = 0;
+            
+            // If we've been told to junk the I/O for launchctl, open 
+            // /dev/null and dup that down to stdin, stdout, and stderr.
+            
+			if (junkStdIO) {
+				int		fd;
+				int		err2;
+
+				fd = open("/dev/null", O_RDWR);
+				if (fd < 0) {
+					err = errno;
+				}
+				if (err == 0) {
+					if ( dup2(fd, STDIN_FILENO) < 0 ) {
+						err = errno;
+					}
+				}
+				if (err == 0) {
+					if ( dup2(fd, STDOUT_FILENO) < 0 ) {
+						err = errno;
+					}
+				}
+				if (err == 0) {
+					if ( dup2(fd, STDERR_FILENO) < 0 ) {
+						err = errno;
+					}
+				}
+				err2 = close(fd);
+				if (err2 < 0) {
+					err2 = 0;
+				}
+				if (err == 0) {
+					err = err2;
+				}
+			}
+			if (err == 0) {
+				err = execve(args[0], (char **) args, environ);
+			}
+			if (err < 0) {
+				err = errno;
+			}
+			_exit(EXIT_FAILURE);
+			break;
+		case -1:
+			err = errno;
+			break;
+		default:
+			err = 0;
+			break;
+	}
+	
+    // Only the parent gets here.  Wait for the child to complete and get its 
+    // exit status.
+	
+	if (err == 0) {
+		do {
+			waitResult = waitpid(childPID, &status, 0);
+		} while ( (waitResult == -1) && (errno == EINTR) );
+
+		if (waitResult < 0) {
+			err = errno;
+		} else {
+			assert(waitResult == childPID);
+
+            if ( ! WIFEXITED(status) || (WEXITSTATUS(status) != 0) ) {
+                err = EINVAL;
+            }
+		}
+	}
+
+    fprintf(stderr, "launchctl -> %d %ld 0x%x\n", err, (long) childPID, status);
+	
+	return err;
+}
+
+static int CopyFileOverwriting(
+	const char					*sourcePath, 
+	mode_t						destMode, 
+	const char					*destPath
+)
+	// Our own version of a file copy. This routine will either handle
+	// the copy of the tool binary or the plist file associated with
+	// that binary. As the function name suggests, it writes over any 
+	// existing file pointed to by (const char *) destPath.
+{
+	int			err;
+	int			junk;
+	int			sourceFD;
+	int			destFD;
+	char		buf[65536];
+	
+	// Pre-conditions.
+	assert(sourcePath != NULL);
+	assert(destPath != NULL);
+	
+    (void) unlink(destPath);
+	
+	destFD = -1;
+	
+	err = 0;
+	sourceFD = open(sourcePath, O_RDONLY);
+	if (sourceFD < 0) {
+		err = errno;
+	}
+	
+	if (err == 0) {
+		destFD = open(destPath, O_CREAT | O_EXCL | O_WRONLY, destMode);
+		if (destFD < 0) {
+			err = errno;
+		}
+	}
+	
+	if (err == 0) {
+		ssize_t	bytesReadThisTime;
+		ssize_t	bytesWrittenThisTime;
+		ssize_t	bytesWritten;
+		
+		do {
+			bytesReadThisTime = read(sourceFD, buf, sizeof(buf));
+			if (bytesReadThisTime < 0) {
+				err = errno;
+			}
+			
+			bytesWritten = 0;
+			while ( (err == 0) && (bytesWritten < bytesReadThisTime) ) {
+				bytesWrittenThisTime = write(destFD, &buf[bytesWritten], bytesReadThisTime - bytesWritten);
+				if (bytesWrittenThisTime < 0) {
+					err = errno;
+				} else {
+					bytesWritten += bytesWrittenThisTime;
+				}
+			}
+
+		} while ( (err == 0) && (bytesReadThisTime != 0) );
+	}
+	
+	// Clean up.
+	
+	if (sourceFD != -1) {
+		junk = close(sourceFD);
+		assert(junk == 0);
+	}
+	if (destFD != -1) {
+		junk = close(destFD);
+		assert(junk == 0);
+	}
+
+    fprintf(stderr, "copy '%s' %#o '%s' -> %d\n", sourcePath, (int) destMode, destPath, err);
+	
+	return err;
+}
+
+static int InstallCommand(
+	const char *				bundleID, 
+	const char *				toolSourcePath, 
+	const char *				plistSourcePath
+)
+	// Heavy lifting function for handling all the necessary steps to install a
+	// helper tool in the correct location, with the correct permissions,
+	// and call launchctl in order to load it as a current job.
+{
+	int			err;
+	char		toolDestPath[PATH_MAX];
+	char		plistDestPath[PATH_MAX];
+	struct stat	sb;
+    static const mode_t kDirectoryMode  = ACCESSPERMS & ~(S_IWGRP | S_IWOTH);
+    static const mode_t kExecutableMode = ACCESSPERMS & ~(S_IWGRP | S_IWOTH);
+    static const mode_t kFileMode       = DEFFILEMODE & ~(S_IWGRP | S_IWOTH);
+	
+	// Pre-conditions.
+	assert(bundleID != NULL);
+	assert(toolSourcePath != NULL);
+	assert(plistSourcePath != NULL);
+	
+	(void) snprintf(toolDestPath,  sizeof(toolDestPath),  kBASToolPathFormat,  bundleID);
+	(void) snprintf(plistDestPath, sizeof(plistDestPath), kBASPlistPathFormat, bundleID);
+
+    // Stop the helper tool if it's currently running.
+
+	(void) RunLaunchCtl(true, "unload", plistDestPath);
+
+    // Create the PrivilegedHelperTools directory.  The owner will be "root" because 
+    // we're running as root (our EUID is 0).  The group will be "admin" because 
+    // it's inherited from "/Library".  The permissions will be rwxr-xr-x because 
+    // of kDirectoryMode combined with our umask.
+
+	err = mkdir(kBASToolDirPath, kDirectoryMode);
+	if (err < 0) {
+		err = errno;
+	}
+    fprintf(stderr, "mkdir '%s' %#o -> %d\n", kBASToolDirPath, kDirectoryMode, err);
+	if ( (err == 0) || (err == EEXIST) ) {
+		err = stat(kBASToolDirPath, &sb);
+		if (err < 0) {
+			err = errno;
+		}
+    }
+    
+    // /Library/PrivilegedHelperTools may have come from a number of places:
+    //
+    // A. We may have just created it.  In this case it will be 
+    //    root:admin rwxr-xr-x.
+    //
+    // B. It may have been correctly created by someone else.  By definition, 
+    //    that makes it root:wheel rwxr-xr-x.
+    //
+    // C. It may have been created (or moved here) incorrectly (or maliciously) 
+    //    by someone else.  In that case it will be u:g xxxxxxxxx, where u is 
+    //    not root, or root:g xxxxwxxwx (that is, root-owned by writeable by 
+    //    someone other than root).
+    //
+    // In case A, we want to correct the group.  In case B, we want to do 
+    // nothing.  In case C, we want to fail.
+
+    if (err == 0) {
+        if ( (sb.st_uid == 0) && (sb.st_gid == 0) ) {
+            // case B -- do nothing
+        } else if ( (sb.st_uid == 0) && (sb.st_gid != 0) && ((sb.st_mode & ALLPERMS) == kDirectoryMode) ) {
+            // case A -- fix the group ID
+            // 
+            // This is safe because /Library is sticky and the file is owned 
+            // by root, which means that only root can move it.  Also, we 
+            // don't have to worry about malicious files existing within the 
+            // directory because its only writeable by root.
+
+            err = chown(kBASToolDirPath, -1, 0);
+            if (err < 0) {
+                err = errno;
+            }
+            fprintf(stderr, "chown -1:0 '%s' -> %d\n", kBASToolDirPath, err);
+        } else {
+            fprintf(stderr, "bogus perms on '%s' %d:%d %o\n", kBASToolDirPath, (int) sb.st_uid, (int) sb.st_gid, (int) sb.st_mode);
+            err = EPERM;
+        }
+	}
+
+    // Then create the known good copy.  The ownership and permissions 
+    // will be set appropriately, as described in the comments for mkdir. 
+    // We don't have to worry about atomicity because this tool won't be 
+    // looked at until our plist is installed.
+
+	if (err == 0) {
+		err = CopyFileOverwriting(toolSourcePath, kExecutableMode, toolDestPath);
+	}
+
+    // For the plist, our caller has created the file in /tmp and we just copy it 
+    // into the correct location.  This ensures that the file is complete 
+    // and valid before anyone starts looking at it and will also overwrite 
+	// any existing file with this new version.
+    // 
+	// Since we have to read/write in the file byte by byte to make sure that 
+	// the file is complete we are rolling our own 'copy'. This clearly is 
+	// ignoring atomicity since we do not roll back to the state of 'what was 
+	// previously there' if there is an error; rather, whatever has been 
+	// written up to that point of granular failure /is/ the state of the 
+	// plist file.
+
+	if (err == 0) {
+		err = CopyFileOverwriting(plistSourcePath, kFileMode, plistDestPath);
+	}
+	
+    // Use launchctl to load our job.  The plist file starts out disabled, 
+    // so we pass "-w" to enable it permanently.
+
+	if (err == 0) {
+		err = RunLaunchCtl(false, "load", plistDestPath);
+	}
+	
+	return err;
+}
+
+static int EnableCommand(
+	const char					*bundleID
+)
+	// Utility function to call through to RunLaunchCtl in order to load a job
+	// given by the path contructed from the (const char *) bundleID.
+{
+	int		err;
+	char	plistPath[PATH_MAX];
+	
+	// Pre-condition.
+	assert(bundleID != NULL);
+	
+	(void) snprintf(plistPath, sizeof(plistPath), kBASPlistPathFormat, bundleID);
+	err = RunLaunchCtl(false, "load", plistPath);
+
+	return err;
+}
+
+int main(int argc, char **argv)
+{
+	int err;
+	
+	// Print our PID so that the app can avoid creating zombies.
+
+	fprintf(stdout, kBASAntiZombiePIDToken1 "%ld" kBASAntiZombiePIDToken2 "\n", (long) getpid());
+	fflush(stdout);
+
+    // On the client side, AEWP only gives a handle to stdout, so we dup stdout 
+    // downto stderr for the rest of this tool.  This ensures that all our output 
+	// makes it to the client.
+
+	err = dup2(STDOUT_FILENO, STDERR_FILENO);
+	if (err < 0) {
+		err = errno;
+	} else {
+		err = 0;
+	}
+
+    // Set up the standard umask.  The goal here is to be robust in the 
+	// face of common environmental changes, not to resist a malicious attack.
+	// Also sync the RUID to the 0 because launchctl keys off the RUID (at least 
+	// on 10.4.x).
+
+	if (err == 0) {
+		(void) umask(S_IWGRP | S_IWOTH);
+		
+        err = setuid(0);
+        if (err < 0) {
+            fprintf(stderr, "setuid\n");
+            err = EINVAL;
+        }
+	}
+	
+	if ( (err == 0) && (argc < 2) ) {
+		fprintf(stderr, "usage\n");
+		err = EINVAL;
+	}
+
+	// The first argument is the command.  Switch off that and extract the 
+	// remaining arguments and pass them to our command routines.
+	
+	if (err == 0) {
+		if ( strcmp(argv[1], kBASInstallToolInstallCommand) == 0 ) {
+			if (argc == 5) {
+				err = InstallCommand(argv[2], argv[3], argv[4]);
+			} else {
+				fprintf(stderr, "usage3\n");
+				err = EINVAL;
+			}
+		} else if ( strcmp(argv[1], kBASInstallToolEnableCommand) == 0 ) {
+			if (argc == 3) {
+				err = EnableCommand(argv[2]);
+			} else {
+				fprintf(stderr, "usage4\n");
+				err = EINVAL;
+			}
+		} else {
+			fprintf(stderr, "usage2\n");
+			err = EINVAL;
+		}
+	}
+
+	// Write "oK" to stdout and quit.  The presence of the "oK" on the last 
+	// line of output is used by the calling code to detect success.
+	
+	if (err == 0) {
+		fprintf(stderr, kBASInstallToolSuccess "\n");
+    } else {
+		fprintf(stderr, kBASInstallToolFailure "\n", err);
+	}
+	
+	return (err == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}


Property changes on: branches/gsoc08-framework/MacPorts_Framework/BetterAuthorizationSampleLibInstallTool.c
___________________________________________________________________
Name: svn:executable
   + *

Added: branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.c
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.c	                        (rev 0)
+++ branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.c	2008-08-01 14:07:22 UTC (rev 38876)
@@ -0,0 +1,36 @@
+/*
+ *  MPHelperCommon.c
+ *  MacPorts.Framework
+ *
+ *  Created by George  Armah on 7/31/08.
+ *  Copyright 2008 Lafayette College. All rights reserved.
+ *
+ */
+
+#include "MPHelperCommon.h"
+
+
+/*
+ IMPORTANT
+ ---------
+ This array must be exactly parallel to the kMPHelperCommandProcs array 
+ in "MPHelperTool.m".
+ */
+
+const BASCommandSpec kMPHelperCommandSet[] = {
+
+	{	kMPHelperEvaluateTclCommand,		//commandName
+		kMPHelperEvaluateTclRightsName,		//rightName
+		"default",							//rightDefaultRule	-- by default, you have to have admin credentials
+		NULL,								//rightDescriptionKey 
+		NULL								// userData ... I might use this to pass the NSError object later on
+	},
+
+	{	NULL,								//the array is null terminated
+		NULL,
+		NULL,
+		NULL,
+		NULL
+	}
+	
+};

Added: branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.h
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.h	                        (rev 0)
+++ branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.h	2008-08-01 14:07:22 UTC (rev 38876)
@@ -0,0 +1,42 @@
+/*
+ *  MPHelperCommon.h
+ *  MacPorts.Framework
+ *
+ *  Created by George  Armah on 7/31/08.
+ *  Copyright 2008 Lafayette College. All rights reserved.
+ *
+ */
+
+#ifndef _MPHELPERCOMMON_H
+#define _MPHELPERCOMMON_H
+
+#include "BetterAuthorizationSampleLib.h"
+
+//We need only one command for this Tool
+
+#define kMPHelperEvaluateTclCommand					"EvaluateTcl"
+
+	// authorization right name
+	
+	#define kMPHelperEvaluateTclRightsName			"com.MacPorts.MacPortsFramework.EvaluateTcl"
+
+	// request  keys 
+	// Should I put the NSError object in the request dictionary ? 
+	// I'll try that for now and see how it goes
+	
+	//String to be Evlauted
+	#define kTclStringToBeEvaluated		"TclString"					//CFString 
+	
+
+	//response keys
+	#define kTclStringEvaluationResult	"TclStringEvaluationResult"		//CFString
+
+	//Actually hold off doing errors for now
+	//NSError object we are passing
+	#define kNSErrorString				"NSErrorString"				//Am I allowed to pass in an NSError object?
+																	//Lets make it a string for now
+
+
+extern const BASCommandSpec kMPHelperCommandSet[];
+
+#endif
\ No newline at end of file


Property changes on: branches/gsoc08-framework/MacPorts_Framework/MPHelperCommon.h
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: branches/gsoc08-framework/MacPorts_Framework/MPHelperTool.m
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPHelperTool.m	2008-08-01 12:15:55 UTC (rev 38875)
+++ branches/gsoc08-framework/MacPorts_Framework/MPHelperTool.m	2008-08-01 14:07:22 UTC (rev 38876)
@@ -6,193 +6,104 @@
 //  Copyright 2008 Lafayette College. All rights reserved.
 //
 
+//#include <netinet/in.h>
+#include <stdio.h>
+//#include <sys/socket.h>
+#include <unistd.h>
+
+#include <CoreServices/CoreServices.h>
+#include "BetterAuthorizationSampleLib.h"
+#include "MPHelperCommon.h"
+
 #import <Cocoa/Cocoa.h>
-#import <Foundation/Foundation.h>
 #import <Security/Security.h>
 #import "MPInterpreter.h"
-#include <stdlib.h>
-#include <sys/stat.h>
 
 
+static OSStatus DoEvaluateTclString (
+	AuthorizationRef			auth,
+	const void *				userData,
+	CFDictionaryRef				request,
+	CFMutableDictionaryRef		response,
+	aslclient					asl,
+	aslmsg						aslMsg
+)
 
-// /////////////////////////////////////////////////////////////////////////////
-// exitCleanly
-//
-// Exits the program with the correct status and releases the autorelease pool.
-void exitCleanly(int code, NSAutoreleasePool *pool)
 {
-	[pool release];
-	exit(code);
+	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 NSString that was passed in the request dictionary
+	NSString * tclCmd;
+	CFStringRef  cTclCmd = (CFStringRef)CFDictionaryGetValue(request, CFSTR(kTclStringToBeEvaluated));
+	
+	if (cTclCmd != NULL) {
+		tclCmd = (NSString *) cTclCmd;
+	}
+	else {
+		//something went wrong ... do some error handling
+		retval = coreFoundationUnknownErr;
+	}
+	
+	MPInterpreter * interp = [MPInterpreter sharedInterpreter];
+	NSError * evalError;
+	NSString * result = [interp evaluateStringAsString:tclCmd 
+												 error:&evalError];
+	CFStringRef cResult = (CFStringRef) result;
+	
+	if( result != nil && evalError != nil) {
+		CFDictionaryAddValue(response, CFSTR(kTclStringEvaluationResult), cResult); 
+	}
+	else{
+		//Try setting the user data pointer to the error
+		retval = coreFoundationUnknownErr;
+	}
+	
+	return retval;
 }
 
 
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Tool Infrastructure
 
-//This code is adapted from :
-// http://forums.macrumors.com/showthread.php?t=508394
+/*
+ IMPORTANT
+ ---------
+ This array must be exactly parallel to the kMPHelperCommandSet array 
+ in "MPHelperCommon.c".
+ */
 
-//I will first try implementing this without any Authorization services.
-//I just want a helper tool whose user id is set to 0 and can self repair itself
-//I will be using message passing for IPC rather than piping or anything else
-//like that. Check on IRC if there are any dangers in doing that.
+static const BASCommandProc kMPHelperCommandProcs[] = {
+	DoEvaluateTclString,
+	NULL
+};
 
 
 
 
-//Usage 
-// ./MPHelperTool --self-repair srOptions --rec-count recOptions interpCmd
-// So argv is of size 6 ... no more no less 
+//Should I just do stuff in main and use the above method to 
+//just retrieve the string to be evaluated as a tcl command?
 
-// srOptions is a C string with value of "yes" or "no" to tell us whether or not
-// to run self repair
-
-//recOptions is a number telling us how many times we have been called recursively
-
-//interpCmd is a the 
-
 int main(int argc, char const * argv[]) {
 	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 	
-	setuid(0);
-	NSLog(@"UID is %i", geteuid());	
-	//Check for right number of args
-	if (argc != 6) {
-		exitCleanly(TCL_ERROR , pool);
-	}
+	// Go directly into BetterAuthorizationSampleLib code.
 	
-	//The second thing to check for is recOptions ... This method should be called
-	//recursively at most once. Its a bit weird but I think from the code above,
-	//this tool repairs itself AND THEN proceeds to execute the required command all in
-	//one process. This means the number of recursive calls should not exceed one.
-	//If we fail to self repair at the first try, we should just exit.
-
-									 
-	int recOptions = [[NSString stringWithCString:argv[4] encoding:NSUTF8StringEncoding] intValue];
-	if (recOptions > 1) {
-		exitCleanly(TCL_ERROR, pool);
-	}
-	else {
-		++recOptions;
-	}
+    // IMPORTANT
+    // BASHelperToolMain doesn't clean up after itself, so once it returns 
+    // we must quit.
+    BASHelperToolMain(kMPHelperCommandSet, kMPHelperCommandProcs);
 	
-	MPInterpreter * interp = [MPInterpreter sharedInterpreter];
-	NSString * interpCmd = [NSString stringWithCString:argv[5] encoding:NSUTF8StringEncoding];
-	NSString * _path_to_self = [[NSBundle mainBundle] pathForResource:@"MPHelperTool" 
-															   ofType:nil];
 	
-	//OSStatus status;
-	BOOL authenticatedMode = YES;
-	//AuthorizationRef auth;
-	//AuthorizationExternalForm extAuth;
+	[pool release];
 	
-	//This memory pointer should be valid till _path_to_self is freed or released
-	//in the autorelease pool .. in other words its save for our purposes
-	//and we dont' have to worry about releasing memory
-	const char * path_to_self = [_path_to_self cStringUsingEncoding:NSUTF8StringEncoding];
-	
-	if (!strcmp(argv[1], "--self-repair") && !strcmp(argv[2], "yes") )
-	{
-		NSLog(@"MacPortsFramework MPHelperTool main Self-repair. Starting");
-		// We have started ourself in self-repair mode.  This means that this executable is not owned by root and/or the setuid bit is not set.  We need to recover that...
-		struct stat st;
-        int fd_tool;
-		
-		
-		//We don't need this code for now
-		/* Recover the passed in AuthorizationRef.*/
-		
-		
-		/* Open tool exclusively, so noone can change it while we bless it */
-        fd_tool = open(path_to_self, O_NONBLOCK|O_RDONLY|O_EXLOCK, 0);
-		
-        if (fd_tool == -1)
-        {
-            NSLog(@"MacPortsFramework MPHelperTool main Self-Repair. Exclusive open while repairing tool failed: %d.",errno);
-			exitCleanly(-1,pool);
-        }
-		
-        if (fstat(fd_tool, &st))
-		{
-			NSLog(@"MacPortsFramework MPHelperTool main Self-Repair. fstat failed");
-            exitCleanly(-1,pool);
-		}
-        
-        if (st.st_uid != 0)
-		{
-            fchown(fd_tool, 0, st.st_gid);
-		}
-		
-		
-        /* Disable group and world writability and make setuid root. */
-        fchmod(fd_tool, (st.st_mode & (~(S_IWGRP|S_IWOTH))) | S_ISUID);
-		
-        close(fd_tool);
-		
-		NSLog(@"MacPortsFramework MPHelperTool main Self-repair. Complete");
-		authenticatedMode = YES;
-		
-		
-		/*/Hopefully this works
-		int result = [interp execute:_path_to_self 
-							withArgs:[NSArray arrayWithObjects:SELF_REPAIR, @"no", REC_COUNT, 
-									  [NSString stringWithFormat:@"%d", recOptions], interpCmd,  nil]];
-		
-		exitCleanly(result, pool);*/
-		
-		
-	}
-	else
-	{
-		//To Do
-		//Add code here to receive Authorization reference from somwhere
-		
-		authenticatedMode = YES;
-	}
-	
-	/* If we are not running as root we need to self-repair. But we don't want to do it more than once
-	 which means */
-	if (authenticatedMode && geteuid() != 0)
-	{
-		NSLog(@"MacPortsFramework MPHelperTool main Normal-Mode. Not running as root! Starting self-repair mode.");
-		
-		//We run again in self repair mode. I am assuming that this new "forked" process
-		// will be able to repair the binary and execute the command successfully ... 
-		//if it fails I guess i should return something to that effect?
-		int result = [interp execute:_path_to_self 
-			   withArgs:[NSArray arrayWithObjects:SELF_REPAIR, @"yes", REC_COUNT, 
-						 [NSString stringWithFormat:@"%d", recOptions], interpCmd,  nil]];
-		
-		
-		//Is the above method guaranteed to always complete before the
-		//program execution gets here?
-		exitCleanly(result, pool);
-		
-		
-	}
-	
-	//Now we can finally execute the method ... whew
-	if (interpCmd != nil) {
-		NSError * evalError;
-		NSLog(@"Executin Tcl command %@", interpCmd);
-		
-		NSString * result = [interp evaluateStringAsString:interpCmd 
-													 error:&evalError];
-		if(result == nil && evalError) {
-			NSLog(@"Command %@ exited with Error %@", interpCmd, evalError);
-			exitCleanly(TCL_ERROR,pool);
-		}
-		else {
-			NSLog(@"Command %@ returned %@", interpCmd, result);
-			exitCleanly(TCL_OK, pool);
-		}
-		/*while(*argv != NULL) {
-			NSLog(@"Passed parameter is %@", [NSString stringWithCString:*argv]);
-			++argv;
-		}*/
-	}
-	
-    [pool release];
-	
-	
     return 0;
 }
 

Modified: branches/gsoc08-framework/MacPorts_Framework/MPInterpreter.h
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPInterpreter.h	2008-08-01 12:15:55 UTC (rev 38875)
+++ branches/gsoc08-framework/MacPorts_Framework/MPInterpreter.h	2008-08-01 14:07:22 UTC (rev 38876)
@@ -47,6 +47,7 @@
 //Defining some flags for MPHelperTool
 #define SELF_REPAIR @"--self-repair"
 #define REC_COUNT @"--rec-count"
+#define MP_HELPER @"MPHelperTool"
 
 #define	MPPackage				@"macports"
 #define MPPackageVersion		@"1.0"
@@ -65,10 +66,12 @@
 @interface MPInterpreter : NSObject {
 
 	Tcl_Interp* _interpreter;
+	NSString * helperToolInterpCommand;
 	
 }
 
 
+
 /*!
  @brief Return singleton shared MPInterpreter instance
  */
@@ -170,5 +173,6 @@
 - (NSString *)getVariableAsString:(NSString *)variable;
 
 
-
+//For testing helper tool
+-(NSString *)evaluateStringWithMPHelperTool:(NSString *)statement;
 @end

Modified: branches/gsoc08-framework/MacPorts_Framework/MPInterpreter.m
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPInterpreter.m	2008-08-01 12:15:55 UTC (rev 38875)
+++ branches/gsoc08-framework/MacPorts_Framework/MPInterpreter.m	2008-08-01 14:07:22 UTC (rev 38876)
@@ -34,7 +34,17 @@
  */
 
 #import "MPInterpreter.h"
+#include "BetterAuthorizationSampleLib.h"
+#include "MPHelperCommon.h"
 
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Globals
+
+static AuthorizationRef mpAuth;
+
+#pragma mark -
+
 @implementation MPInterpreter
 
 #pragma mark Notifications Code 
@@ -138,7 +148,56 @@
 }
 
 #pragma mark -
+#pragma mark Authorization Code
+//Internal method for setting up Authorizatin Reference
+//Called during initialization of interpreter object
+//This is a bit weird: Since MPInterpreter is compiled twice First for the helper
+//tool and then for the Framework, we want to call this method only when it is
+//being compiled for the Framework ... hence we check that the main bundle name is
+//MacPorts.Framework before initializing authorization
+-(void) initializeAuthorization {
+	//I'll probably regret doing this later on
+	//but whatever .... 
+	char * username = "armahg";
+	char * password = "200485";
+	
+	OSStatus junk;
+	
+	//Create Environment specific to this machine for tesitng purposes
+	AuthorizationItem envItems[2];
+	envItems[0].name = kAuthorizationEnvironmentPassword;
+	envItems[0].value = password;
+	envItems[0].valueLength = strlen(password);
+	envItems[0].flags = 0;
+	
+	envItems[1].name = kAuthorizationEnvironmentUsername;
+	envItems[1].value = username;
+	envItems[1].valueLength = strlen(username);
+	envItems[1].flags = 0;
+	
+	AuthorizationItemSet env = { 2 , envItems };
+	
+	AuthorizationFlags envFlags;
+	envFlags = kAuthorizationFlagDefaults |
+				kAuthorizationFlagExtendRights |
+				kAuthorizationFlagPreAuthorize;
+	
+	
+	junk = AuthorizationCreate(NULL, &env, envFlags, &mpAuth);
+	assert(junk == noErr);
+	//assert (junk == noErr) == (mpAuth != NULL);
+	
+	NSString * bundleID = [[NSBundle bundleForClass:[self class]] bundleIdentifier];
+	
+	BASSetDefaultRules(mpAuth, 
+					   kMPHelperCommandSet, 
+					   (CFStringRef) bundleID, 
+					   NULL);
+	
+}
 
+
+#pragma mark -
 #pragma mark MPInterpreter Code
 
 - (id) init {
@@ -185,6 +244,12 @@
 			Tcl_DeleteInterp(_interpreter);
 		}
 		
+		//Initialize helperToolInterpCommand
+		helperToolInterpCommand = @"";
+		
+		//Initialize Authorization stuff should probably check for errors ....
+		[self initializeAuthorization];
+		
 	}
 	return self;
 }
@@ -193,6 +258,7 @@
 	return _interpreter;
 }
 
+
 + (MPInterpreter*)sharedInterpreter {
 	return [self sharedInterpreterWithPkgPath:MP_DEFAULT_PKG_PATH];
 }
@@ -351,5 +417,55 @@
 	return [NSString stringWithUTF8String:Tcl_GetVar(_interpreter, [variable UTF8String], 0)];
 }
 
-
+- (NSString *) evaluateStringWithMPHelperTool:(NSString *) statement {
+	OSStatus        err;
+    BASFailCode     failCode;
+    NSString *      bundleID;
+    NSDictionary *  request;
+    CFDictionaryRef response;
+	
+	response = NULL;
+	
+	request = [NSDictionary dictionaryWithObjectsAndKeys:
+			   @kMPHelperEvaluateTclCommand, @kBASCommandKey,
+			   statement, @kTclStringToBeEvaluated, nil];
+	assert(request != NULL);
+	
+	bundleID = [[NSBundle bundleForClass:[self class]] bundleIdentifier];
+	NSLog(@" %@ ", bundleID);
+	assert(bundleID != NULL);
+	
+	
+	err = BASExecuteRequestInHelperTool(mpAuth, 
+										kMPHelperCommandSet, 
+										(CFStringRef) bundleID, 
+										(CFDictionaryRef) request, 
+										&response);
+	
+	//Try to recover
+	if ( (err != noErr) && (err != userCanceledErr) ) {
+		failCode = BASDiagnoseFailure(mpAuth, (CFStringRef) bundleID);
+		
+		err = BASFixFailure(mpAuth, 
+							(CFStringRef) bundleID, 
+							CFSTR("MPHelperInstallTool"), 
+							CFSTR("MPHelperTool"), 
+							failCode);
+		
+		if (err == noErr) {
+			err = BASExecuteRequestInHelperTool(mpAuth, 
+												kMPHelperCommandSet, 
+												(CFStringRef) bundleID, 
+												(CFDictionaryRef) request, 
+												&response);
+		}
+	}
+	else {
+		err = userCanceledErr;
+	}
+	
+	CFStringRef result = CFDictionaryGetValue(response, CFSTR(kTclStringEvaluationResult));
+	
+	return (NSString *) result;
+}
 @end

Modified: branches/gsoc08-framework/MacPorts_Framework/MPInterpreterTest.h
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPInterpreterTest.h	2008-08-01 12:15:55 UTC (rev 38875)
+++ branches/gsoc08-framework/MacPorts_Framework/MPInterpreterTest.h	2008-08-01 14:07:22 UTC (rev 38876)
@@ -43,7 +43,7 @@
 
 - (void)testInitialization;
 - (void)testGetVariableAsArray;
-//- (void)testMPHelperTool;
+- (void)testMPHelperTool;
 //- (void)testMutableDictionaryFromTclListAsString;
 //- (void)testEvaluateStringAsString;
 

Modified: branches/gsoc08-framework/MacPorts_Framework/MPInterpreterTest.m
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPInterpreterTest.m	2008-08-01 12:15:55 UTC (rev 38875)
+++ branches/gsoc08-framework/MacPorts_Framework/MPInterpreterTest.m	2008-08-01 14:07:22 UTC (rev 38876)
@@ -58,14 +58,11 @@
 }
  
 
-/*
+
 - (void)testMPHelperTool {
 	
-	//Interesting ... we'll see who MPInterpreter belongs to
-	//at the time of execution MPHelperTool ... or MacPorts.Framework
-	[interp execute:[[NSBundle bundleForClass:MPInterpreter] pathForResource:@"MPHelperTool" ofType:nil] 
-		   withArgs:[NSArray arrayWithObjects:@"--sel-repair", @"no", @"--rec-count", 
-					 @"0", @"mportselfupdate", nil]];
+	//Here goes nothing
+	[interp evaluateStringWithMPHelperTool:@"mportsync"];
 }
 
 /*

Modified: branches/gsoc08-framework/MacPorts_Framework/MPMacPortsTest.h
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPMacPortsTest.h	2008-08-01 12:15:55 UTC (rev 38875)
+++ branches/gsoc08-framework/MacPorts_Framework/MPMacPortsTest.h	2008-08-01 14:07:22 UTC (rev 38876)
@@ -49,9 +49,9 @@
 -(void) testPathToPortIndex;
 //-(void) testDepends;
 //-(void) testSearch;
--(void) testSync;
+//-(void) testSync;
 -(void) testVersion;
 //-(void) testInstall;
--(void) testMPHelperTool;
+//-(void) testMPHelperTool;
 
 @end

Modified: branches/gsoc08-framework/MacPorts_Framework/MPMacPortsTest.m
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MPMacPortsTest.m	2008-08-01 12:15:55 UTC (rev 38875)
+++ branches/gsoc08-framework/MacPorts_Framework/MPMacPortsTest.m	2008-08-01 14:07:22 UTC (rev 38876)
@@ -74,7 +74,7 @@
 	STAssertNotNil(searchResults, @"This dictionary should have at least %d key value pairs", [searchResults count]);
 }
 
-
+/*
 -(void) testSync {
 	NSError * syncError = nil;
 	[testPort sync:&syncError];
@@ -89,7 +89,7 @@
 	
 }
 
-/*
+
 -(void) testSelfupdate {
 	//The only way to test this that I know of is to listen for the posted notifications
 	//and take actions as appropriate
@@ -104,22 +104,6 @@
 }
 
 
--(void) testMPHelperTool {
-	NSLog(@"Testing MPHelperTool");
-	NSTask *task = [[NSTask alloc] init];
-	[task setLaunchPath:[[NSBundle bundleForClass:[MPMacPorts class]] 
-						 pathForResource:@"MPHelperTool" 
-						 ofType:nil]];
-	
-	NSArray * args = [NSArray arrayWithObjects:SELF_REPAIR, @"no",
-					  REC_COUNT, @"0", @"return [macports::version]", nil];
-	[task setArguments:args];
-	
-	[task launch];
-	
-	[task release];
-	
-}
 
 
 /*

Modified: branches/gsoc08-framework/MacPorts_Framework/MacPorts.Framework.xcodeproj/project.pbxproj
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework/MacPorts.Framework.xcodeproj/project.pbxproj	2008-08-01 12:15:55 UTC (rev 38875)
+++ branches/gsoc08-framework/MacPorts_Framework/MacPorts.Framework.xcodeproj/project.pbxproj	2008-08-01 14:07:22 UTC (rev 38876)
@@ -44,19 +44,27 @@
 		6E49F37B0DFFAB0B0030C3AF /* MPInterpreterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 489DD92E0C94674B00595506 /* MPInterpreterTest.m */; };
 		6E49F37F0DFFAFF80030C3AF /* MacPorts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* MacPorts.framework */; };
 		6EA294590E080DEB00902D12 /* MPMacPortsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E88D1CB0DF4B90B00684E9F /* MPMacPortsTest.m */; };
+		6EB6F7C40E432A840057962C /* MPHelperTool in CopyFiles */ = {isa = PBXBuildFile; fileRef = 6ED12A4A0E3E552F0026773D /* MPHelperTool */; };
+		6EC260730E426FC80013BC48 /* BetterAuthorizationSampleLib.c in Sources */ = {isa = PBXBuildFile; fileRef = 6EC260700E426FC80013BC48 /* BetterAuthorizationSampleLib.c */; };
+		6EC260740E426FC80013BC48 /* BetterAuthorizationSampleLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EC260710E426FC80013BC48 /* BetterAuthorizationSampleLib.h */; };
+		6EC260760E426FC80013BC48 /* BetterAuthorizationSampleLib.c in Sources */ = {isa = PBXBuildFile; fileRef = 6EC260700E426FC80013BC48 /* BetterAuthorizationSampleLib.c */; };
+		6EC260770E426FC80013BC48 /* BetterAuthorizationSampleLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EC260710E426FC80013BC48 /* BetterAuthorizationSampleLib.h */; };
+		6EC260970E4272D20013BC48 /* MPHelperCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EC260950E4272D20013BC48 /* MPHelperCommon.h */; };
+		6EC260980E4272D20013BC48 /* MPHelperCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 6EC260960E4272D20013BC48 /* MPHelperCommon.c */; };
+		6EC260990E4272D20013BC48 /* MPHelperCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EC260950E4272D20013BC48 /* MPHelperCommon.h */; };
+		6EC2609A0E4272D20013BC48 /* MPHelperCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 6EC260960E4272D20013BC48 /* MPHelperCommon.c */; };
+		6EC2609B0E4273920013BC48 /* BetterAuthorizationSampleLibInstallTool.c in Sources */ = {isa = PBXBuildFile; fileRef = 6EC260720E426FC80013BC48 /* BetterAuthorizationSampleLibInstallTool.c */; };
+		6EC2609E0E42950C0013BC48 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EC2609D0E42950C0013BC48 /* CoreFoundation.framework */; };
+		6EC2614F0E42959B0013BC48 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ED12AA60E3E7E900026773D /* Cocoa.framework */; };
 		6ED12A4F0E3E55660026773D /* MPHelperTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 6ED12A4E0E3E55660026773D /* MPHelperTool.m */; };
 		6ED12A550E3E55DF0026773D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ED12A540E3E55DF0026773D /* Security.framework */; };
 		6ED12A560E3E55DF0026773D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ED12A540E3E55DF0026773D /* Security.framework */; };
-		6ED12A5A0E3E56420026773D /* MPHelperTool in Resources */ = {isa = PBXBuildFile; fileRef = 6ED12A4A0E3E552F0026773D /* MPHelperTool */; };
-		6ED12AA30E3E7E7C0026773D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ED12AA20E3E7E7C0026773D /* Foundation.framework */; };
-		6ED12AA70E3E7E900026773D /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ED12AA60E3E7E900026773D /* Cocoa.framework */; };
 		6ED12AC90E3E94310026773D /* MPInterpreter.m in Sources */ = {isa = PBXBuildFile; fileRef = 48906AFC0C4230B700A3ED8A /* MPInterpreter.m */; };
 		6ED12AD20E3E9AC30026773D /* MPInterpreter.h in Headers */ = {isa = PBXBuildFile; fileRef = 48906AFB0C4230B700A3ED8A /* MPInterpreter.h */; };
 		6ED12AF10E3E9E210026773D /* Tcl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EA0F56E0DFEB55E00C15082 /* Tcl.framework */; };
 		6ED12AFB0E3E9F980026773D /* MPNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E270D070E158CED00BAE687 /* MPNotifications.h */; };
 		6ED12AFC0E3E9FA60026773D /* MPNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E270D080E158CED00BAE687 /* MPNotifications.m */; };
 		6ED12B060E3EA9E30026773D /* init.tcl in Resources */ = {isa = PBXBuildFile; fileRef = 48E9939E0C82CEB000219DDF /* init.tcl */; };
-		6ED12C9E0E40C3320026773D /* template.c in Resources */ = {isa = PBXBuildFile; fileRef = 6ED12C9D0E40C3320026773D /* template.c */; };
 		8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };
 		8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
 /* End PBXBuildFile section */
@@ -69,15 +77,35 @@
 			remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
 			remoteInfo = MacPorts;
 		};
+		6EC2608B0E4270110013BC48 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 6EC260860E426FF10013BC48;
+			remoteInfo = MPHelperInstallTool;
+		};
 		6ED12A520E3E55A50026773D /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
 			proxyType = 1;
-			remoteGlobalIDString = 6ED12A490E3E552F0026773D /* MPHelperTool */;
+			remoteGlobalIDString = 6ED12A490E3E552F0026773D;
 			remoteInfo = MPHelperTool;
 		};
 /* End PBXContainerItemProxy section */
 
+/* Begin PBXCopyFilesBuildPhase section */
+		6EB6F7B00E4326F40057962C /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 6;
+			files = (
+				6EB6F7C40E432A840057962C /* MPHelperTool in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
 /* Begin PBXFileReference section */
 		0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
 		089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -114,13 +142,17 @@
 		6E88D1CB0DF4B90B00684E9F /* MPMacPortsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacPortsTest.m; sourceTree = "<group>"; };
 		6EA0F56E0DFEB55E00C15082 /* Tcl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Tcl.framework; path = System/Library/Frameworks/Tcl.framework; sourceTree = SDKROOT; };
 		6EAFD8B70DEC614E00E97270 /* dummycommit.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dummycommit.test; sourceTree = "<group>"; };
+		6EC260700E426FC80013BC48 /* BetterAuthorizationSampleLib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BetterAuthorizationSampleLib.c; sourceTree = "<group>"; };
+		6EC260710E426FC80013BC48 /* BetterAuthorizationSampleLib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BetterAuthorizationSampleLib.h; sourceTree = "<group>"; };
+		6EC260720E426FC80013BC48 /* BetterAuthorizationSampleLibInstallTool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BetterAuthorizationSampleLibInstallTool.c; sourceTree = "<group>"; };
+		6EC260870E426FF10013BC48 /* MPHelperInstallTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MPHelperInstallTool; sourceTree = BUILT_PRODUCTS_DIR; };
+		6EC260950E4272D20013BC48 /* MPHelperCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPHelperCommon.h; sourceTree = "<group>"; };
+		6EC260960E4272D20013BC48 /* MPHelperCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MPHelperCommon.c; sourceTree = "<group>"; };
+		6EC2609D0E42950C0013BC48 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
 		6ED12A4A0E3E552F0026773D /* MPHelperTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MPHelperTool; sourceTree = BUILT_PRODUCTS_DIR; };
 		6ED12A4E0E3E55660026773D /* MPHelperTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPHelperTool.m; sourceTree = "<group>"; };
 		6ED12A540E3E55DF0026773D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
-		6ED12AA20E3E7E7C0026773D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
 		6ED12AA60E3E7E900026773D /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
-		6ED12C2D0E405E660026773D /* finkLauncher.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = finkLauncher.c; sourceTree = "<group>"; };
-		6ED12C9D0E40C3320026773D /* template.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = template.c; sourceTree = "<group>"; };
 		8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
 		8DC2EF5B0486A6940098B216 /* MacPorts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MacPorts.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
@@ -134,14 +166,21 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		6EC260850E426FF10013BC48 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		6ED12A480E3E552F0026773D /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				6ED12A550E3E55DF0026773D /* Security.framework in Frameworks */,
-				6ED12AA30E3E7E7C0026773D /* Foundation.framework in Frameworks */,
-				6ED12AA70E3E7E900026773D /* Cocoa.framework in Frameworks */,
 				6ED12AF10E3E9E210026773D /* Tcl.framework in Frameworks */,
+				6EC2609E0E42950C0013BC48 /* CoreFoundation.framework in Frameworks */,
+				6EC2614F0E42959B0013BC48 /* Cocoa.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -164,6 +203,7 @@
 				8DC2EF5B0486A6940098B216 /* MacPorts.framework */,
 				489DD8F40C94365F00595506 /* Test.octest */,
 				6ED12A4A0E3E552F0026773D /* MPHelperTool */,
+				6EC260870E426FF10013BC48 /* MPHelperInstallTool */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -181,8 +221,8 @@
 				0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */,
 				034768DFFF38A50411DB9C8B /* Products */,
 				6EA0F56E0DFEB55E00C15082 /* Tcl.framework */,
-				6ED12AA20E3E7E7C0026773D /* Foundation.framework */,
 				6ED12AA60E3E7E900026773D /* Cocoa.framework */,
+				6EC2609D0E42950C0013BC48 /* CoreFoundation.framework */,
 			);
 			name = "MacPorts Foundation";
 			sourceTree = "<group>";
@@ -290,9 +330,12 @@
 		6ED12A460E3E54A80026773D /* MPHelperTool */ = {
 			isa = PBXGroup;
 			children = (
+				6EC260700E426FC80013BC48 /* BetterAuthorizationSampleLib.c */,
+				6EC260710E426FC80013BC48 /* BetterAuthorizationSampleLib.h */,
+				6EC260720E426FC80013BC48 /* BetterAuthorizationSampleLibInstallTool.c */,
 				6ED12A4E0E3E55660026773D /* MPHelperTool.m */,
-				6ED12C2D0E405E660026773D /* finkLauncher.c */,
-				6ED12C9D0E40C3320026773D /* template.c */,
+				6EC260950E4272D20013BC48 /* MPHelperCommon.h */,
+				6EC260960E4272D20013BC48 /* MPHelperCommon.c */,
 			);
 			name = MPHelperTool;
 			sourceTree = "<group>";
@@ -306,6 +349,8 @@
 			files = (
 				6ED12AFB0E3E9F980026773D /* MPNotifications.h in Headers */,
 				6ED12AD20E3E9AC30026773D /* MPInterpreter.h in Headers */,
+				6EC260740E426FC80013BC48 /* BetterAuthorizationSampleLib.h in Headers */,
+				6EC260970E4272D20013BC48 /* MPHelperCommon.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -322,6 +367,8 @@
 				48A866AA0CD364F700B521BC /* MPReceipt.h in Headers */,
 				481D04A20CDAAAFD00D4A550 /* MPMutableDictionary.h in Headers */,
 				6E270D090E158CED00BAE687 /* MPNotifications.h in Headers */,
+				6EC260770E426FC80013BC48 /* BetterAuthorizationSampleLib.h in Headers */,
+				6EC260990E4272D20013BC48 /* MPHelperCommon.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -347,6 +394,22 @@
 			productReference = 489DD8F40C94365F00595506 /* Test.octest */;
 			productType = "com.apple.product-type.bundle";
 		};
+		6EC260860E426FF10013BC48 /* MPHelperInstallTool */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 6EC260940E4270420013BC48 /* Build configuration list for PBXNativeTarget "MPHelperInstallTool" */;
+			buildPhases = (
+				6EC260840E426FF10013BC48 /* Sources */,
+				6EC260850E426FF10013BC48 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = MPHelperInstallTool;
+			productName = MPHelperInstallTool;
+			productReference = 6EC260870E426FF10013BC48 /* MPHelperInstallTool */;
+			productType = "com.apple.product-type.tool";
+		};
 		6ED12A490E3E552F0026773D /* MPHelperTool */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = 6ED12A500E3E55660026773D /* Build configuration list for PBXNativeTarget "MPHelperTool" */;
@@ -374,11 +437,13 @@
 				8DC2EF540486A6940098B216 /* Sources */,
 				8DC2EF560486A6940098B216 /* Frameworks */,
 				6E49F4F40E00DD520030C3AF /* ShellScript */,
+				6EB6F7B00E4326F40057962C /* CopyFiles */,
 			);
 			buildRules = (
 			);
 			dependencies = (
 				6ED12A530E3E55A50026773D /* PBXTargetDependency */,
+				6EC2608C0E4270110013BC48 /* PBXTargetDependency */,
 			);
 			name = MacPorts;
 			productInstallPath = "$(HOME)/Library/Frameworks";
@@ -402,6 +467,7 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
+				6EC260860E426FF10013BC48 /* MPHelperInstallTool */,
 				6ED12A490E3E552F0026773D /* MPHelperTool */,
 				8DC2EF4F0486A6940098B216 /* MacPorts */,
 				489DD8F30C94365F00595506 /* Test */,
@@ -423,7 +489,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				6ED12B060E3EA9E30026773D /* init.tcl in Resources */,
-				6ED12C9E0E40C3320026773D /* template.c in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -431,7 +496,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				6ED12A5A0E3E56420026773D /* MPHelperTool in Resources */,
 				8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */,
 				48E9939F0C82CEB000219DDF /* init.tcl in Resources */,
 				6E44A00D0E2DAD66007DE8EC /* ToDo.txt in Resources */,
@@ -492,6 +556,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		6EC260840E426FF10013BC48 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				6EC2609B0E4273920013BC48 /* BetterAuthorizationSampleLibInstallTool.c in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		6ED12A470E3E552F0026773D /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -499,6 +571,8 @@
 				6ED12AC90E3E94310026773D /* MPInterpreter.m in Sources */,
 				6ED12AFC0E3E9FA60026773D /* MPNotifications.m in Sources */,
 				6ED12A4F0E3E55660026773D /* MPHelperTool.m in Sources */,
+				6EC260730E426FC80013BC48 /* BetterAuthorizationSampleLib.c in Sources */,
+				6EC260980E4272D20013BC48 /* MPHelperCommon.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -514,6 +588,8 @@
 				481D04A30CDAAAFD00D4A550 /* MPMutableDictionary.m in Sources */,
 				4825ECC40CE61468006B0385 /* MPRegistry.m in Sources */,
 				6E270D0A0E158CED00BAE687 /* MPNotifications.m in Sources */,
+				6EC260760E426FC80013BC48 /* BetterAuthorizationSampleLib.c in Sources */,
+				6EC2609A0E4272D20013BC48 /* MPHelperCommon.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -525,6 +601,11 @@
 			target = 8DC2EF4F0486A6940098B216 /* MacPorts */;
 			targetProxy = 6E1AE8460E232D0700F6D7BC /* PBXContainerItemProxy */;
 		};
+		6EC2608C0E4270110013BC48 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 6EC260860E426FF10013BC48 /* MPHelperInstallTool */;
+			targetProxy = 6EC2608B0E4270110013BC48 /* PBXContainerItemProxy */;
+		};
 		6ED12A530E3E55A50026773D /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = 6ED12A490E3E552F0026773D /* MPHelperTool */;
@@ -689,6 +770,36 @@
 			};
 			name = Release;
 		};
+		6EC260890E426FF30013BC48 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_ENABLE_FIX_AND_CONTINUE = YES;
+				GCC_MODEL_TUNING = G5;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				INSTALL_PATH = /usr/local/bin;
+				PREBINDING = NO;
+				PRODUCT_NAME = MPHelperInstallTool;
+			};
+			name = Debug;
+		};
+		6EC2608A0E426FF30013BC48 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_ENABLE_FIX_AND_CONTINUE = NO;
+				GCC_MODEL_TUNING = G5;
+				INSTALL_PATH = /usr/local/bin;
+				PREBINDING = NO;
+				PRODUCT_NAME = MPHelperInstallTool;
+				ZERO_LINK = NO;
+			};
+			name = Release;
+		};
 		6ED12A4C0E3E55300026773D /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -770,6 +881,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		6EC260940E4270420013BC48 /* Build configuration list for PBXNativeTarget "MPHelperInstallTool" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				6EC260890E426FF30013BC48 /* Debug */,
+				6EC2608A0E426FF30013BC48 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		6ED12A500E3E55660026773D /* Build configuration list for PBXNativeTarget "MPHelperTool" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20080801/d7354d97/attachment-0001.html 


More information about the macports-changes mailing list