[39568] branches/gsoc08-framework/MacPorts_Framework_Release

armahg at macports.org armahg at macports.org
Sun Aug 24 22:36:55 PDT 2008


Revision: 39568
          http://trac.macosforge.org/projects/macports/changeset/39568
Author:   armahg at macports.org
Date:     2008-08-24 22:36:55 -0700 (Sun, 24 Aug 2008)
Log Message:
-----------
Added IPCAdditions category to MPNotifications class. This is a cleaner way to deal with incorporating additional IPC code into MPNotifications. Works with Test bundle

Modified Paths:
--------------
    branches/gsoc08-framework/MacPorts_Framework_Release/MPInterpreter.m
    branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.h
    branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.m
    branches/gsoc08-framework/MacPorts_Framework_Release/MacPorts.Framework.xcodeproj/project.pbxproj

Added Paths:
-----------
    branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.h
    branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.m

Modified: branches/gsoc08-framework/MacPorts_Framework_Release/MPInterpreter.m
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework_Release/MPInterpreter.m	2008-08-25 05:10:18 UTC (rev 39567)
+++ branches/gsoc08-framework/MacPorts_Framework_Release/MPInterpreter.m	2008-08-25 05:36:55 UTC (rev 39568)
@@ -386,26 +386,26 @@
 		//I hope BAS's main method is blocking ... it should be since we obtain
 		//a return value
 		MPNotifications * notificationObject = [MPNotifications sharedListener];
-		if ([notificationObject respondsToSelector:@selector(prepareServerThread)]) {
+		//if ([notificationObject respondsToSelector:@selector(prepareIPCServerThread)]) {
 			NSLog(@"PREPARING SERVER THREAD");
-			[notificationObject prepareServerThread];
-		}
+			[notificationObject prepareIPCServerThread];
+		//}
 		
-		if ([notificationObject respondsToSelector:@selector(startServerThread)]) {
+		//if ([notificationObject respondsToSelector:@selector(startServerThread)]) {
 			NSThread * cThread = [NSThread currentThread];
 			NSLog(@"STARTING SERVER THREAD with previous thread %@", [cThread threadDictionary]);
-		[NSThread detachNewThreadSelector:@selector(startServerThread) 
+		[NSThread detachNewThreadSelector:@selector(startIPCServerThread) 
 								 toTarget:notificationObject 
 							   withObject:nil];
 		
-		}
+		//}
 		secondResult = [self evaluateStringWithMPHelperTool:statement error:mportError];
 		
 		//We can stop the thread now
-		if ([notificationObject respondsToSelector:@selector(stopServerThread)]) {
+		//if ([notificationObject respondsToSelector:@selector(stopServerThread)]) {
 			NSLog(@"STOPPING SERVER THREAD");
-			[notificationObject stopServerThread];
-		}
+			[notificationObject stopIPCServerThread];
+		//}
 		return secondResult;
 	}
 	

Added: branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.h
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.h	                        (rev 0)
+++ branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.h	2008-08-25 05:36:55 UTC (rev 39568)
@@ -0,0 +1,18 @@
+//
+//  MPNotifications+IPCAdditions.h
+//  MacPorts.Framework
+//
+//  Created by George  Armah on 8/24/08.
+//  Copyright 2008 Lafayette College. All rights reserved.
+//
+
+#import "MPNotifications.h"
+
+
+ at interface MPNotifications (IPCAdditions) 
+
+-(void) startIPCServerThread;
+-(void) prepareIPCServerThread;
+-(void) stopIPCServerThread;
+
+ at end


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

Added: branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.m
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.m	                        (rev 0)
+++ branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.m	2008-08-25 05:36:55 UTC (rev 39568)
@@ -0,0 +1,1452 @@
+//
+//  MPNotifications+IPCAdditions.m
+//  MacPorts.Framework
+//
+//  Created by George  Armah on 8/24/08.
+//  Copyright 2008 Lafayette College. All rights reserved.
+//
+
+
+
+#pragma mark MPNotifications Server Code
+//Things will get ugly ... before they can get beautiful ...
+/*
+ File:       Server.c
+ 
+ Contains:   Server showing integration of CFSockets and UNIX domain sockets.
+ 
+ Written by: DTS
+ 
+ Copyright:  Copyright (c) 2005 by Apple Computer, Inc., All Rights Reserved.
+ 
+ Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, 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 Computer, 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.
+ 
+ Change History (most recent first):
+ 
+ $Log: Server.c,v $
+ Revision 1.2  2005/05/18 13:36:39         
+ Fixed various documentation/comment changes.
+ 
+ Revision 1.1  2005/05/17 12:19:32         
+ First checked in.
+ 
+ 
+ */
+
+/////////////////////////////////////////////////////////////////
+
+// System interfaces
+
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+
+#include "MPHelperNotificationsCommon.h"
+#include "MPHelperNotificationsProtocol.h"
+#import "MPNotifications+IPCAdditions.h"
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Client State Management
+
+// The server maintains a ClientState structure to track the state of 
+// each client.  This state divides neatly into three groups.
+//
+// o socket -- fSockFD, fSockCF, and fRunLoopSource all represent different 
+//   aspects of the UNIX domain socket that we're using to talk to the client.
+//
+// o incoming -- fBufferedData is a buffer containing any incomplete packets that 
+//   we've received from the client.
+//
+// o outgoing -- fPendingSends and fPendingSendOffset control the packets that 
+//   are waiting to be sent to the client.  This list might get long if the 
+//   client stops listening to us.  Packets will first back up in the UNIX 
+//   domain socket's socket buffer.  Once that fills up we won't be able to 
+//   write to the socket anymore.  We respond to that by buffering the 
+//   packets on the fPendingSends list.  We also tell CFSocket to let us know 
+//   (with a kCFSocketWriteCallBack event) if space becomes available.
+//
+//   At this point one of two things will happen.  Either we'll buffer too 
+//   many packets for the client (kClientMaximumPendingSends) in which case 
+//   we'll kill the client.  Or the client will start receiving packets again, 
+//   which will start to empty the socket buffer.  CFSocket will tells about 
+//   this by sending us a kCFSocketWriteCallBack event, and we'll start 
+//   pulling packets off the fPendingSends list and writing them to the socket.
+
+enum {
+    kClientStateMagic = 'LSCM'               // for Local Server Client Magic
+};
+
+struct ClientState {
+    OSType              fMagic;             // kClientStateMagic
+    int                 fSockFD;            // UNIX domain socket to client
+    CFSocketRef         fSockCF;            // CFSocket wrapper for the above
+    CFRunLoopSourceRef  fRunLoopSource;     // runloop source for the above
+    CFMutableDataRef    fBufferedData;      // buffers data for incomplete incoming packets
+    CFMutableArrayRef   fPendingSends;      // list of packets waiting to be sent
+    size_t              fPendingSendOffset; // offset of next byte to send in first packet on list
+    Boolean             fListening;         // true if this client is a listener
+};
+typedef struct ClientState ClientState;
+
+// To prevent a deaf client from sucking down all of our memory, we limit the 
+// number of packets that we'll buffer for a given client.  If the length of 
+// fPendingSends exceeds kClientMaximumPendingSends, we'll kill the client rather 
+// than queue more data.
+
+enum {
+    kClientMaximumPendingSends = 100
+};
+
+// gClients is a set of all clients we know about.
+
+static CFMutableSetRef gClients = NULL;		// of (ClientState *)
+
+#pragma mark Misc
+
+static Boolean ClientCheckPacketSize(ClientState *client, const PacketHeader *packet, size_t requiredSize)
+// Checks that a packet that has arrived from a client is of the 
+// appropriate size.  Returns false, and prints a message, if it isn't.
+{
+    Boolean result;
+    
+    assert(client != NULL);
+    assert(packet != NULL);
+    assert(requiredSize >= sizeof(PacketHeader));
+    
+    result = true;
+    if (packet->fSize != requiredSize) {
+        fprintf(
+				stderr, 
+				"ClientCheckPacketSize: Client %p sent us a '%.4s' of the wrong size (got %" PRIu32 ", wanted %zu).\n", 
+				client, 
+				(char *) &packet->fType, 
+				packet->fSize, 
+				requiredSize
+				);
+        result = false;
+    }
+    return result;
+}
+
+static Boolean ClientCheckPacketID(ClientState *client, const PacketHeader *packet, int32_t requiredID)
+// Checks that a packet that has arrived from a client has the 
+// correct ID.  Returns false, and prints a message, if it doesn't.
+{
+    Boolean result;
+	
+    assert(client != NULL);
+    assert(packet != NULL);
+    
+    result = true;
+    if (packet->fID != requiredID) {
+        fprintf(
+				stderr, 
+				"ClientCheckPacketID: Client %p sent us a '%.4s' with the wrong ID (got %" PRId32 ", wanted %" PRId32 ").\n", 
+				client, 
+				(char *) &packet->fType, 
+				packet->fID, 
+				requiredID
+				);
+        result = false;
+    }
+    return result;
+}
+
+// Forward declarations
+
+static void ClientGotSpace(ClientState *client);
+static void ClientGotData(ClientState *client, const void *data);
+
+static void ClientEvent(
+						CFSocketRef             s, 
+						CFSocketCallBackType    type, 
+						CFDataRef               address, 
+						const void *            data, 
+						void *                  info
+)
+// This is the CFSocket event callback for client sockets.  For a description 
+// of the parameters, see the CFSocket documentation.
+//
+// This routine responds to two events, kCFSocketDataCallBack and 
+// kCFSocketWriteCallBack, dispatching them to ClientGotData and 
+// ClientGotSpace, respectively.
+{
+#pragma unused(address)
+	ClientState *	client;
+	
+    assert(s != NULL);
+	
+    client = (ClientState *) info;
+    assert(client != NULL);
+    assert(client->fMagic == kClientStateMagic);
+    
+    switch (type) {
+        case kCFSocketDataCallBack:
+            ClientGotData(client, data);
+            break;
+        case kCFSocketWriteCallBack:
+            ClientGotSpace(client);
+            break;
+        default:
+            assert(false);
+            break;
+    }
+}
+
+#pragma mark Create/Destroy
+
+static void ClientDestroy(ClientState *client);
+
+static int ClientInitialise(void)
+// Initialises the client management layer, which simply involves 
+// creating an empty gClients set.
+{
+	int err;
+	
+	err = 0;
+	gClients = CFSetCreateMutable(NULL, 0, NULL);
+	if (gClients == NULL) {
+		err = ENOMEM;
+	}
+	return err;
+}
+
+static void ClientTerminate(void)
+// Shuts down the client management layer.  This involves destroying 
+// any remaining clients and disposing of gClients.
+{
+	CFIndex             clientCount;
+	CFIndex             clientIndex;
+	ClientState **      allClients;
+	
+    if (gClients != NULL) {
+		// Can't use CFSetApplyFunction because the ClientDestroy modifies 
+		// the gClients set.
+		
+		clientCount = CFSetGetCount(gClients);
+		
+		allClients = calloc(clientCount, sizeof(ClientState *));
+		if (allClients == NULL) {
+			fprintf(stderr, "CFLocalServer: Could not clean up clients because we couldn't allocate memory.\n");
+		} else {
+			CFSetGetValues(gClients, (const void **) allClients);
+			
+			for (clientIndex = 0; clientIndex < clientCount; clientIndex++) {
+				fprintf(stderr, "CFLocalServer: Client %p killed because we're quitting.\n", allClients[clientIndex]);
+				
+				ClientDestroy( allClients[clientIndex] );
+			}
+		}
+		
+		free(allClients);
+		
+		CFRelease(gClients);
+		gClients = NULL;
+    }
+}
+
+static int ClientCreate(int clientSockFD, ClientState **clientPtr)
+// Creates a new client that communicates over clientSockFD. 
+// If clientPtr is not NULL, it returns a pointer to the 
+// client state record in *clientPtr.
+//
+// clientSockFD must be a valid file descriptor referencing a 
+// socket that's connected to the client
+// On input, if clientPtr is not NULL, *clientPtr must be NULL
+// Returns an errno-style error code
+// On success, if clientPtr is not NULL, *clientPtr will not be NULL
+// On success, clientSockFD is owned by the new client; the caller 
+// need not close it
+// On error, if clientPtr is not NULL, *clientPtr will be NULL
+// On error, clientSockFD will have been closed.
+//
+// IMPORTANT:
+// Regardless of whether this routine succeeds or fails, it assumes 
+// responsibility for clientSockFD.  The caller is never required to 
+// close it.
+{
+    int             err;
+    int             junk;
+    ClientState *   client;
+    
+    assert( (clientPtr == NULL) || (*clientPtr == NULL) );
+    
+    assert(gClients != NULL);
+    
+    // Create the client state record.
+    
+    err = 0;
+    client = (ClientState *) calloc(1, sizeof(*client));
+    if (client == NULL) {
+        err = ENOMEM;
+    }
+	
+    // Fill in the easy fields.  This also prepares us for the clean up 
+    // on failure.
+    
+    if (err == 0) {
+        client->fMagic = kClientStateMagic;
+		
+        // For clean up to work properly, we must make sure that, if 
+        // the connection record is allocated successfully, we always 
+        // set fSockFD to the incoming clientSockFD.
+		
+        client->fSockFD = clientSockFD;
+        
+        client->fBufferedData = CFDataCreateMutable(NULL, 0);
+        client->fPendingSends = CFArrayCreateMutable(NULL, 0, NULL);
+		
+        if ( (client->fBufferedData == NULL) || (client->fPendingSends == NULL) ) {
+            err = ENOMEM;
+        }
+    }
+    
+    // Make the socket non-blocking.  We need to do this because 
+    // otherwise ClientSendPending can get stuck in a write.
+    
+    if (err == 0) {
+        err = MoreUNIXSetNonBlocking(client->fSockFD);
+    }
+    
+    // Wrap the socket in a CFSocket, and create and install the run loop source.
+    
+    if (err == 0) {
+        CFSocketContext context;
+        
+        memset(&context, 0, sizeof(context));
+        context.info = client;
+        
+        client->fSockCF = CFSocketCreateWithNative(
+												   NULL, 
+												   (CFSocketNativeHandle) client->fSockFD, 
+												   kCFSocketDataCallBack + kCFSocketWriteCallBack, 
+												   ClientEvent, 
+												   &context
+												   );
+        if (client->fSockCF == NULL) {
+            err = EINVAL;
+        }
+    }
+	
+    if (err == 0) {
+        client->fRunLoopSource = CFSocketCreateRunLoopSource(NULL, client->fSockCF, 0);
+        if (client->fRunLoopSource == NULL) {
+            err = EINVAL;
+        }
+    }
+    if (err == 0) {
+        CFRunLoopAddSource( CFRunLoopGetCurrent(), client->fRunLoopSource, kCFRunLoopDefaultMode);
+        
+        assert( ! CFSetContainsValue(gClients, client) );
+        
+        // It's all good.  Record that this client exists.
+        
+        CFSetAddValue(gClients, client);
+    }
+    
+    // Clean up.
+    
+    if (err != 0) {
+        fprintf(stderr, "ClientCreate: Error %d creating client.\n", err);
+		
+        // If client is NULL, we couldn't allocate a client record, therefore 
+        // we had nowhere to record clientSockFD, therefore ClientDestroy won't 
+        // clean it up.  Thus, we have to do it ourselves.
+        
+        if (client == NULL) {
+            junk = close(clientSockFD);
+            assert(junk == 0);
+        } else {
+            ClientDestroy(client);
+        }
+        client = NULL;
+    }
+    if (clientPtr != NULL) {
+        *clientPtr = client;
+    }
+    
+    assert( (clientPtr == NULL) || ((err == 0) == (*clientPtr != NULL)) );
+	
+    return err;
+}
+
+static void ClientDestroy(ClientState *client)
+// Destroys a client.  This is called in a number of different circumstances, 
+// but these basically boil down to:
+// 
+// a) if ClientCreate fails, it's called to destroy the partially-created client, 
+// b) if some sort of communications error happens, it's called to destroy the 
+//    client,
+// c) if the client sends us a goodbye packet, this is called to destroy the client, and 
+// d) on quit, all clients are destroyed.
+{
+    int     junk;
+    
+    assert(client != NULL);
+    assert(client->fMagic == kClientStateMagic);
+	
+    // This following assert is NOT true.  If the client dies before it 
+    // gets fully started (that is, we get an error halfway through 
+    // ClientCreate), ClientDestroy is called to tidy up the mess but 
+    // the client hasn't been added into gClients yet.
+    
+    // assert( CFSetContainsValue(gClients, client) );
+    
+    // Remove the client our record of existant clients.
+    
+    CFSetRemoveValue(gClients, client);
+    
+    // Clean up the runloop source and CFSocket.
+    
+    if (client->fRunLoopSource != NULL) {
+        CFRunLoopSourceInvalidate(client->fRunLoopSource);
+        
+        CFRelease(client->fRunLoopSource);
+    }
+    if (client->fSockCF != NULL) {
+        CFSocketInvalidate(client->fSockCF);
+        
+        CFRelease(client->fSockCF);
+    }
+    
+    // Close the socket itself, but only if we don't have a corresponding 
+    // CFSocket; if a CFSocket was created, it takes over responsibility 
+    // for closing the socket.
+    
+    if ( (client->fSockFD != -1) && (client->fSockCF == NULL) ) {
+        junk = close(client->fSockFD);
+        assert(junk == 0);
+    }
+    
+    // Free any packets waiting to go out to this client.
+    
+    if (client->fPendingSends != NULL) {
+        CFIndex index;
+        CFIndex count;
+		
+        count = CFArrayGetCount(client->fPendingSends);
+        for (index = 0; index < count; index++) {
+            free( (void *) CFArrayGetValueAtIndex(client->fPendingSends, index) );
+        }
+        CFRelease(client->fPendingSends);
+    }
+    
+    // Free any buffered data from this client.
+    
+    if (client->fBufferedData != NULL) {
+        CFRelease(client->fBufferedData);
+    }
+    
+    // Free the client state record itself.
+    
+    client->fMagic = 'FRE!';
+    free(client);
+}
+
+#pragma mark Send
+
+static int ClientSendPending(ClientState *client)
+// This routine attempts to send any packets that are queued in the fSendPending 
+// array.  It is somewhat complex.  There are three possible final results.
+//
+// o It successfully sends all packets in the queue.  In this case it returns 0.
+//
+// o The write side of the socket is full (flow controlled).  In this case the 
+//   function enables the socket write callback (kCFSocketWriteCallBack, using 
+//   CFSocketEnableCallBacks) and returns 0.  When socket buffer empties a little, 
+//   CFSocket will send us the kCFSocketWriteCallBack event and we'll resume sending.
+//
+// o It fails for some other reasons (for example, the other end of the socket has 
+//   been closed, causing an EPIPE).  In this case it returns an errno-style error 
+//   indicating the failure.  The caller typically responds by destroying the client.
+//
+// This whole process is further complicated by the possibilty that the socket 
+// buffer might have enough space for half a packet.  In this case you'll get a 
+// short write, that is, write will return a positive number less than its nbytes 
+// parameter.  To handle this case we record the offset into the packet of the first 
+// byte of unwritten data.  When we go to send a packet, we always send from there. 
+// When write accepts some data, we bump the offset by that amount.  If that 
+// completes the send of the packet, we start on next packet, resetting the offset 
+// back to 0.
+{
+    int                     err;
+    Boolean                 done;
+    const PacketHeader *    thisPacket;
+    ssize_t                 bytesWritten;
+    
+    err = 0;
+    
+    // Keep going until we've sent all pending packets for this client.
+    
+    while ( (err == 0) && (CFArrayGetCount(client->fPendingSends) != 0) ) {
+        thisPacket = (const PacketHeader *) CFArrayGetValueAtIndex(client->fPendingSends, 0);
+        
+        // Try to send this packet by writing it to the socket.
+        
+        done = false;
+        do {
+            bytesWritten = write(
+								 client->fSockFD, 
+								 ((char *) thisPacket) + client->fPendingSendOffset, 
+								 thisPacket->fSize - client->fPendingSendOffset
+								 );
+            
+            if (bytesWritten > 0) {
+                // We're written some bytes.  Adjust fPendingSendOffset by 
+                // that amount and see if that completes the packet.
+                
+                client->fPendingSendOffset += bytesWritten;
+                
+                if (client->fPendingSendOffset == thisPacket->fSize) {
+                    // Packet complete.  Delete it from the head of the 
+                    // send list, reset offset back to 0, and let's go 
+                    // deal with the next packet.
+                    
+                    CFArrayRemoveValueAtIndex(client->fPendingSends, 0);
+                    free( (void *) thisPacket);
+                    client->fPendingSendOffset = 0;
+                    done = true;
+                } else {
+                    // Packet still not fully sent.  The send offset has already 
+                    // been updated, so we just loop to try again.
+                }
+            } else if (bytesWritten == -1) {
+                // We got some sort of error.
+                
+                err = errno;
+                switch (err) {
+                    case EINTR:
+                        // Interrupted.  Do nothing, so we loop and retry this 
+                        // send immediately.
+						
+                        err = 0;
+                        break;
+                    case EAGAIN:
+                        // Flow controlled.  Break out of the loop with an EAGAIN 
+                        // error; we try again when space becomes available.
+						
+                        fprintf(stderr, "ClientSendPending: Client %p write-side flow control.\n", client);
+                        
+                        // Tell the CFSocket that we now /really/ need to be told about 
+                        // write space becoming available.  Without this we'll never 
+                        // recover if the client stops accepting messages temporarily 
+                        // (which causes the socket buffer to fill up and us to get an 
+                        // EAGAIN) and then starts accepting messages again.  At that 
+                        // point the client will drain the socket buffer, but we'll never 
+                        // hear about it because CFSocket doesn't know that we care 
+                        // about write space.  With this call CFSocket knows that we 
+                        // care, and will send us an kCFSocketWriteCallBack event if 
+                        // space becomes available in the socket buffer.
+                        
+                        CFSocketEnableCallBacks(client->fSockCF, kCFSocketWriteCallBack);
+                        break;
+                    default:
+                        // Errored.  Our response is typically draconian: 
+                        // we return the error to our caller, which then kills the client 
+                        // completely.
+						
+                        fprintf(stderr, "ClientSendPending: Client %p killed because of send error (%d).\n", client, err);
+                        break;
+                }
+            } else {
+                assert(false);
+            }
+        } while ( (err == 0) && ! done );
+    }
+    
+    // As far as the caller is concerned, write-side flow control is not an error.
+    
+    if (err == EAGAIN) {
+        err = 0;
+    }
+    
+    return err;
+}
+
+static Boolean ClientSend(ClientState *client, const PacketHeader *packet)
+// Called in various places to send a packet to a client.  
+// This adds it to the send queue and then calls ClientSendPending 
+// to attempt a send.
+{
+    Boolean         result;
+    PacketHeader *  copiedPacket;
+    
+    assert(client != NULL);
+    assert(packet != NULL);
+    assert(packet->fSize >= sizeof(PacketHeader));
+	
+    // If we've buffered kClientMaximumPendingSends already, the client is 
+    // just not reading them.  To avoid us consuming all of our memory buffering 
+    // packets for a deaf client, we just kill the client.
+    
+    result = true;
+    if ( CFArrayGetCount(client->fPendingSends) >= kClientMaximumPendingSends ) {
+        fprintf(stderr, "ClientSend: Client %p killed because of too many outstanding sends.\n", client);
+        
+        result = false;
+    }
+	
+    // Copy the packet data, append that copy to the send queue, and then 
+    // give it a kick.
+    //
+    // The memory allocated here will be freed when the packet is succesfully 
+    // sent (SendPending), or the client is destroy.
+    
+    if (result) {
+        copiedPacket = (PacketHeader *) malloc(packet->fSize);
+        result = (copiedPacket != NULL);
+    }
+    if (result) {
+        memcpy(copiedPacket, packet, packet->fSize);
+        
+        CFArrayAppendValue(client->fPendingSends, copiedPacket);
+        
+        result = ( ClientSendPending(client) == 0 );
+    }
+	
+    return result;
+}
+
+static Boolean ClientSendReply(ClientState *client, const PacketHeader *request, int errNum)
+// Send an RPC reply packet to the client.  You must supply request 
+// because it forms the basis of many of the fields in the reply. 
+// You also have to supply errNum, which is an errno-style error 
+// indicating the fate of the request.
+{
+    PacketReply     response;
+	
+    assert(client  != NULL);
+    assert(request != NULL);
+	
+    InitPacketHeader(&response.fHeader, kPacketTypeReply, sizeof(response), false);
+    // Copy the ID from the request packet, overriding the fID set by InitPacketHeader.
+    response.fHeader.fID    = request->fID;
+    response.fErr = errNum;
+	
+    return ClientSend(client, &response.fHeader);
+}
+
+static void ClientGotSpace(ClientState *client)
+// This routine is called by ClientEvent when it receives the kCFSocketWriteCallBack 
+// event, indicating that there is space to write in the client's socket buffer. 
+// It calls ClientSendPending to process any packets that are waiting to be sent.  
+// In most cases this does nothing because the client send queue is empty.  However, 
+// if the client goes deaf, so the socket buffer becomes write-side flow controlled, 
+// packets can back up in the send queue.  When the client starts receiving packets 
+// again, space becomes available in the socket buffer and CFSocket sends us the 
+// kCFSocketWriteCallBack.  We respond to that by resuming our sends.
+{
+    int             err;
+	
+	assert(client != NULL);
+    
+    fprintf(stderr, "ClientGotSpace: Client %p lifted write-side flow control.\n", client);
+	
+    err = ClientSendPending(client);
+	
+	// If the sending failed for any reason (except flow control, for which 
+	// ClientSendPending mutates the EAGAIN status to a 0) we kill the client.
+	
+    if (err != 0) {
+        ClientDestroy(client);
+    }
+}
+
+#pragma mark Receive
+
+// The receive engine is based around ClientGotData, which is the routine that gets 
+// called when new data arrives, and a variety of packet handlers for processing 
+// specific types of packets and that all have the same form.
+//
+// A packet handle routine takes two parameters, the client and the packet, neither 
+// of which can be NULL, and does the work to process that packet.  This typically 
+// involves checking that the packet is valid, doing the job requested by the packet, 
+// and then, if the packet is for an RPC, sending the reply.
+//
+// If the packet handler returns false, the caller (ClientGotData) assumes that 
+// something was seriously wrong with the packet and kills the connection to the 
+// client.  A packet handler typically does this if the packet itself is malformed; 
+// if the job requested by the packet can't be done (for example, there might not 
+// be enough memory), the packet handler wouldn't return false but would, instead, 
+// send an error status back to the client in the RPC reply.
+
+static Boolean ClientGoodbye(ClientState *client, PacketGoodbye *packet)
+// A packet handler for the Goodbye packet.  See the large comment above for 
+// a discussion of the general form of a packet handler.
+//
+// A Goodbye packet is sent by the client to indicate to us that it's closing 
+// its end of the connection.
+{
+    Boolean     result;
+    
+    assert(client != NULL);
+    assert(packet != NULL);
+    
+    result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketGoodbye));
+    if ( result ) {
+        result = ClientCheckPacketID(client, &packet->fHeader, kPacketIDNone);
+    }
+    
+    if (result) {
+        // During reliability print all of the goodbyes proved to be too verbose, 
+        // so I've disabled it for now.
+        
+        if (false) {
+            fprintf(stderr, "%p: Goodbye (%.*s).\n", client, (int) sizeof(packet->fMessage), packet->fMessage);
+        }
+        
+        // Unlike most packet handlers, we return false on success.  This is because 
+		// the Goodbye packet tells us that the client has gone away, and thus we 
+		// need to kill the client.  It turns out that returning false does the job 
+		// without us having to write any special code.
+        
+        result = false;
+    }
+    
+    return result;
+}
+
+static Boolean ClientNOP(ClientState *client, PacketNOP *packet)
+// A packet handler for the NOP packet.  See the large comment above for 
+// a discussion of the general form of a packet handler.
+//
+// A NOP RPC does nothing; it's used to test client/server connection.
+{
+    Boolean     result;
+    
+    result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketNOP));
+    
+    if (result) {
+        fprintf(stderr, "%p: NOP\n", client);
+        
+        result = ClientSendReply(client, &packet->fHeader, 0);
+    }
+    
+    return result;
+}
+
+static Boolean ClientWhisper(ClientState *client, PacketWhisper *packet)
+// A packet handler for the Whisper packet.  See the large comment above for 
+// a discussion of the general form of a packet handler.
+//
+// A Whisper RPC causes the server to print the associated message.
+{
+    Boolean result;
+    
+    result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketWhisper));
+    if (result) {
+        fprintf(stderr, "%p: Whisper \"%.*s\"\n", client, (int) sizeof(packet->fMessage), packet->fMessage);
+		
+        result = ClientSendReply(client, &packet->fHeader, 0);
+    }
+    
+    return result;
+}
+
+static Boolean ClientShout(ClientState *client, PacketShout *packet)
+// A packet handler for the Shout packet.  See the large comment above for 
+// a discussion of the general form of a packet handler.
+//
+// A Shout packet causes the server to echo the message (in the form 
+// of a Shout packet) to every client that has registered as a listener.
+{
+    Boolean     result;
+    Boolean     sendResult;
+    
+    result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketShout));
+    if (result) {
+        result = ClientCheckPacketID(client, &packet->fHeader, kPacketIDNone);
+    }
+	
+	// The Shout packet is good.  Let's echo it to each listener.
+	
+    if (result) {
+        ClientState  ** allClients;
+        CFIndex         clientCount;
+        CFIndex         clientIndex;
+		
+        fprintf(stderr, "%p: Shout   \"%.*s\"\n", client, (int) sizeof(packet->fMessage), packet->fMessage);
+		
+		NSString * shout = [NSString stringWithCString:packet->fMessage encoding:NSUTF8StringEncoding];
+		NSLog(@"CLIENT SHOUT BEING CALLED (YAAAY!!) : %@" , shout);
+        
+        // We make a snapshot of the client list because clients might disappear 
+        // as we talk to them.  That is, the act of talking to the client might 
+        // cause us to notice that the client is dead.
+        
+        clientCount = CFSetGetCount(gClients);
+        
+        allClients = calloc(clientCount, sizeof(ClientState *));
+        if (allClients == NULL) {
+            fprintf(stderr, "ClientShout: Shout from %p failed because we couldn't allocate memory.\n", client);
+        } else {
+            CFSetGetValues(gClients, (const void **) allClients);
+            
+			// Iterate through the array of clients, sending the Shout packet to each.
+			
+            for (clientIndex = 0; clientIndex < clientCount; clientIndex++) {
+                ClientState *  thisClient;
+                
+                thisClient = allClients[clientIndex];
+                if (thisClient->fListening) {
+                    sendResult = ClientSend(thisClient, &packet->fHeader);
+                    
+                    // Fun fun fun.  If we're sending to ourselves, we return the result 
+                    // of the send as our function result, which, if there's a failure, 
+                    // will trigger ClientGotData to clean us up.  OTOH, if we're sending 
+                    // to another client, we're responsible for cleaning up if there's 
+                    // a failure.
+                    
+                    if (thisClient == client) {
+                        result = sendResult;
+                    } else {
+                        if ( ! sendResult ) {
+                            fprintf(stderr, "ClientShout: Shout from %p to %p failed.\n", client, thisClient);
+                            
+                            ClientDestroy(thisClient);
+                        }
+                    }
+                }
+            }
+        }
+		
+        free(allClients);
+    }
+    return result;
+}
+
+static Boolean ClientListen(ClientState *client, PacketListen *packet)
+// A packet handler for the Listen packet.  See the large comment above for 
+// a discussion of the general form of a packet handler.
+//
+// A Listen RPC tells the server that the client wants to hear about shouted 
+// messages.
+{
+    Boolean     result;
+    
+    result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketListen));
+    if (result) {
+        if (client->fListening) {
+            fprintf(stderr, "ClientListen: Redundant Listen from %p.\n", client);
+        } else {
+            fprintf(stderr, "%p: Listen\n", client);
+        }
+        client->fListening = true;
+        
+        result = ClientSendReply(client, &packet->fHeader, 0);
+    }
+    
+    return result;
+}
+
+static Boolean ClientQuit(ClientState *client, PacketQuit *packet)
+// A packet handler for the Quit packet.  See the large comment above for 
+// a discussion of the general form of a packet handler.
+//
+// A Quit RPC causes the server to quit.
+{
+    Boolean     result;
+    
+    result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketQuit));
+    if (result) {
+        fprintf(stderr, "%p: Quit\n", client);
+        
+        // Stop the main event loop.
+        
+        CFRunLoopStop( CFRunLoopGetCurrent() );
+        
+        // This reply should go out immediately.  If the client, for some reason, 
+        // is flow controlled, it may not see the response.  But really, that's 
+        // the client's fault (-:
+        
+        result = ClientSendReply(client, &packet->fHeader, 0);
+    }
+    
+    return result;
+}
+
+static void ClientGotData(ClientState *client, const void *data)
+// This routine is called by ClientEvent when it receives the kCFSocketDataCallBack 
+// event, indicating that CFSocket has read data from the socket.  The routine 
+// appends the data to the receive buffer (fBufferedData) and then looks through 
+// the receive buffer for complete packets.  For each complete packet it finds, 
+// it calls the packet handler (the various routines above) to process the packet 
+// and then it deletes the packet from the front of the receive buffer.
+// 
+// data is the data read for us by CFSocket.  It's actually a CFDataRef 
+// but, because we're being called from a generic CFSocket event handler, 
+// it's of type (const void *).  We have to do the cast here.
+{
+    CFDataRef       newData;
+	
+	assert(client != NULL);
+    
+    newData = (CFDataRef) data;
+    assert(newData != NULL);
+    assert( CFGetTypeID(newData) == CFDataGetTypeID() );
+	
+    if ( CFDataGetLength(newData) == 0 ) {
+        // A zero length data indicates the end of the data stream; the client is dead 
+		// so we just go and remove our record of it.
+        
+        fprintf(stderr, "ClientGotData: Client %p died unexpectedly.\n", client);
+        
+        ClientDestroy(client);
+    } else {
+		
+        // Append the new data to whatever data we have already buffered 
+        // (most likely nothing).
+        
+        CFDataAppendBytes(client->fBufferedData, CFDataGetBytePtr(newData), CFDataGetLength(newData));
+        
+        // Process packets until we run out of complete ones.
+        
+        do {
+            PacketHeader *  thisPacket;
+            Boolean         success;
+            
+            if ( CFDataGetLength(client->fBufferedData) < sizeof(PacketHeader) ) {
+                // Not enough data for the packet header; we're done.
+                break;
+            }
+            
+            thisPacket = (PacketHeader *) CFDataGetBytePtr(client->fBufferedData);
+            
+            if ( thisPacket->fMagic != kPacketMagic ) {
+                fprintf(stderr, "ClientGotData: Client %p sent us a packet with bad magic (%.4s).\n", client, (char *) &thisPacket->fMagic);
+                
+                ClientDestroy(client);
+                break;
+            }
+            
+            if (thisPacket->fSize > kPacketMaximumSize) {
+                fprintf(stderr, "ClientGotData: Client %p sent us a packet that's just too big (%" PRIu32 ").\n", client, thisPacket->fSize);
+                
+                ClientDestroy(client);
+                break;
+            }
+            
+            if ( CFDataGetLength(client->fBufferedData) < thisPacket->fSize ) {
+                // Not enough data for the packet body; we're done.
+                break;
+            }
+			
+			// Dispatch to the appropriate packet handler.
+            
+            switch (thisPacket->fType) {
+                case kPacketTypeGoodbye:
+                    success = ClientGoodbye(client, (PacketGoodbye *) thisPacket);
+                    break;
+                case kPacketTypeNOP:
+                    success = ClientNOP(client, (PacketNOP *) thisPacket);
+                    break;
+                case kPacketTypeWhisper:
+                    success = ClientWhisper(client, (PacketWhisper *) thisPacket);
+                    break;
+                case kPacketTypeShout:
+                    success = ClientShout(client, (PacketShout *) thisPacket);
+                    break;
+                case kPacketTypeListen:
+                    success = ClientListen(client, (PacketListen *) thisPacket);
+                    break;
+                case kPacketTypeQuit:
+                    success = ClientQuit(client, (PacketQuit *) thisPacket);
+                    break;
+                default:
+                    fprintf(stderr, "ClientGotData: Client %p sent us a packet with an unexpected type (%.4s).\n", client, (char *) &thisPacket->fType);
+                    
+                    success = false;
+                    break;                
+            }
+            if ( ! success ) {
+                ClientDestroy(client);
+                break;
+            }
+            
+            // Delete this packet from the front of our packet buffer.
+            
+            CFDataDeleteBytes(client->fBufferedData, CFRangeMake(0, thisPacket->fSize));
+            
+        } while (true);
+    }
+}
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Debug Infrastructure
+
+static void ClientPrintInfo(const void *value, void *context)
+// Called by PrintServerState to print the state of a particular 
+// client.  Actually passed as a callback to CFSetApplyFunction, 
+// which is why the value parameter is a (const void *) rather 
+// than a (ClientState *).  context is not used in this... context (-;
+{
+#pragma unused(context)
+    ClientState *  client;
+    static const char * kBoolToStr[2] = { "false", "true" };
+	
+    client = (ClientState *) value;
+    assert(client != NULL);
+    assert(client->fMagic == kClientStateMagic);
+    
+    fprintf(stderr, "  Client %p:\n", client);
+    fprintf(stderr, "    fSockFD            = %d\n", client->fSockFD);
+    fprintf(stderr, "    fSockCF            = %p\n", client->fSockCF);
+    fprintf(stderr, "    fRunLoopSource     = %p\n", client->fRunLoopSource);
+    fprintf(stderr, "    fBufferedData      = %p (count: %ld)\n", client->fBufferedData, CFDataGetLength(client->fBufferedData));
+    fprintf(stderr, "    fPendingSends      = %p (count: %ld)\n", client->fPendingSends, CFArrayGetCount(client->fPendingSends));
+    fprintf(stderr, "    fPendingSendOffset = %zd\n", client->fPendingSendOffset);
+    fprintf(stderr, "    fListening         = %s\n", kBoolToStr[client->fListening]);    
+}
+
+static void PrintServerState(void)
+// Called in response to a SIGINFO.  This prints a bunch of state 
+// information about the server.  Note that it is not called from a 
+// signal handler directly, rather from SignalRunLoopCallback which 
+// is a runloop callback.  So we can do all sorts of things that 
+// aren't safe in a true signal handler.
+{
+    fprintf(stderr, "CFLocalServer State\n");
+    fprintf(stderr, "-------------------\n");
+    if ( CFSetGetCount(gClients) == 0) {
+        fprintf(stderr, "Clients: none\n");
+    } else {
+        fprintf(stderr, "Clients:\n");
+        CFSetApplyFunction(gClients, ClientPrintInfo, NULL);
+    }
+    fprintf(stderr, "\n");
+	
+    DebugPrintDescriptorTable();
+}
+
+/////////////////////////////////////////////////////////////////
+#pragma mark ***** Server Framework
+
+static void ListeningSocketAcceptCallback(
+										  CFSocketRef             s, 
+										  CFSocketCallBackType    type, 
+										  CFDataRef               address, 
+										  const void *            data, 
+										  void *                  info
+)
+// This is the CFSocket event callback for the listening socket.  For a 
+// description of the parameters, see the CFSocket documentation.
+//
+// CFSocket calls this routine when it has accepted a new connection on 
+// the socket.  in this case data is a pointer to the newly created 
+// file descriptor that describes the new connection.  This routine 
+// responds by calling into the client layer to create a new client.
+{
+#pragma unused(s)
+#pragma unused(address)
+#pragma unused(info)
+    
+    assert(type == kCFSocketAcceptCallBack);
+    assert(   (int *) data  != NULL );
+    assert( (*(int *) data) != -1 );
+	
+    (void) ClientCreate( (*(int *) data), NULL );
+    
+    // If ClientCreate fails, it cleans up after itself, including 
+    // closing the newly created client socket.  It's even printed 
+    // a happy message (well, an unhappy message).  So we do nothing 
+	// on failure.
+}
+
+static void SignalRunLoopCallback(const siginfo_t *sigInfo, void *refCon)
+// This routine is called in response to a signal (SIGINT 
+// or SIGINFO).  It is not, however, a signal handler.  Rather, 
+// we orchestrate to have it called from the runloop (via 
+// the magic of InstallSignalToSocket).  It's purpose 
+// is to a) stop the server when the user types ^C (SIGINT), or 
+// b) print some information about the server (SIGINFO).
+{
+#pragma unused(sigInfo)
+#pragma unused(refCon)
+    
+    switch (sigInfo->si_signo) {
+        case SIGINFO:
+			// Respond to SIGINFO by printing some information about the server.
+			
+            PrintServerState();
+            break;
+        case SIGINT:
+			// Respond to SIGINT by stopping the server.
+			
+			// Stop the runloop.  Note that we can get a reference to the runloop by 
+			// calling CFRunLoopGetCurrent because this is called from the runloop.
+			
+			CFRunLoopStop( CFRunLoopGetCurrent() );
+			
+			// Print a bonus newline to ensure that the next command prompt isn't 
+			// printed on the same line as the echoed ^C.
+			
+			fprintf(stderr, "\n");
+            break;
+        default:
+            assert(false);
+            break;
+    }
+}
+
+static int SafeBindUnixDomainSocket(int sockFD, const char *socketPath)
+// This routine is called to safely bind the UNIX domain socket 
+// specified by sockFD to the path specificed by socketPath.  To avoid 
+// security problems, socketPath must point it to a sticky directory 
+// (such as "/var/tmp").  This allows us to create the socket with 
+// very specific permissions, without us having to worry about a malicious 
+// process switching stuff out from underneath us.
+//
+// For this test program, socketpath is "/var/tmp/com.apple.dts.CFLocalServer/Socket". 
+// The code calculates parentPath as ""/var/tmp/com.apple.dts.CFLocalServer" 
+// and grandParentPath as "/var/tmp".  Each ancestor has certain key attributes. 
+//
+// o grandParentPath must a sticky directory.  Because it's sticky, we 
+//   can create a directory within it and know that either a) we created 
+//   the directory, and no one else can mess with it because it's sticky, 
+//   or b) the directory exists, in which case we can check it's owner 
+//   and permissions and, if they are set correctly, know that no one else 
+//   can mess with it.
+//
+// o When we create the parentPath directory within grandParentPath, we set its 
+//   permissions to make it readable by everyone (so everyone can connect to our 
+//   server) but writeable only by us (so that only we can create the listening 
+//   socket).  Because parentPath is set this way, we know that no one else 
+//   can modify it to produce a security problem.
+//
+// IMPORTANT:
+// This routine is designed to protect against external attack, not against 
+// being called incorrectly.  It only does minimal checking of socketPath.  
+// For example, if one of the components of socketPath was "..", the security 
+// checking done by this routine might be invalid.  Do not pass an untrusted 
+// socketPath to this routine.
+{
+    int                 err;
+    char *              parentPath;
+    char *              grandParentPath;
+    char *              lastSlash;
+    struct stat         sb;
+    struct sockaddr_un  bindReq;
+    static const mode_t kRequiredParentMode = S_IRWXU | (S_IRGRP | S_IXGRP) | (S_IROTH | S_IXOTH); // rwxr-xr-x
+    
+    parentPath      = NULL;
+    grandParentPath = NULL;
+    
+    // sockaddr_un can only hold a very short path (it's 104 bytes long), 
+    // so we check that limit right up front.  Note the use of >= in the 
+    // check below: we fail if socketPath is exactly 104 chars long because 
+    // that would leave no space for the trailing null character.  Looking at 
+    // the kernel code, I don't think this is strictly necessary (in fact, 
+    // it seems that the kernel code will handle much longer paths than sun_path, 
+    // up to an overall sockaddr size ofSOCK_MAXADDRLEN), but I'm being 
+    // paranoid.
+    
+    err = 0;
+    if (strlen(socketPath) >= sizeof(bindReq.sun_path)) {
+        err = EINVAL;
+    }
+    
+    // Construct parentPath and grandParent path by knocking path components 
+    // off the end.
+    
+    if (err == 0) {
+        parentPath = strdup(socketPath);
+        if (parentPath == NULL) {
+            err = ENOMEM;
+        }
+    }
+    if (err == 0) {
+        lastSlash = strrchr(parentPath, '/');
+        if (lastSlash == NULL) {
+            fprintf(stderr, "SafeBindUnixDomainSocket: Can't get parent for path (%s).\n", socketPath);
+            err = EINVAL;
+        } else {
+            *lastSlash = 0;
+        }
+    }
+    if (err == 0) {
+        grandParentPath = strdup(parentPath);
+        if (grandParentPath == NULL) {
+            err = ENOMEM;
+        }
+    }
+    if (err == 0) {
+        lastSlash = strrchr(grandParentPath, '/');
+        if (lastSlash == NULL) {
+            fprintf(stderr, "SafeBindUnixDomainSocket: Can't get grandparent for path (%s).\n", socketPath);
+            err = EINVAL;
+        } else {
+            *lastSlash = 0;
+        }
+    }
+    
+    // Check that the parent directory is a sticky root-owned directory.  If the 
+    // grandparent directory is sticky, we know that any items in that directory 
+    // that are owned by us can't be substituted by anyone else (that is: deleted, 
+    // moved or renamed, and then replaced by an attacker's item).
+    
+    if (err == 0) {        
+        err = stat(grandParentPath, &sb);
+        err = MoreUNIXErrno(err);
+    }
+	
+    if ( (err == 0) && ( ! (sb.st_mode & S_ISTXT) || (sb.st_uid != 0) ) ) {
+        fprintf(stderr, "SafeBindUnixDomainSocket: Grandparent directory (%s) is not a sticky root-owned directory.\n", grandParentPath);
+        err = EINVAL;
+    }
+    
+    // Create the parent directory.  Ignore an EEXIST error because of the 
+    // next check.
+	
+    if (err == 0) {
+        err = mkdir(parentPath, kRequiredParentMode);
+        err = MoreUNIXErrno(err);
+        
+        if (err == EEXIST) {
+            err = 0;
+        }
+    }
+    
+    // Check that the parent directory is a directory, is owned by us, and 
+    // has the right mode.  This ensures that no one except us can be monkeying 
+    // with its contents.  And we know that no one can substitute a /different/ 
+    // directory underneath us because its parent (grandParentPath) is sticky.
+	
+    if (err == 0) {
+        err = stat(parentPath, &sb);
+        err = MoreUNIXErrno(err);
+    }
+    if ( (err == 0) && (sb.st_uid != geteuid()) ) {
+        fprintf(stderr, "SafeBindUnixDomainSocket: Parent (%s) is not owned by us.\n", parentPath);
+        err = EINVAL;
+    }
+    if ( (err == 0) && ! S_ISDIR(sb.st_mode) ) {
+        fprintf(stderr, "SafeBindUnixDomainSocket: Parent (%s) is not a directory.\n", parentPath);
+        err = EINVAL;
+    }
+    if ( (err == 0) && ( (sb.st_mode & ACCESSPERMS) != kRequiredParentMode ) ) {
+        fprintf(stderr, "SafeBindUnixDomainSocket: Parent (%s) has wrong permissions.\n", parentPath);
+        err = EINVAL;
+    }
+    
+    // If all is well, let's bind our socket.  This involves deleting any existing 
+    // socket and recreating our own.  We know we can do this without worrying 
+    // about substitution because only we have write access to the parent directory.
+	
+    if (err == 0) {
+        mode_t              oldUmask;
+		
+        // Temporarily set the umask to 0 (the default is 0022) so that the 
+        // socket is created rwxrwxrwx.  This allows any user to connect to 
+        // our socket.
+		
+        oldUmask = umask(0);
+		
+        // Delete any existing socket.  We delete the socket when we shut down, 
+        // but, if we quit unexpectedly, it could've been left lying around.
+        
+        (void) unlink(socketPath);
+		
+        // Bind the socket, allowing other clients to connect.
+        
+        bindReq.sun_len    = sizeof(bindReq);
+        bindReq.sun_family = AF_UNIX;
+        strcpy(bindReq.sun_path, socketPath);
+		
+        err = bind(sockFD, (struct sockaddr *) &bindReq, SUN_LEN(&bindReq));
+        err = MoreUNIXErrno(err);
+		
+        (void) umask(oldUmask);
+    }
+	
+    free(parentPath);
+    free(grandParentPath);
+    
+    return err;
+}
+
+static void PrintUsage(const char *argv0)
+// Print the program's usage.  Given that it supports no arguments whatsoever, 
+// this is pretty simple.
+{
+    const char *command;
+    
+    command = strrchr(argv0, '/');
+    if (command == NULL) {
+        command = argv0;
+    } else {
+        command += 1;
+    }
+    fprintf(stderr, "usage: %s\n", command);
+}
+
+
+
+ at implementation MPNotifications (IPCAdditions)
+
+-(void) startIPCServerThread {
+	NSAutoreleasePool * sPool = [[NSAutoreleasePool alloc] init];
+	
+	NSLog(@"INSIDE SERVER THREAD");
+	
+	//Configure runloop
+	int         err = 0;
+    int         listenerFD;
+    CFSocketRef listenerCF;
+    Boolean     didBind;
+    
+    didBind    = false;
+    listenerFD = -1;
+    listenerCF = NULL;
+	
+	
+	
+	
+	// Ignore SIGPIPE because it's a deeply annoying concept.  If you don't ignore 
+	// SIGPIPE when writing to a UNIX domain socket whose far side has been closed 
+	// will trigger a SIGPIPE, whose default action is to terminate the program.
+    if (err == 0) {
+        fprintf(stderr, "CFLocalServer: Starting up (pid: %ld).\n", (long) getpid());
+		NSLog(@"CFLocalServer: Starting up (pid: %ld).\n", (long) getpid());
+        err = MoreUNIXIgnoreSIGPIPE();
+    }
+    
+	// Set up the signal handlers we are interested in.  In this case we redirect 
+	// SIGINT and SIGINFO to our runloop.  If either of these signals occurs, we 
+	// end up executing SignalRunLoopCallback.
+    if (err == 0) {
+        sigset_t    interestingSignals;
+        (void) sigemptyset(&interestingSignals);
+        (void) sigaddset(&interestingSignals, SIGINT);
+        (void) sigaddset(&interestingSignals, SIGINFO);
+        
+        err = InstallSignalToSocket(
+									&interestingSignals,
+									CFRunLoopGetCurrent(),
+									kCFRunLoopDefaultMode,
+									SignalRunLoopCallback,
+									NULL
+									);
+    }
+	
+	// Create the initial client set.
+    if (err == 0) {
+		err = ClientInitialise();
+		NSLog(@"Initilalizing Client");
+    }
+    
+	// Create our listening socket, bind it, and then wrap it in a CFSocket.
+    if (err == 0) {
+        listenerFD = socket(AF_UNIX, SOCK_STREAM, 0);
+        err = MoreUNIXErrno(listenerFD);
+		NSLog(@"Creating Socket");
+    }
+    if (err == 0) {
+        err = SafeBindUnixDomainSocket(listenerFD, kServerSocketPath);
+        didBind = (err == 0);
+		NSLog(@"Binding Socket %i", err);
+    }
+    if (err == 0) {
+        err = listen(listenerFD, 5);
+        err = MoreUNIXErrno(err);
+		NSLog(@"Listening Socket");
+    }
+    if (err == 0) {
+        listenerCF = CFSocketCreateWithNative(
+											  NULL, 
+											  (CFSocketNativeHandle) listenerFD, 
+											  kCFSocketAcceptCallBack, 
+											  ListeningSocketAcceptCallback, 
+											  NULL);
+		
+		NSLog(@"Creating Callbacks!");
+        if (listenerCF == NULL) {
+            err = EINVAL;
+        }
+    }
+    
+	// Schedule the listening socket on our runloop.
+	NSRunLoop * currentLoop = [NSRunLoop currentRunLoop];
+    if (err == 0) {
+        CFRunLoopSourceRef  rls;
+        
+        rls = CFSocketCreateRunLoopSource(NULL, listenerCF, 0);
+        if (rls == NULL) {
+            err = EINVAL;
+        } else {
+            CFRunLoopAddSource( [currentLoop getCFRunLoop], rls, kCFRunLoopDefaultMode);
+            NSLog(@"Adding Source to current runloop");
+			
+            // We no longer need this source, so we just release it.
+            CFRelease(rls);
+        }
+    }
+	
+	
+	double resolution = 30.0;
+	
+	//Add input sources to my run loop 
+	//terminateBackgroundThread is going to be set to NO before the privileged operation is called
+	//it will be set to YES after the privileged operation finishes execution. So I guess I need
+	//accessor methods?
+	NSThread * cThread = [NSThread currentThread];
+	NSLog(@"RUNNING RUN LOOP with thread %@" , [cThread threadDictionary]);
+	
+	do {
+		NSDate * nextDate = [NSDate dateWithTimeIntervalSinceNow:resolution];
+		[currentLoop runMode:NSDefaultRunLoopMode beforeDate:nextDate];
+		
+		//might add some code here to clean up and recreate autoreleasepool
+	}	while (terminateBackgroundThread == NO);
+	
+	[sPool release];
+}
+
+-(void) prepareIPCServerThread {
+	terminateBackgroundThread == NO;
+}
+
+-(void) stopIPCServerThread {
+	terminateBackgroundThread == YES;
+}
+
+
+ at end


Property changes on: branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications+IPCAdditions.m
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.h
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.h	2008-08-25 05:10:18 UTC (rev 39567)
+++ branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.h	2008-08-25 05:36:55 UTC (rev 39568)
@@ -99,6 +99,7 @@
 	int sd1, rc;
 	struct sockaddr_un serveraddr;
 	BOOL hasSetFileDescriptor;
+	BOOL terminateBackgroundThread;
 	
 }
 

Modified: branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.m
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.m	2008-08-25 05:10:18 UTC (rev 39567)
+++ branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.m	2008-08-25 05:36:55 UTC (rev 39568)
@@ -1360,8 +1360,8 @@
 
 
 @implementation MPNotifications
-BOOL terminateBackgroundThread;
 
+
 + (MPNotifications *)sharedListener {
 	@synchronized(self) {
 		if ([[[NSThread currentThread] threadDictionary] objectForKey:@"sharedMPListener"] == nil) {

Modified: branches/gsoc08-framework/MacPorts_Framework_Release/MacPorts.Framework.xcodeproj/project.pbxproj
===================================================================
--- branches/gsoc08-framework/MacPorts_Framework_Release/MacPorts.Framework.xcodeproj/project.pbxproj	2008-08-25 05:10:18 UTC (rev 39567)
+++ branches/gsoc08-framework/MacPorts_Framework_Release/MacPorts.Framework.xcodeproj/project.pbxproj	2008-08-25 05:36:55 UTC (rev 39568)
@@ -73,6 +73,8 @@
 		6EE6DDA40E626D4A00FB2115 /* MacPorts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* MacPorts.framework */; };
 		6EE6DDA70E626D5A00FB2115 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6EB6FC900E45DEA80057962C /* Foundation.framework */; };
 		6EE6DDAB0E626DC900FB2115 /* MPHelperToolIPCTester.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE6DDAA0E626DC900FB2115 /* MPHelperToolIPCTester.m */; };
+		6EE6DDCE0E6276AA00FB2115 /* MPNotifications+IPCAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EE6DDCC0E6276AA00FB2115 /* MPNotifications+IPCAdditions.h */; };
+		6EE6DDCF0E6276AA00FB2115 /* MPNotifications+IPCAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EE6DDCD0E6276AA00FB2115 /* MPNotifications+IPCAdditions.m */; };
 		6EE93E670E493AC600AECE9E /* interpInit.tcl in Resources */ = {isa = PBXBuildFile; fileRef = 6EE93E660E493AC600AECE9E /* interpInit.tcl */; };
 		6EE93FAB0E495C2200AECE9E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ECD970D0E465C7800488335 /* Security.framework */; };
 		8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };
@@ -169,6 +171,8 @@
 		6ED1AC740E4BA162000353B6 /* HelperToolServerFile.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HelperToolServerFile.txt; sourceTree = "<group>"; };
 		6EE6DD9E0E626D2800FB2115 /* MPHelperToolIPCTester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; name = MPHelperToolIPCTester; path = build/Debug/MPHelperToolIPCTester; sourceTree = "<group>"; };
 		6EE6DDAA0E626DC900FB2115 /* MPHelperToolIPCTester.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPHelperToolIPCTester.m; sourceTree = "<group>"; };
+		6EE6DDCC0E6276AA00FB2115 /* MPNotifications+IPCAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPNotifications+IPCAdditions.h"; sourceTree = "<group>"; };
+		6EE6DDCD0E6276AA00FB2115 /* MPNotifications+IPCAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPNotifications+IPCAdditions.m"; sourceTree = "<group>"; };
 		6EE93E660E493AC600AECE9E /* interpInit.tcl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = interpInit.tcl; sourceTree = "<group>"; };
 		6EE93E780E495B3100AECE9E /* Tcl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Tcl.framework; path = Library/Frameworks/Tcl.framework; sourceTree = SDKROOT; };
 		8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
@@ -365,6 +369,8 @@
 				6E8563B70E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h */,
 				6E270D070E158CED00BAE687 /* MPNotifications.h */,
 				6E270D080E158CED00BAE687 /* MPNotifications.m */,
+				6EE6DDCC0E6276AA00FB2115 /* MPNotifications+IPCAdditions.h */,
+				6EE6DDCD0E6276AA00FB2115 /* MPNotifications+IPCAdditions.m */,
 			);
 			name = "Tcl Notifications ";
 			sourceTree = "<group>";
@@ -421,6 +427,7 @@
 				6EC260990E4272D20013BC48 /* MPHelperCommon.h in Headers */,
 				6E8563B90E5DDF7000C1D73C /* MPHelperNotificationsCommon.h in Headers */,
 				6E8563BA0E5DDF7000C1D73C /* MPHelperNotificationsProtocol.h in Headers */,
+				6EE6DDCE0E6276AA00FB2115 /* MPNotifications+IPCAdditions.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -673,6 +680,7 @@
 				6EC260760E426FC80013BC48 /* BetterAuthorizationSampleLib.c in Sources */,
 				6EC2609A0E4272D20013BC48 /* MPHelperCommon.c in Sources */,
 				6E8563B80E5DDF7000C1D73C /* MPHelperNotificationsCommon.c in Sources */,
+				6EE6DDCF0E6276AA00FB2115 /* MPNotifications+IPCAdditions.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macports-changes/attachments/20080824/a9b2253c/attachment-0001.html 


More information about the macports-changes mailing list