Changeset 39569


Ignore:
Timestamp:
Aug 25, 2008, 5:53:02 AM (9 years ago)
Author:
armahg@…
Message:

Removed IPC server code from MPNotifications files.

Location:
branches/gsoc08-framework/MacPorts_Framework_Release
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • branches/gsoc08-framework/MacPorts_Framework_Release/MPHelperTool.m

    r39562 r39569  
    496496        //aslMsg may be null
    497497       
    498         //Set the file Descriptor here
    499         NSNumber * num = (NSNumber *) (CFNumberRef) CFDictionaryGetValue(request, CFSTR(kServerFileDescriptor));
    500         notificationsFileDescriptor = [num intValue];
    501         asl_NSLog(asl, aslMsg, ASL_LEVEL_DEBUG, @"Setting file descriptor with value %i", notificationsFileDescriptor);
    502         if (notificationsFileDescriptor > 0) {
    503                 hasSetFileDescriptor = YES;
    504         }
    505498       
    506499        //Get the string that was passed in the request dictionary
     
    694687        asl_close(logClient);
    695688       
    696    
     689    // Clean up.
     690    ConnectionClose(conn);
    697691       
    698692       
    699693        int result = BASHelperToolMain(kMPHelperCommandSet, kMPHelperCommandProcs);
    700694       
    701         // Clean up.
    702     ConnectionClose(conn);
     695       
    703696       
    704697        [pool release];
  • branches/gsoc08-framework/MacPorts_Framework_Release/MPInterpreter.m

    r39568 r39569  
    430430                                                                 pathForResource:@"interpInit" ofType:@"tcl"];
    431431       
    432         int serverFileDesc = [[MPNotifications sharedListener] getServerFileDescriptor];
    433        
    434         if (serverFileDesc < 0)
    435                 NSLog(@"Uninitialized file descriptor for HelperTool IPC");
    436432       
    437433       
     
    440436                           statement, @kTclStringToBeEvaluated,
    441437                           tclInterpreterPkgPath, @kTclInterpreterInitPath ,
    442                            interpInitPath, @kInterpInitFilePath,
    443                            [NSNumber numberWithInt:serverFileDesc], @kServerFileDescriptor, nil];
     438                           interpInitPath, @kInterpInitFilePath, nil];
    444439       
    445440        assert(request != NULL);
  • branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.h

    r39568 r39569  
    7474
    7575
    76 #include <stdio.h>
    77 #include <sys/types.h>
    78 #include <sys/socket.h>
    79 #include <sys/un.h>
    80 #include <string.h>
    81 
    8276
    8377/*!
     
    9690       
    9791        //BSD sockets stuff
    98         NSString * serverFilePath;
    99         int sd1, rc;
    100         struct sockaddr_un serveraddr;
    101         BOOL hasSetFileDescriptor;
    10292        BOOL terminateBackgroundThread;
    10393       
     
    144134-(void)setPerformingTclCommand:(NSString *)string;
    145135-(NSString *)performingTclCommand;
    146 -(int) getServerFileDescriptor;
    147136
    148137
  • branches/gsoc08-framework/MacPorts_Framework_Release/MPNotifications.m

    r39568 r39569  
    3737#import "MPNotifications.h"
    3838
    39 #pragma mark MPNotifications Server Code - I
    40 //Things will get ugly ... before they can get beautiful ...
    41 /*
    42  File:       Server.c
    43  
    44  Contains:   Server showing integration of CFSockets and UNIX domain sockets.
    45  
    46  Written by: DTS
    47  
    48  Copyright:  Copyright (c) 2005 by Apple Computer, Inc., All Rights Reserved.
    49  
    50  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
    51  ("Apple") in consideration of your agreement to the following terms, and your
    52  use, installation, modification or redistribution of this Apple software
    53  constitutes acceptance of these terms.  If you do not agree with these terms,
    54  please do not use, install, modify or redistribute this Apple software.
    55  
    56  In consideration of your agreement to abide by the following terms, and subject
    57  to these terms, Apple grants you a personal, non-exclusive license, under Apple's
    58  copyrights in this original Apple software (the "Apple Software"), to use,
    59  reproduce, modify and redistribute the Apple Software, with or without
    60  modifications, in source and/or binary forms; provided that if you redistribute
    61  the Apple Software in its entirety and without modifications, you must retain
    62  this notice and the following text and disclaimers in all such redistributions of
    63  the Apple Software.  Neither the name, trademarks, service marks or logos of
    64  Apple Computer, Inc. may be used to endorse or promote products derived from the
    65  Apple Software without specific prior written permission from Apple.  Except as
    66  expressly stated in this notice, no other rights or licenses, express or implied,
    67  are granted by Apple herein, including but not limited to any patent rights that
    68  may be infringed by your derivative works or by other works in which the Apple
    69  Software may be incorporated.
    70  
    71  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
    72  WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
    73  WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    74  PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
    75  COMBINATION WITH YOUR PRODUCTS.
    76  
    77  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
    78  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
    79  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    80  ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
    81  OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
    82  (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
    83  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    84  
    85  Change History (most recent first):
    86  
    87  $Log: Server.c,v $
    88  Revision 1.2  2005/05/18 13:36:39         
    89  Fixed various documentation/comment changes.
    90  
    91  Revision 1.1  2005/05/17 12:19:32         
    92  First checked in.
    93  
    94  
    95  */
    96 
    97 /////////////////////////////////////////////////////////////////
    98 
    99 // System interfaces
    100 
    101 #include <stdlib.h>
    102 #include <assert.h>
    103 #include <unistd.h>
    104 #include <fcntl.h>
    105 #include <sys/socket.h>
    106 #include <sys/un.h>
    107 #include <sys/stat.h>
    108 #include <sys/param.h>
    109 
    110 
    111 #include "MPHelperNotificationsCommon.h"
    112 #include "MPHelperNotificationsProtocol.h"
    113 
    114 /////////////////////////////////////////////////////////////////
    115 #pragma mark ***** Client State Management
    116 
    117 // The server maintains a ClientState structure to track the state of
    118 // each client.  This state divides neatly into three groups.
    119 //
    120 // o socket -- fSockFD, fSockCF, and fRunLoopSource all represent different
    121 //   aspects of the UNIX domain socket that we're using to talk to the client.
    122 //
    123 // o incoming -- fBufferedData is a buffer containing any incomplete packets that
    124 //   we've received from the client.
    125 //
    126 // o outgoing -- fPendingSends and fPendingSendOffset control the packets that
    127 //   are waiting to be sent to the client.  This list might get long if the
    128 //   client stops listening to us.  Packets will first back up in the UNIX
    129 //   domain socket's socket buffer.  Once that fills up we won't be able to
    130 //   write to the socket anymore.  We respond to that by buffering the
    131 //   packets on the fPendingSends list.  We also tell CFSocket to let us know
    132 //   (with a kCFSocketWriteCallBack event) if space becomes available.
    133 //
    134 //   At this point one of two things will happen.  Either we'll buffer too
    135 //   many packets for the client (kClientMaximumPendingSends) in which case
    136 //   we'll kill the client.  Or the client will start receiving packets again,
    137 //   which will start to empty the socket buffer.  CFSocket will tells about
    138 //   this by sending us a kCFSocketWriteCallBack event, and we'll start
    139 //   pulling packets off the fPendingSends list and writing them to the socket.
    140 
    141 enum {
    142     kClientStateMagic = 'LSCM'               // for Local Server Client Magic
    143 };
    144 
    145 struct ClientState {
    146     OSType              fMagic;             // kClientStateMagic
    147     int                 fSockFD;            // UNIX domain socket to client
    148     CFSocketRef         fSockCF;            // CFSocket wrapper for the above
    149     CFRunLoopSourceRef  fRunLoopSource;     // runloop source for the above
    150     CFMutableDataRef    fBufferedData;      // buffers data for incomplete incoming packets
    151     CFMutableArrayRef   fPendingSends;      // list of packets waiting to be sent
    152     size_t              fPendingSendOffset; // offset of next byte to send in first packet on list
    153     Boolean             fListening;         // true if this client is a listener
    154 };
    155 typedef struct ClientState ClientState;
    156 
    157 // To prevent a deaf client from sucking down all of our memory, we limit the
    158 // number of packets that we'll buffer for a given client.  If the length of
    159 // fPendingSends exceeds kClientMaximumPendingSends, we'll kill the client rather
    160 // than queue more data.
    161 
    162 enum {
    163     kClientMaximumPendingSends = 100
    164 };
    165 
    166 // gClients is a set of all clients we know about.
    167 
    168 static CFMutableSetRef gClients = NULL;         // of (ClientState *)
    169 
    170 #pragma mark Misc
    171 
    172 static Boolean ClientCheckPacketSize(ClientState *client, const PacketHeader *packet, size_t requiredSize)
    173 // Checks that a packet that has arrived from a client is of the
    174 // appropriate size.  Returns false, and prints a message, if it isn't.
    175 {
    176     Boolean result;
    177    
    178     assert(client != NULL);
    179     assert(packet != NULL);
    180     assert(requiredSize >= sizeof(PacketHeader));
    181    
    182     result = true;
    183     if (packet->fSize != requiredSize) {
    184         fprintf(
    185                                 stderr,
    186                                 "ClientCheckPacketSize: Client %p sent us a '%.4s' of the wrong size (got %" PRIu32 ", wanted %zu).\n",
    187                                 client,
    188                                 (char *) &packet->fType,
    189                                 packet->fSize,
    190                                 requiredSize
    191                                 );
    192         result = false;
    193     }
    194     return result;
    195 }
    196 
    197 static Boolean ClientCheckPacketID(ClientState *client, const PacketHeader *packet, int32_t requiredID)
    198 // Checks that a packet that has arrived from a client has the
    199 // correct ID.  Returns false, and prints a message, if it doesn't.
    200 {
    201     Boolean result;
    202        
    203     assert(client != NULL);
    204     assert(packet != NULL);
    205    
    206     result = true;
    207     if (packet->fID != requiredID) {
    208         fprintf(
    209                                 stderr,
    210                                 "ClientCheckPacketID: Client %p sent us a '%.4s' with the wrong ID (got %" PRId32 ", wanted %" PRId32 ").\n",
    211                                 client,
    212                                 (char *) &packet->fType,
    213                                 packet->fID,
    214                                 requiredID
    215                                 );
    216         result = false;
    217     }
    218     return result;
    219 }
    220 
    221 // Forward declarations
    222 
    223 static void ClientGotSpace(ClientState *client);
    224 static void ClientGotData(ClientState *client, const void *data);
    225 
    226 static void ClientEvent(
    227                                                 CFSocketRef             s,
    228                                                 CFSocketCallBackType    type,
    229                                                 CFDataRef               address,
    230                                                 const void *            data,
    231                                                 void *                  info
    232 )
    233 // This is the CFSocket event callback for client sockets.  For a description
    234 // of the parameters, see the CFSocket documentation.
    235 //
    236 // This routine responds to two events, kCFSocketDataCallBack and
    237 // kCFSocketWriteCallBack, dispatching them to ClientGotData and
    238 // ClientGotSpace, respectively.
    239 {
    240 #pragma unused(address)
    241         ClientState *   client;
    242        
    243     assert(s != NULL);
    244        
    245     client = (ClientState *) info;
    246     assert(client != NULL);
    247     assert(client->fMagic == kClientStateMagic);
    248    
    249     switch (type) {
    250         case kCFSocketDataCallBack:
    251             ClientGotData(client, data);
    252             break;
    253         case kCFSocketWriteCallBack:
    254             ClientGotSpace(client);
    255             break;
    256         default:
    257             assert(false);
    258             break;
    259     }
    260 }
    261 
    262 #pragma mark Create/Destroy
    263 
    264 static void ClientDestroy(ClientState *client);
    265 
    266 static int ClientInitialise(void)
    267 // Initialises the client management layer, which simply involves
    268 // creating an empty gClients set.
    269 {
    270         int err;
    271        
    272         err = 0;
    273         gClients = CFSetCreateMutable(NULL, 0, NULL);
    274         if (gClients == NULL) {
    275                 err = ENOMEM;
    276         }
    277         return err;
    278 }
    279 
    280 static void ClientTerminate(void)
    281 // Shuts down the client management layer.  This involves destroying
    282 // any remaining clients and disposing of gClients.
    283 {
    284         CFIndex             clientCount;
    285         CFIndex             clientIndex;
    286         ClientState **      allClients;
    287        
    288     if (gClients != NULL) {
    289                 // Can't use CFSetApplyFunction because the ClientDestroy modifies
    290                 // the gClients set.
    291                
    292                 clientCount = CFSetGetCount(gClients);
    293                
    294                 allClients = calloc(clientCount, sizeof(ClientState *));
    295                 if (allClients == NULL) {
    296                         fprintf(stderr, "CFLocalServer: Could not clean up clients because we couldn't allocate memory.\n");
    297                 } else {
    298                         CFSetGetValues(gClients, (const void **) allClients);
    299                        
    300                         for (clientIndex = 0; clientIndex < clientCount; clientIndex++) {
    301                                 fprintf(stderr, "CFLocalServer: Client %p killed because we're quitting.\n", allClients[clientIndex]);
    302                                
    303                                 ClientDestroy( allClients[clientIndex] );
    304                         }
    305                 }
    306                
    307                 free(allClients);
    308                
    309                 CFRelease(gClients);
    310                 gClients = NULL;
    311     }
    312 }
    313 
    314 static int ClientCreate(int clientSockFD, ClientState **clientPtr)
    315 // Creates a new client that communicates over clientSockFD.
    316 // If clientPtr is not NULL, it returns a pointer to the
    317 // client state record in *clientPtr.
    318 //
    319 // clientSockFD must be a valid file descriptor referencing a
    320 // socket that's connected to the client
    321 // On input, if clientPtr is not NULL, *clientPtr must be NULL
    322 // Returns an errno-style error code
    323 // On success, if clientPtr is not NULL, *clientPtr will not be NULL
    324 // On success, clientSockFD is owned by the new client; the caller
    325 // need not close it
    326 // On error, if clientPtr is not NULL, *clientPtr will be NULL
    327 // On error, clientSockFD will have been closed.
    328 //
    329 // IMPORTANT:
    330 // Regardless of whether this routine succeeds or fails, it assumes
    331 // responsibility for clientSockFD.  The caller is never required to
    332 // close it.
    333 {
    334     int             err;
    335     int             junk;
    336     ClientState *   client;
    337    
    338     assert( (clientPtr == NULL) || (*clientPtr == NULL) );
    339    
    340     assert(gClients != NULL);
    341    
    342     // Create the client state record.
    343    
    344     err = 0;
    345     client = (ClientState *) calloc(1, sizeof(*client));
    346     if (client == NULL) {
    347         err = ENOMEM;
    348     }
    349        
    350     // Fill in the easy fields.  This also prepares us for the clean up
    351     // on failure.
    352    
    353     if (err == 0) {
    354         client->fMagic = kClientStateMagic;
    355                
    356         // For clean up to work properly, we must make sure that, if
    357         // the connection record is allocated successfully, we always
    358         // set fSockFD to the incoming clientSockFD.
    359                
    360         client->fSockFD = clientSockFD;
    361        
    362         client->fBufferedData = CFDataCreateMutable(NULL, 0);
    363         client->fPendingSends = CFArrayCreateMutable(NULL, 0, NULL);
    364                
    365         if ( (client->fBufferedData == NULL) || (client->fPendingSends == NULL) ) {
    366             err = ENOMEM;
    367         }
    368     }
    369    
    370     // Make the socket non-blocking.  We need to do this because
    371     // otherwise ClientSendPending can get stuck in a write.
    372    
    373     if (err == 0) {
    374         err = MoreUNIXSetNonBlocking(client->fSockFD);
    375     }
    376    
    377     // Wrap the socket in a CFSocket, and create and install the run loop source.
    378    
    379     if (err == 0) {
    380         CFSocketContext context;
    381        
    382         memset(&context, 0, sizeof(context));
    383         context.info = client;
    384        
    385         client->fSockCF = CFSocketCreateWithNative(
    386                                                                                                    NULL,
    387                                                                                                    (CFSocketNativeHandle) client->fSockFD,
    388                                                                                                    kCFSocketDataCallBack + kCFSocketWriteCallBack,
    389                                                                                                    ClientEvent,
    390                                                                                                    &context
    391                                                                                                    );
    392         if (client->fSockCF == NULL) {
    393             err = EINVAL;
    394         }
    395     }
    396        
    397     if (err == 0) {
    398         client->fRunLoopSource = CFSocketCreateRunLoopSource(NULL, client->fSockCF, 0);
    399         if (client->fRunLoopSource == NULL) {
    400             err = EINVAL;
    401         }
    402     }
    403     if (err == 0) {
    404         CFRunLoopAddSource( CFRunLoopGetCurrent(), client->fRunLoopSource, kCFRunLoopDefaultMode);
    405        
    406         assert( ! CFSetContainsValue(gClients, client) );
    407        
    408         // It's all good.  Record that this client exists.
    409        
    410         CFSetAddValue(gClients, client);
    411     }
    412    
    413     // Clean up.
    414    
    415     if (err != 0) {
    416         fprintf(stderr, "ClientCreate: Error %d creating client.\n", err);
    417                
    418         // If client is NULL, we couldn't allocate a client record, therefore
    419         // we had nowhere to record clientSockFD, therefore ClientDestroy won't
    420         // clean it up.  Thus, we have to do it ourselves.
    421        
    422         if (client == NULL) {
    423             junk = close(clientSockFD);
    424             assert(junk == 0);
    425         } else {
    426             ClientDestroy(client);
    427         }
    428         client = NULL;
    429     }
    430     if (clientPtr != NULL) {
    431         *clientPtr = client;
    432     }
    433    
    434     assert( (clientPtr == NULL) || ((err == 0) == (*clientPtr != NULL)) );
    435        
    436     return err;
    437 }
    438 
    439 static void ClientDestroy(ClientState *client)
    440 // Destroys a client.  This is called in a number of different circumstances,
    441 // but these basically boil down to:
    442 //
    443 // a) if ClientCreate fails, it's called to destroy the partially-created client,
    444 // b) if some sort of communications error happens, it's called to destroy the
    445 //    client,
    446 // c) if the client sends us a goodbye packet, this is called to destroy the client, and
    447 // d) on quit, all clients are destroyed.
    448 {
    449     int     junk;
    450    
    451     assert(client != NULL);
    452     assert(client->fMagic == kClientStateMagic);
    453        
    454     // This following assert is NOT true.  If the client dies before it
    455     // gets fully started (that is, we get an error halfway through
    456     // ClientCreate), ClientDestroy is called to tidy up the mess but
    457     // the client hasn't been added into gClients yet.
    458    
    459     // assert( CFSetContainsValue(gClients, client) );
    460    
    461     // Remove the client our record of existant clients.
    462    
    463     CFSetRemoveValue(gClients, client);
    464    
    465     // Clean up the runloop source and CFSocket.
    466    
    467     if (client->fRunLoopSource != NULL) {
    468         CFRunLoopSourceInvalidate(client->fRunLoopSource);
    469        
    470         CFRelease(client->fRunLoopSource);
    471     }
    472     if (client->fSockCF != NULL) {
    473         CFSocketInvalidate(client->fSockCF);
    474        
    475         CFRelease(client->fSockCF);
    476     }
    477    
    478     // Close the socket itself, but only if we don't have a corresponding
    479     // CFSocket; if a CFSocket was created, it takes over responsibility
    480     // for closing the socket.
    481    
    482     if ( (client->fSockFD != -1) && (client->fSockCF == NULL) ) {
    483         junk = close(client->fSockFD);
    484         assert(junk == 0);
    485     }
    486    
    487     // Free any packets waiting to go out to this client.
    488    
    489     if (client->fPendingSends != NULL) {
    490         CFIndex index;
    491         CFIndex count;
    492                
    493         count = CFArrayGetCount(client->fPendingSends);
    494         for (index = 0; index < count; index++) {
    495             free( (void *) CFArrayGetValueAtIndex(client->fPendingSends, index) );
    496         }
    497         CFRelease(client->fPendingSends);
    498     }
    499    
    500     // Free any buffered data from this client.
    501    
    502     if (client->fBufferedData != NULL) {
    503         CFRelease(client->fBufferedData);
    504     }
    505    
    506     // Free the client state record itself.
    507    
    508     client->fMagic = 'FRE!';
    509     free(client);
    510 }
    511 
    512 #pragma mark Send
    513 
    514 static int ClientSendPending(ClientState *client)
    515 // This routine attempts to send any packets that are queued in the fSendPending
    516 // array.  It is somewhat complex.  There are three possible final results.
    517 //
    518 // o It successfully sends all packets in the queue.  In this case it returns 0.
    519 //
    520 // o The write side of the socket is full (flow controlled).  In this case the
    521 //   function enables the socket write callback (kCFSocketWriteCallBack, using
    522 //   CFSocketEnableCallBacks) and returns 0.  When socket buffer empties a little,
    523 //   CFSocket will send us the kCFSocketWriteCallBack event and we'll resume sending.
    524 //
    525 // o It fails for some other reasons (for example, the other end of the socket has
    526 //   been closed, causing an EPIPE).  In this case it returns an errno-style error
    527 //   indicating the failure.  The caller typically responds by destroying the client.
    528 //
    529 // This whole process is further complicated by the possibilty that the socket
    530 // buffer might have enough space for half a packet.  In this case you'll get a
    531 // short write, that is, write will return a positive number less than its nbytes
    532 // parameter.  To handle this case we record the offset into the packet of the first
    533 // byte of unwritten data.  When we go to send a packet, we always send from there.
    534 // When write accepts some data, we bump the offset by that amount.  If that
    535 // completes the send of the packet, we start on next packet, resetting the offset
    536 // back to 0.
    537 {
    538     int                     err;
    539     Boolean                 done;
    540     const PacketHeader *    thisPacket;
    541     ssize_t                 bytesWritten;
    542    
    543     err = 0;
    544    
    545     // Keep going until we've sent all pending packets for this client.
    546    
    547     while ( (err == 0) && (CFArrayGetCount(client->fPendingSends) != 0) ) {
    548         thisPacket = (const PacketHeader *) CFArrayGetValueAtIndex(client->fPendingSends, 0);
    549        
    550         // Try to send this packet by writing it to the socket.
    551        
    552         done = false;
    553         do {
    554             bytesWritten = write(
    555                                                                  client->fSockFD,
    556                                                                  ((char *) thisPacket) + client->fPendingSendOffset,
    557                                                                  thisPacket->fSize - client->fPendingSendOffset
    558                                                                  );
    559            
    560             if (bytesWritten > 0) {
    561                 // We're written some bytes.  Adjust fPendingSendOffset by
    562                 // that amount and see if that completes the packet.
    563                
    564                 client->fPendingSendOffset += bytesWritten;
    565                
    566                 if (client->fPendingSendOffset == thisPacket->fSize) {
    567                     // Packet complete.  Delete it from the head of the
    568                     // send list, reset offset back to 0, and let's go
    569                     // deal with the next packet.
    570                    
    571                     CFArrayRemoveValueAtIndex(client->fPendingSends, 0);
    572                     free( (void *) thisPacket);
    573                     client->fPendingSendOffset = 0;
    574                     done = true;
    575                 } else {
    576                     // Packet still not fully sent.  The send offset has already
    577                     // been updated, so we just loop to try again.
    578                 }
    579             } else if (bytesWritten == -1) {
    580                 // We got some sort of error.
    581                
    582                 err = errno;
    583                 switch (err) {
    584                     case EINTR:
    585                         // Interrupted.  Do nothing, so we loop and retry this
    586                         // send immediately.
    587                                                
    588                         err = 0;
    589                         break;
    590                     case EAGAIN:
    591                         // Flow controlled.  Break out of the loop with an EAGAIN
    592                         // error; we try again when space becomes available.
    593                                                
    594                         fprintf(stderr, "ClientSendPending: Client %p write-side flow control.\n", client);
    595                        
    596                         // Tell the CFSocket that we now /really/ need to be told about
    597                         // write space becoming available.  Without this we'll never
    598                         // recover if the client stops accepting messages temporarily
    599                         // (which causes the socket buffer to fill up and us to get an
    600                         // EAGAIN) and then starts accepting messages again.  At that
    601                         // point the client will drain the socket buffer, but we'll never
    602                         // hear about it because CFSocket doesn't know that we care
    603                         // about write space.  With this call CFSocket knows that we
    604                         // care, and will send us an kCFSocketWriteCallBack event if
    605                         // space becomes available in the socket buffer.
    606                        
    607                         CFSocketEnableCallBacks(client->fSockCF, kCFSocketWriteCallBack);
    608                         break;
    609                     default:
    610                         // Errored.  Our response is typically draconian:
    611                         // we return the error to our caller, which then kills the client
    612                         // completely.
    613                                                
    614                         fprintf(stderr, "ClientSendPending: Client %p killed because of send error (%d).\n", client, err);
    615                         break;
    616                 }
    617             } else {
    618                 assert(false);
    619             }
    620         } while ( (err == 0) && ! done );
    621     }
    622    
    623     // As far as the caller is concerned, write-side flow control is not an error.
    624    
    625     if (err == EAGAIN) {
    626         err = 0;
    627     }
    628    
    629     return err;
    630 }
    631 
    632 static Boolean ClientSend(ClientState *client, const PacketHeader *packet)
    633 // Called in various places to send a packet to a client. 
    634 // This adds it to the send queue and then calls ClientSendPending
    635 // to attempt a send.
    636 {
    637     Boolean         result;
    638     PacketHeader *  copiedPacket;
    639    
    640     assert(client != NULL);
    641     assert(packet != NULL);
    642     assert(packet->fSize >= sizeof(PacketHeader));
    643        
    644     // If we've buffered kClientMaximumPendingSends already, the client is
    645     // just not reading them.  To avoid us consuming all of our memory buffering
    646     // packets for a deaf client, we just kill the client.
    647    
    648     result = true;
    649     if ( CFArrayGetCount(client->fPendingSends) >= kClientMaximumPendingSends ) {
    650         fprintf(stderr, "ClientSend: Client %p killed because of too many outstanding sends.\n", client);
    651        
    652         result = false;
    653     }
    654        
    655     // Copy the packet data, append that copy to the send queue, and then
    656     // give it a kick.
    657     //
    658     // The memory allocated here will be freed when the packet is succesfully
    659     // sent (SendPending), or the client is destroy.
    660    
    661     if (result) {
    662         copiedPacket = (PacketHeader *) malloc(packet->fSize);
    663         result = (copiedPacket != NULL);
    664     }
    665     if (result) {
    666         memcpy(copiedPacket, packet, packet->fSize);
    667        
    668         CFArrayAppendValue(client->fPendingSends, copiedPacket);
    669        
    670         result = ( ClientSendPending(client) == 0 );
    671     }
    672        
    673     return result;
    674 }
    675 
    676 static Boolean ClientSendReply(ClientState *client, const PacketHeader *request, int errNum)
    677 // Send an RPC reply packet to the client.  You must supply request
    678 // because it forms the basis of many of the fields in the reply.
    679 // You also have to supply errNum, which is an errno-style error
    680 // indicating the fate of the request.
    681 {
    682     PacketReply     response;
    683        
    684     assert(client  != NULL);
    685     assert(request != NULL);
    686        
    687     InitPacketHeader(&response.fHeader, kPacketTypeReply, sizeof(response), false);
    688     // Copy the ID from the request packet, overriding the fID set by InitPacketHeader.
    689     response.fHeader.fID    = request->fID;
    690     response.fErr = errNum;
    691        
    692     return ClientSend(client, &response.fHeader);
    693 }
    694 
    695 static void ClientGotSpace(ClientState *client)
    696 // This routine is called by ClientEvent when it receives the kCFSocketWriteCallBack
    697 // event, indicating that there is space to write in the client's socket buffer.
    698 // It calls ClientSendPending to process any packets that are waiting to be sent. 
    699 // In most cases this does nothing because the client send queue is empty.  However,
    700 // if the client goes deaf, so the socket buffer becomes write-side flow controlled,
    701 // packets can back up in the send queue.  When the client starts receiving packets
    702 // again, space becomes available in the socket buffer and CFSocket sends us the
    703 // kCFSocketWriteCallBack.  We respond to that by resuming our sends.
    704 {
    705     int             err;
    706        
    707         assert(client != NULL);
    708    
    709     fprintf(stderr, "ClientGotSpace: Client %p lifted write-side flow control.\n", client);
    710        
    711     err = ClientSendPending(client);
    712        
    713         // If the sending failed for any reason (except flow control, for which
    714         // ClientSendPending mutates the EAGAIN status to a 0) we kill the client.
    715        
    716     if (err != 0) {
    717         ClientDestroy(client);
    718     }
    719 }
    720 
    721 #pragma mark Receive
    722 
    723 // The receive engine is based around ClientGotData, which is the routine that gets
    724 // called when new data arrives, and a variety of packet handlers for processing
    725 // specific types of packets and that all have the same form.
    726 //
    727 // A packet handle routine takes two parameters, the client and the packet, neither
    728 // of which can be NULL, and does the work to process that packet.  This typically
    729 // involves checking that the packet is valid, doing the job requested by the packet,
    730 // and then, if the packet is for an RPC, sending the reply.
    731 //
    732 // If the packet handler returns false, the caller (ClientGotData) assumes that
    733 // something was seriously wrong with the packet and kills the connection to the
    734 // client.  A packet handler typically does this if the packet itself is malformed;
    735 // if the job requested by the packet can't be done (for example, there might not
    736 // be enough memory), the packet handler wouldn't return false but would, instead,
    737 // send an error status back to the client in the RPC reply.
    738 
    739 static Boolean ClientGoodbye(ClientState *client, PacketGoodbye *packet)
    740 // A packet handler for the Goodbye packet.  See the large comment above for
    741 // a discussion of the general form of a packet handler.
    742 //
    743 // A Goodbye packet is sent by the client to indicate to us that it's closing
    744 // its end of the connection.
    745 {
    746     Boolean     result;
    747    
    748     assert(client != NULL);
    749     assert(packet != NULL);
    750    
    751     result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketGoodbye));
    752     if ( result ) {
    753         result = ClientCheckPacketID(client, &packet->fHeader, kPacketIDNone);
    754     }
    755    
    756     if (result) {
    757         // During reliability print all of the goodbyes proved to be too verbose,
    758         // so I've disabled it for now.
    759        
    760         if (false) {
    761             fprintf(stderr, "%p: Goodbye (%.*s).\n", client, (int) sizeof(packet->fMessage), packet->fMessage);
    762         }
    763        
    764         // Unlike most packet handlers, we return false on success.  This is because
    765                 // the Goodbye packet tells us that the client has gone away, and thus we
    766                 // need to kill the client.  It turns out that returning false does the job
    767                 // without us having to write any special code.
    768        
    769         result = false;
    770     }
    771    
    772     return result;
    773 }
    774 
    775 static Boolean ClientNOP(ClientState *client, PacketNOP *packet)
    776 // A packet handler for the NOP packet.  See the large comment above for
    777 // a discussion of the general form of a packet handler.
    778 //
    779 // A NOP RPC does nothing; it's used to test client/server connection.
    780 {
    781     Boolean     result;
    782    
    783     result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketNOP));
    784    
    785     if (result) {
    786         fprintf(stderr, "%p: NOP\n", client);
    787        
    788         result = ClientSendReply(client, &packet->fHeader, 0);
    789     }
    790    
    791     return result;
    792 }
    793 
    794 static Boolean ClientWhisper(ClientState *client, PacketWhisper *packet)
    795 // A packet handler for the Whisper packet.  See the large comment above for
    796 // a discussion of the general form of a packet handler.
    797 //
    798 // A Whisper RPC causes the server to print the associated message.
    799 {
    800     Boolean result;
    801    
    802     result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketWhisper));
    803     if (result) {
    804         fprintf(stderr, "%p: Whisper \"%.*s\"\n", client, (int) sizeof(packet->fMessage), packet->fMessage);
    805                
    806         result = ClientSendReply(client, &packet->fHeader, 0);
    807     }
    808    
    809     return result;
    810 }
    811 
    812 static Boolean ClientShout(ClientState *client, PacketShout *packet)
    813 // A packet handler for the Shout packet.  See the large comment above for
    814 // a discussion of the general form of a packet handler.
    815 //
    816 // A Shout packet causes the server to echo the message (in the form
    817 // of a Shout packet) to every client that has registered as a listener.
    818 {
    819     Boolean     result;
    820     Boolean     sendResult;
    821    
    822     result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketShout));
    823     if (result) {
    824         result = ClientCheckPacketID(client, &packet->fHeader, kPacketIDNone);
    825     }
    826        
    827         // The Shout packet is good.  Let's echo it to each listener.
    828        
    829     if (result) {
    830         ClientState  ** allClients;
    831         CFIndex         clientCount;
    832         CFIndex         clientIndex;
    833                
    834         fprintf(stderr, "%p: Shout   \"%.*s\"\n", client, (int) sizeof(packet->fMessage), packet->fMessage);
    835                
    836                 NSString * shout = [NSString stringWithCString:packet->fMessage encoding:NSUTF8StringEncoding];
    837                 NSLog(@"CLIENT SHOUT BEING CALLED (YAAAY!!) : %@" , shout);
    838        
    839         // We make a snapshot of the client list because clients might disappear
    840         // as we talk to them.  That is, the act of talking to the client might
    841         // cause us to notice that the client is dead.
    842        
    843         clientCount = CFSetGetCount(gClients);
    844        
    845         allClients = calloc(clientCount, sizeof(ClientState *));
    846         if (allClients == NULL) {
    847             fprintf(stderr, "ClientShout: Shout from %p failed because we couldn't allocate memory.\n", client);
    848         } else {
    849             CFSetGetValues(gClients, (const void **) allClients);
    850            
    851                         // Iterate through the array of clients, sending the Shout packet to each.
    852                        
    853             for (clientIndex = 0; clientIndex < clientCount; clientIndex++) {
    854                 ClientState *  thisClient;
    855                
    856                 thisClient = allClients[clientIndex];
    857                 if (thisClient->fListening) {
    858                     sendResult = ClientSend(thisClient, &packet->fHeader);
    859                    
    860                     // Fun fun fun.  If we're sending to ourselves, we return the result
    861                     // of the send as our function result, which, if there's a failure,
    862                     // will trigger ClientGotData to clean us up.  OTOH, if we're sending
    863                     // to another client, we're responsible for cleaning up if there's
    864                     // a failure.
    865                    
    866                     if (thisClient == client) {
    867                         result = sendResult;
    868                     } else {
    869                         if ( ! sendResult ) {
    870                             fprintf(stderr, "ClientShout: Shout from %p to %p failed.\n", client, thisClient);
    871                            
    872                             ClientDestroy(thisClient);
    873                         }
    874                     }
    875                 }
    876             }
    877         }
    878                
    879         free(allClients);
    880     }
    881     return result;
    882 }
    883 
    884 static Boolean ClientListen(ClientState *client, PacketListen *packet)
    885 // A packet handler for the Listen packet.  See the large comment above for
    886 // a discussion of the general form of a packet handler.
    887 //
    888 // A Listen RPC tells the server that the client wants to hear about shouted
    889 // messages.
    890 {
    891     Boolean     result;
    892    
    893     result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketListen));
    894     if (result) {
    895         if (client->fListening) {
    896             fprintf(stderr, "ClientListen: Redundant Listen from %p.\n", client);
    897         } else {
    898             fprintf(stderr, "%p: Listen\n", client);
    899         }
    900         client->fListening = true;
    901        
    902         result = ClientSendReply(client, &packet->fHeader, 0);
    903     }
    904    
    905     return result;
    906 }
    907 
    908 static Boolean ClientQuit(ClientState *client, PacketQuit *packet)
    909 // A packet handler for the Quit packet.  See the large comment above for
    910 // a discussion of the general form of a packet handler.
    911 //
    912 // A Quit RPC causes the server to quit.
    913 {
    914     Boolean     result;
    915    
    916     result = ClientCheckPacketSize(client, &packet->fHeader, sizeof(PacketQuit));
    917     if (result) {
    918         fprintf(stderr, "%p: Quit\n", client);
    919        
    920         // Stop the main event loop.
    921        
    922         CFRunLoopStop( CFRunLoopGetCurrent() );
    923        
    924         // This reply should go out immediately.  If the client, for some reason,
    925         // is flow controlled, it may not see the response.  But really, that's
    926         // the client's fault (-:
    927        
    928         result = ClientSendReply(client, &packet->fHeader, 0);
    929     }
    930    
    931     return result;
    932 }
    933 
    934 static void ClientGotData(ClientState *client, const void *data)
    935 // This routine is called by ClientEvent when it receives the kCFSocketDataCallBack
    936 // event, indicating that CFSocket has read data from the socket.  The routine
    937 // appends the data to the receive buffer (fBufferedData) and then looks through
    938 // the receive buffer for complete packets.  For each complete packet it finds,
    939 // it calls the packet handler (the various routines above) to process the packet
    940 // and then it deletes the packet from the front of the receive buffer.
    941 //
    942 // data is the data read for us by CFSocket.  It's actually a CFDataRef
    943 // but, because we're being called from a generic CFSocket event handler,
    944 // it's of type (const void *).  We have to do the cast here.
    945 {
    946     CFDataRef       newData;
    947        
    948         assert(client != NULL);
    949    
    950     newData = (CFDataRef) data;
    951     assert(newData != NULL);
    952     assert( CFGetTypeID(newData) == CFDataGetTypeID() );
    953        
    954     if ( CFDataGetLength(newData) == 0 ) {
    955         // A zero length data indicates the end of the data stream; the client is dead
    956                 // so we just go and remove our record of it.
    957        
    958         fprintf(stderr, "ClientGotData: Client %p died unexpectedly.\n", client);
    959        
    960         ClientDestroy(client);
    961     } else {
    962                
    963         // Append the new data to whatever data we have already buffered
    964         // (most likely nothing).
    965        
    966         CFDataAppendBytes(client->fBufferedData, CFDataGetBytePtr(newData), CFDataGetLength(newData));
    967        
    968         // Process packets until we run out of complete ones.
    969        
    970         do {
    971             PacketHeader *  thisPacket;
    972             Boolean         success;
    973            
    974             if ( CFDataGetLength(client->fBufferedData) < sizeof(PacketHeader) ) {
    975                 // Not enough data for the packet header; we're done.
    976                 break;
    977             }
    978            
    979             thisPacket = (PacketHeader *) CFDataGetBytePtr(client->fBufferedData);
    980            
    981             if ( thisPacket->fMagic != kPacketMagic ) {
    982                 fprintf(stderr, "ClientGotData: Client %p sent us a packet with bad magic (%.4s).\n", client, (char *) &thisPacket->fMagic);
    983                
    984                 ClientDestroy(client);
    985                 break;
    986             }
    987            
    988             if (thisPacket->fSize > kPacketMaximumSize) {
    989                 fprintf(stderr, "ClientGotData: Client %p sent us a packet that's just too big (%" PRIu32 ").\n", client, thisPacket->fSize);
    990                
    991                 ClientDestroy(client);
    992                 break;
    993             }
    994            
    995             if ( CFDataGetLength(client->fBufferedData) < thisPacket->fSize ) {
    996                 // Not enough data for the packet body; we're done.
    997                 break;
    998             }
    999                        
    1000                         // Dispatch to the appropriate packet handler.
    1001            
    1002             switch (thisPacket->fType) {
    1003                 case kPacketTypeGoodbye:
    1004                     success = ClientGoodbye(client, (PacketGoodbye *) thisPacket);
    1005                     break;
    1006                 case kPacketTypeNOP:
    1007                     success = ClientNOP(client, (PacketNOP *) thisPacket);
    1008                     break;
    1009                 case kPacketTypeWhisper:
    1010                     success = ClientWhisper(client, (PacketWhisper *) thisPacket);
    1011                     break;
    1012                 case kPacketTypeShout:
    1013                     success = ClientShout(client, (PacketShout *) thisPacket);
    1014                     break;
    1015                 case kPacketTypeListen:
    1016                     success = ClientListen(client, (PacketListen *) thisPacket);
    1017                     break;
    1018                 case kPacketTypeQuit:
    1019                     success = ClientQuit(client, (PacketQuit *) thisPacket);
    1020                     break;
    1021                 default:
    1022                     fprintf(stderr, "ClientGotData: Client %p sent us a packet with an unexpected type (%.4s).\n", client, (char *) &thisPacket->fType);
    1023                    
    1024                     success = false;
    1025                     break;               
    1026             }
    1027             if ( ! success ) {
    1028                 ClientDestroy(client);
    1029                 break;
    1030             }
    1031            
    1032             // Delete this packet from the front of our packet buffer.
    1033            
    1034             CFDataDeleteBytes(client->fBufferedData, CFRangeMake(0, thisPacket->fSize));
    1035            
    1036         } while (true);
    1037     }
    1038 }
    1039 
    1040 /////////////////////////////////////////////////////////////////
    1041 #pragma mark ***** Debug Infrastructure
    1042 
    1043 static void ClientPrintInfo(const void *value, void *context)
    1044 // Called by PrintServerState to print the state of a particular
    1045 // client.  Actually passed as a callback to CFSetApplyFunction,
    1046 // which is why the value parameter is a (const void *) rather
    1047 // than a (ClientState *).  context is not used in this... context (-;
    1048 {
    1049 #pragma unused(context)
    1050     ClientState *  client;
    1051     static const char * kBoolToStr[2] = { "false", "true" };
    1052        
    1053     client = (ClientState *) value;
    1054     assert(client != NULL);
    1055     assert(client->fMagic == kClientStateMagic);
    1056    
    1057     fprintf(stderr, "  Client %p:\n", client);
    1058     fprintf(stderr, "    fSockFD            = %d\n", client->fSockFD);
    1059     fprintf(stderr, "    fSockCF            = %p\n", client->fSockCF);
    1060     fprintf(stderr, "    fRunLoopSource     = %p\n", client->fRunLoopSource);
    1061     fprintf(stderr, "    fBufferedData      = %p (count: %ld)\n", client->fBufferedData, CFDataGetLength(client->fBufferedData));
    1062     fprintf(stderr, "    fPendingSends      = %p (count: %ld)\n", client->fPendingSends, CFArrayGetCount(client->fPendingSends));
    1063     fprintf(stderr, "    fPendingSendOffset = %zd\n", client->fPendingSendOffset);
    1064     fprintf(stderr, "    fListening         = %s\n", kBoolToStr[client->fListening]);   
    1065 }
    1066 
    1067 static void PrintServerState(void)
    1068 // Called in response to a SIGINFO.  This prints a bunch of state
    1069 // information about the server.  Note that it is not called from a
    1070 // signal handler directly, rather from SignalRunLoopCallback which
    1071 // is a runloop callback.  So we can do all sorts of things that
    1072 // aren't safe in a true signal handler.
    1073 {
    1074     fprintf(stderr, "CFLocalServer State\n");
    1075     fprintf(stderr, "-------------------\n");
    1076     if ( CFSetGetCount(gClients) == 0) {
    1077         fprintf(stderr, "Clients: none\n");
    1078     } else {
    1079         fprintf(stderr, "Clients:\n");
    1080         CFSetApplyFunction(gClients, ClientPrintInfo, NULL);
    1081     }
    1082     fprintf(stderr, "\n");
    1083        
    1084     DebugPrintDescriptorTable();
    1085 }
    1086 
    1087 /////////////////////////////////////////////////////////////////
    1088 #pragma mark ***** Server Framework
    1089 
    1090 static void ListeningSocketAcceptCallback(
    1091                                                                                   CFSocketRef             s,
    1092                                                                                   CFSocketCallBackType    type,
    1093                                                                                   CFDataRef               address,
    1094                                                                                   const void *            data,
    1095                                                                                   void *                  info
    1096 )
    1097 // This is the CFSocket event callback for the listening socket.  For a
    1098 // description of the parameters, see the CFSocket documentation.
    1099 //
    1100 // CFSocket calls this routine when it has accepted a new connection on
    1101 // the socket.  in this case data is a pointer to the newly created
    1102 // file descriptor that describes the new connection.  This routine
    1103 // responds by calling into the client layer to create a new client.
    1104 {
    1105 #pragma unused(s)
    1106 #pragma unused(address)
    1107 #pragma unused(info)
    1108    
    1109     assert(type == kCFSocketAcceptCallBack);
    1110     assert(   (int *) data  != NULL );
    1111     assert( (*(int *) data) != -1 );
    1112        
    1113     (void) ClientCreate( (*(int *) data), NULL );
    1114    
    1115     // If ClientCreate fails, it cleans up after itself, including
    1116     // closing the newly created client socket.  It's even printed
    1117     // a happy message (well, an unhappy message).  So we do nothing
    1118         // on failure.
    1119 }
    1120 
    1121 static void SignalRunLoopCallback(const siginfo_t *sigInfo, void *refCon)
    1122 // This routine is called in response to a signal (SIGINT
    1123 // or SIGINFO).  It is not, however, a signal handler.  Rather,
    1124 // we orchestrate to have it called from the runloop (via
    1125 // the magic of InstallSignalToSocket).  It's purpose
    1126 // is to a) stop the server when the user types ^C (SIGINT), or
    1127 // b) print some information about the server (SIGINFO).
    1128 {
    1129 #pragma unused(sigInfo)
    1130 #pragma unused(refCon)
    1131    
    1132     switch (sigInfo->si_signo) {
    1133         case SIGINFO:
    1134                         // Respond to SIGINFO by printing some information about the server.
    1135                        
    1136             PrintServerState();
    1137             break;
    1138         case SIGINT:
    1139                         // Respond to SIGINT by stopping the server.
    1140                        
    1141                         // Stop the runloop.  Note that we can get a reference to the runloop by
    1142                         // calling CFRunLoopGetCurrent because this is called from the runloop.
    1143                        
    1144                         CFRunLoopStop( CFRunLoopGetCurrent() );
    1145                        
    1146                         // Print a bonus newline to ensure that the next command prompt isn't
    1147                         // printed on the same line as the echoed ^C.
    1148                        
    1149                         fprintf(stderr, "\n");
    1150             break;
    1151         default:
    1152             assert(false);
    1153             break;
    1154     }
    1155 }
    1156 
    1157 static int SafeBindUnixDomainSocket(int sockFD, const char *socketPath)
    1158 // This routine is called to safely bind the UNIX domain socket
    1159 // specified by sockFD to the path specificed by socketPath.  To avoid
    1160 // security problems, socketPath must point it to a sticky directory
    1161 // (such as "/var/tmp").  This allows us to create the socket with
    1162 // very specific permissions, without us having to worry about a malicious
    1163 // process switching stuff out from underneath us.
    1164 //
    1165 // For this test program, socketpath is "/var/tmp/com.apple.dts.CFLocalServer/Socket".
    1166 // The code calculates parentPath as ""/var/tmp/com.apple.dts.CFLocalServer"
    1167 // and grandParentPath as "/var/tmp".  Each ancestor has certain key attributes.
    1168 //
    1169 // o grandParentPath must a sticky directory.  Because it's sticky, we
    1170 //   can create a directory within it and know that either a) we created
    1171 //   the directory, and no one else can mess with it because it's sticky,
    1172 //   or b) the directory exists, in which case we can check it's owner
    1173 //   and permissions and, if they are set correctly, know that no one else
    1174 //   can mess with it.
    1175 //
    1176 // o When we create the parentPath directory within grandParentPath, we set its
    1177 //   permissions to make it readable by everyone (so everyone can connect to our
    1178 //   server) but writeable only by us (so that only we can create the listening
    1179 //   socket).  Because parentPath is set this way, we know that no one else
    1180 //   can modify it to produce a security problem.
    1181 //
    1182 // IMPORTANT:
    1183 // This routine is designed to protect against external attack, not against
    1184 // being called incorrectly.  It only does minimal checking of socketPath. 
    1185 // For example, if one of the components of socketPath was "..", the security
    1186 // checking done by this routine might be invalid.  Do not pass an untrusted
    1187 // socketPath to this routine.
    1188 {
    1189     int                 err;
    1190     char *              parentPath;
    1191     char *              grandParentPath;
    1192     char *              lastSlash;
    1193     struct stat         sb;
    1194     struct sockaddr_un  bindReq;
    1195     static const mode_t kRequiredParentMode = S_IRWXU | (S_IRGRP | S_IXGRP) | (S_IROTH | S_IXOTH); // rwxr-xr-x
    1196    
    1197     parentPath      = NULL;
    1198     grandParentPath = NULL;
    1199    
    1200     // sockaddr_un can only hold a very short path (it's 104 bytes long),
    1201     // so we check that limit right up front.  Note the use of >= in the
    1202     // check below: we fail if socketPath is exactly 104 chars long because
    1203     // that would leave no space for the trailing null character.  Looking at
    1204     // the kernel code, I don't think this is strictly necessary (in fact,
    1205     // it seems that the kernel code will handle much longer paths than sun_path,
    1206     // up to an overall sockaddr size ofSOCK_MAXADDRLEN), but I'm being
    1207     // paranoid.
    1208    
    1209     err = 0;
    1210     if (strlen(socketPath) >= sizeof(bindReq.sun_path)) {
    1211         err = EINVAL;
    1212     }
    1213    
    1214     // Construct parentPath and grandParent path by knocking path components
    1215     // off the end.
    1216    
    1217     if (err == 0) {
    1218         parentPath = strdup(socketPath);
    1219         if (parentPath == NULL) {
    1220             err = ENOMEM;
    1221         }
    1222     }
    1223     if (err == 0) {
    1224         lastSlash = strrchr(parentPath, '/');
    1225         if (lastSlash == NULL) {
    1226             fprintf(stderr, "SafeBindUnixDomainSocket: Can't get parent for path (%s).\n", socketPath);
    1227             err = EINVAL;
    1228         } else {
    1229             *lastSlash = 0;
    1230         }
    1231     }
    1232     if (err == 0) {
    1233         grandParentPath = strdup(parentPath);
    1234         if (grandParentPath == NULL) {
    1235             err = ENOMEM;
    1236         }
    1237     }
    1238     if (err == 0) {
    1239         lastSlash = strrchr(grandParentPath, '/');
    1240         if (lastSlash == NULL) {
    1241             fprintf(stderr, "SafeBindUnixDomainSocket: Can't get grandparent for path (%s).\n", socketPath);
    1242             err = EINVAL;
    1243         } else {
    1244             *lastSlash = 0;
    1245         }
    1246     }
    1247    
    1248     // Check that the parent directory is a sticky root-owned directory.  If the
    1249     // grandparent directory is sticky, we know that any items in that directory
    1250     // that are owned by us can't be substituted by anyone else (that is: deleted,
    1251     // moved or renamed, and then replaced by an attacker's item).
    1252    
    1253     if (err == 0) {       
    1254         err = stat(grandParentPath, &sb);
    1255         err = MoreUNIXErrno(err);
    1256     }
    1257        
    1258     if ( (err == 0) && ( ! (sb.st_mode & S_ISTXT) || (sb.st_uid != 0) ) ) {
    1259         fprintf(stderr, "SafeBindUnixDomainSocket: Grandparent directory (%s) is not a sticky root-owned directory.\n", grandParentPath);
    1260         err = EINVAL;
    1261     }
    1262    
    1263     // Create the parent directory.  Ignore an EEXIST error because of the
    1264     // next check.
    1265        
    1266     if (err == 0) {
    1267         err = mkdir(parentPath, kRequiredParentMode);
    1268         err = MoreUNIXErrno(err);
    1269        
    1270         if (err == EEXIST) {
    1271             err = 0;
    1272         }
    1273     }
    1274    
    1275     // Check that the parent directory is a directory, is owned by us, and
    1276     // has the right mode.  This ensures that no one except us can be monkeying
    1277     // with its contents.  And we know that no one can substitute a /different/
    1278     // directory underneath us because its parent (grandParentPath) is sticky.
    1279        
    1280     if (err == 0) {
    1281         err = stat(parentPath, &sb);
    1282         err = MoreUNIXErrno(err);
    1283     }
    1284     if ( (err == 0) && (sb.st_uid != geteuid()) ) {
    1285         fprintf(stderr, "SafeBindUnixDomainSocket: Parent (%s) is not owned by us.\n", parentPath);
    1286         err = EINVAL;
    1287     }
    1288     if ( (err == 0) && ! S_ISDIR(sb.st_mode) ) {
    1289         fprintf(stderr, "SafeBindUnixDomainSocket: Parent (%s) is not a directory.\n", parentPath);
    1290         err = EINVAL;
    1291     }
    1292     if ( (err == 0) && ( (sb.st_mode & ACCESSPERMS) != kRequiredParentMode ) ) {
    1293         fprintf(stderr, "SafeBindUnixDomainSocket: Parent (%s) has wrong permissions.\n", parentPath);
    1294         err = EINVAL;
    1295     }
    1296    
    1297     // If all is well, let's bind our socket.  This involves deleting any existing
    1298     // socket and recreating our own.  We know we can do this without worrying
    1299     // about substitution because only we have write access to the parent directory.
    1300        
    1301     if (err == 0) {
    1302         mode_t              oldUmask;
    1303                
    1304         // Temporarily set the umask to 0 (the default is 0022) so that the
    1305         // socket is created rwxrwxrwx.  This allows any user to connect to
    1306         // our socket.
    1307                
    1308         oldUmask = umask(0);
    1309                
    1310         // Delete any existing socket.  We delete the socket when we shut down,
    1311         // but, if we quit unexpectedly, it could've been left lying around.
    1312        
    1313         (void) unlink(socketPath);
    1314                
    1315         // Bind the socket, allowing other clients to connect.
    1316        
    1317         bindReq.sun_len    = sizeof(bindReq);
    1318         bindReq.sun_family = AF_UNIX;
    1319         strcpy(bindReq.sun_path, socketPath);
    1320                
    1321         err = bind(sockFD, (struct sockaddr *) &bindReq, SUN_LEN(&bindReq));
    1322         err = MoreUNIXErrno(err);
    1323                
    1324         (void) umask(oldUmask);
    1325     }
    1326        
    1327     free(parentPath);
    1328     free(grandParentPath);
    1329    
    1330     return err;
    1331 }
    1332 
    1333 static void PrintUsage(const char *argv0)
    1334 // Print the program's usage.  Given that it supports no arguments whatsoever,
    1335 // this is pretty simple.
    1336 {
    1337     const char *command;
    1338    
    1339     command = strrchr(argv0, '/');
    1340     if (command == NULL) {
    1341         command = argv0;
    1342     } else {
    1343         command += 1;
    1344     }
    1345     fprintf(stderr, "usage: %s\n", command);
    1346 }
    1347 
    1348 
    1349 #pragma mark -
    1350 @interface MPNotifications (Private)
    1351 -(void) socketConnected:(NSNotification *)notification;
    1352 -(void) readData:(NSNotification *)notification;
    1353 -(void) notifyWithData:(NSNotification *)notification;
    1354 -(BOOL) initBSDSocket;
    1355 -(void) startServerThread;
    1356 -(void) prepareServerThread;
    1357 -(void) stopServerThread;
    1358 @end
    135939
    136040
     
    141191                //NSLog(@"Dictionary is %@ ", [blockOptions description]);
    141292               
    1413                 hasSetFileDescriptor = NO;
    1414                
    1415                 if ([self initBSDSocket]) {
    1416                         //should I be using the closeDealloc version instead?
    1417                         acceptHandle = [[NSFileHandle alloc] initWithFileDescriptor:sd1];
    1418                         readHandle = [[NSFileHandle alloc] initWithFileDescriptor:sd1];
    1419                        
    1420        
    1421                        
    1422                         //It would be nice if I could somehow add the fileHandle in the HelperTool as the sender
    1423                         //this notification. That way I don't read stuff not intended for me.
    1424                         //Perhaps I should post a distributed notification to indicate initiation of
    1425                         //the asynchronous notification?
    1426                         [[NSNotificationCenter defaultCenter] addObserver:self
    1427                                                                                                          selector:@selector(socketConnected:)
    1428                                                                                                                  name:NSFileHandleConnectionAcceptedNotification
    1429                                                                                                            object:nil];
    1430                        
    1431                         //Posts the notification above after accepting a connection
    1432                         [acceptHandle acceptConnectionInBackgroundAndNotify];
    1433                 }
    1434                
    1435                
    143693        }
    143794        return self;
     
    1482139
    1483140
    1484 #pragma mark -
    1485 //Internal methods for IPC with helper tool
    1486 //This method should really run in a background separate
    1487 //thread so that it doesn't black ... I'll implement
    1488 //that after I get the basic functionality working
    1489 - (void) socketConnected:(NSNotification *) notification {
    1490        
    1491         [[NSNotificationCenter defaultCenter] addObserver:self
    1492                                                                                          selector:@selector(readData:)
    1493                                                                                                  name:NSFileHandleDataAvailableNotification
    1494                                                                                            object:nil];
    1495        
    1496         //Need to call this again since it is done only once in init
    1497         //and this is a singleton instance class
    1498         //acceptHandle posts the above notification
    1499         [acceptHandle acceptConnectionInBackgroundAndNotify];
    1500        
    1501        
    1502 }
    1503141
    1504 - (void) readData:(NSNotification *) notification {
    1505        
    1506         [[NSNotificationCenter defaultCenter] addObserver:self
    1507                                                                                          selector:@selector(notifyWithData:)
    1508                                                                                                  name:NSFileHandleReadCompletionNotification
    1509                                                                                            object:nil];
    1510        
    1511         //Once data is availabl we can call this method
    1512         //it posts the above notification on completion
    1513         [readHandle readInBackgroundAndNotify];
    1514 }
    1515 
    1516 - (void) notifyWithData:(NSNotification *) notification {
    1517         NSData * inData = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
    1518         NSString * inString = [[NSString alloc] initWithData:inData
    1519                                                                                                 encoding:NSUTF8StringEncoding];
    1520         //Just Log it for now
    1521         NSLog(@"Read in %@ from MPHelperTool", inString);
    1522         [inString release];
    1523        
    1524         //Once we have finished reading a stream of data and we are
    1525         //done logging ... we can start
    1526         //[acceptHandle acceptConnectionInBackgroundAndNotify];
    1527 }
    1528 
    1529 -(BOOL) initBSDSocket {
    1530         //BSD Socket Initialization (maybe I should put this in another method ?)
    1531         sd1 = -1;
    1532         serverFilePath = @"var/run/org.macports.framework.mpnotificationsServer";
    1533         //serverFilePath = [[NSBundle bundleForClass:[MPNotifications class]]
    1534         //                                        pathForResource:@"HelperToolServerFile" ofType:@"txt"];
    1535         sd1 = socket(AF_UNIX, SOCK_STREAM, 0);
    1536         if (sd1 < 0) {
    1537                 NSLog(@"socket() failed");
    1538         }
    1539        
    1540         memset( &serveraddr, 0, sizeof(serveraddr));
    1541         serveraddr.sun_family = AF_UNIX;
    1542         strcpy(serveraddr.sun_path,[serverFilePath cStringUsingEncoding:NSUTF8StringEncoding] );
    1543        
    1544         rc = bind(sd1, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));
    1545         if (rc < 0) {
    1546                 NSLog(@"bind() failed");
    1547         }
    1548         else {
    1549                 hasSetFileDescriptor = YES;
    1550         }
    1551         return hasSetFileDescriptor;
    1552 }
    1553 
    1554 
    1555 -(int) getServerFileDescriptor {
    1556         return sd1;
    1557 }
    1558 
    1559 
    1560 #pragma mark -
    1561 #pragma mark MPNotifications Server Code - II
    1562 
    1563 -(void) startServerThread {
    1564         NSAutoreleasePool * sPool = [[NSAutoreleasePool alloc] init];
    1565        
    1566         NSLog(@"INSIDE SERVER THREAD");
    1567        
    1568         //Configure runloop
    1569         int         err = 0;
    1570     int         listenerFD;
    1571     CFSocketRef listenerCF;
    1572     Boolean     didBind;
    1573    
    1574     didBind    = false;
    1575     listenerFD = -1;
    1576     listenerCF = NULL;
    1577        
    1578        
    1579        
    1580        
    1581         // Ignore SIGPIPE because it's a deeply annoying concept.  If you don't ignore
    1582         // SIGPIPE when writing to a UNIX domain socket whose far side has been closed
    1583         // will trigger a SIGPIPE, whose default action is to terminate the program.
    1584     if (err == 0) {
    1585         fprintf(stderr, "CFLocalServer: Starting up (pid: %ld).\n", (long) getpid());
    1586                 NSLog(@"CFLocalServer: Starting up (pid: %ld).\n", (long) getpid());
    1587         err = MoreUNIXIgnoreSIGPIPE();
    1588     }
    1589    
    1590         // Set up the signal handlers we are interested in.  In this case we redirect
    1591         // SIGINT and SIGINFO to our runloop.  If either of these signals occurs, we
    1592         // end up executing SignalRunLoopCallback.
    1593     if (err == 0) {
    1594         sigset_t    interestingSignals;
    1595         (void) sigemptyset(&interestingSignals);
    1596         (void) sigaddset(&interestingSignals, SIGINT);
    1597         (void) sigaddset(&interestingSignals, SIGINFO);
    1598        
    1599         err = InstallSignalToSocket(
    1600                                                                         &interestingSignals,
    1601                                                                         CFRunLoopGetCurrent(),
    1602                                                                         kCFRunLoopDefaultMode,
    1603                                                                         SignalRunLoopCallback,
    1604                                                                         NULL
    1605                                                                         );
    1606     }
    1607        
    1608         // Create the initial client set.
    1609     if (err == 0) {
    1610                 err = ClientInitialise();
    1611                 NSLog(@"Initilalizing Client");
    1612     }
    1613    
    1614         // Create our listening socket, bind it, and then wrap it in a CFSocket.
    1615     if (err == 0) {
    1616         listenerFD = socket(AF_UNIX, SOCK_STREAM, 0);
    1617         err = MoreUNIXErrno(listenerFD);
    1618                 NSLog(@"Creating Socket");
    1619     }
    1620     if (err == 0) {
    1621         err = SafeBindUnixDomainSocket(listenerFD, kServerSocketPath);
    1622         didBind = (err == 0);
    1623                 NSLog(@"Binding Socket %i", err);
    1624     }
    1625     if (err == 0) {
    1626         err = listen(listenerFD, 5);
    1627         err = MoreUNIXErrno(err);
    1628                 NSLog(@"Listening Socket");
    1629     }
    1630     if (err == 0) {
    1631         listenerCF = CFSocketCreateWithNative(
    1632                                                                                           NULL,
    1633                                                                                           (CFSocketNativeHandle) listenerFD,
    1634                                                                                           kCFSocketAcceptCallBack,
    1635                                                                                           ListeningSocketAcceptCallback,
    1636                                                                                           NULL);
    1637                
    1638                 NSLog(@"Creating Callbacks!");
    1639         if (listenerCF == NULL) {
    1640             err = EINVAL;
    1641         }
    1642     }
    1643    
    1644         // Schedule the listening socket on our runloop.
    1645         NSRunLoop * currentLoop = [NSRunLoop currentRunLoop];
    1646     if (err == 0) {
    1647         CFRunLoopSourceRef  rls;
    1648        
    1649         rls = CFSocketCreateRunLoopSource(NULL, listenerCF, 0);
    1650         if (rls == NULL) {
    1651             err = EINVAL;
    1652         } else {
    1653             CFRunLoopAddSource( [currentLoop getCFRunLoop], rls, kCFRunLoopDefaultMode);
    1654             NSLog(@"Adding Source to current runloop");
    1655                        
    1656             // We no longer need this source, so we just release it.
    1657             CFRelease(rls);
    1658         }
    1659     }
    1660        
    1661        
    1662         double resolution = 30.0;
    1663        
    1664         //Add input sources to my run loop
    1665         //terminateBackgroundThread is going to be set to NO before the privileged operation is called
    1666         //it will be set to YES after the privileged operation finishes execution. So I guess I need
    1667         //accessor methods?
    1668         NSThread * cThread = [NSThread currentThread];
    1669         NSLog(@"RUNNING RUN LOOP with thread %@" , [cThread threadDictionary]);
    1670        
    1671         do {
    1672                 NSDate * nextDate = [NSDate dateWithTimeIntervalSinceNow:resolution];
    1673                 [currentLoop runMode:NSDefaultRunLoopMode beforeDate:nextDate];
    1674                
    1675                 //might add some code here to clean up and recreate autoreleasepool
    1676         }       while (terminateBackgroundThread == NO);
    1677        
    1678         [sPool release];
    1679 }
    1680 
    1681 -(void) prepareServerThread {
    1682         terminateBackgroundThread == NO;
    1683 }
    1684 
    1685 -(void) stopServerThread {
    1686         terminateBackgroundThread == YES;
    1687 }
    1688142@end
Note: See TracChangeset for help on using the changeset viewer.