2  * IPC.c - System Starter IPC routines 
   3  * Wilfredo Sanchez  | wsanchez@opensource.apple.com 
   4  * Kevin Van Vechten | kevinvv@uclink4.berkeley.edu 
   7  * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. 
   9  * @APPLE_APACHE_LICENSE_HEADER_START@ 
  11  * Licensed under the Apache License, Version 2.0 (the "License"); 
  12  * you may not use this file except in compliance with the License. 
  13  * You may obtain a copy of the License at 
  15  *     http://www.apache.org/licenses/LICENSE-2.0 
  17  * Unless required by applicable law or agreed to in writing, software 
  18  * distributed under the License is distributed on an "AS IS" BASIS, 
  19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  20  * See the License for the specific language governing permissions and 
  21  * limitations under the License. 
  23  * @APPLE_APACHE_LICENSE_HEADER_END@ 
  27 #include <mach/mach.h> 
  28 #include <mach/message.h> 
  29 #include <mach/mach_error.h> 
  30 #include <CoreFoundation/CoreFoundation.h> 
  33 #include "bootstrap_public.h" 
  35 #include "StartupItems.h" 
  36 #include "SystemStarter.h" 
  37 #include "SystemStarterIPC.h" 
  39 /* Structure to pass StartupContext and anItem to the termination handler. */ 
  40 typedef struct TerminationContextStorage 
{ 
  41         StartupContext  aStartupContext
; 
  42         CFMutableDictionaryRef anItem
; 
  43 }              *TerminationContext
; 
  46  * A CFMachPort invalidation callback that records the termination of 
  47  * a startup item task.  Stops the current run loop to give system_starter 
  48  * another go at running items. 
  51 startupItemTerminated(CFMachPortRef aMachPort
, void *anInfo
) 
  53         TerminationContext aTerminationContext 
= (TerminationContext
) anInfo
; 
  56                 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(aMachPort
)); 
  58         if (aTerminationContext 
&& aTerminationContext
->anItem
) { 
  62                 CFMutableDictionaryRef anItem 
= aTerminationContext
->anItem
; 
  63                 StartupContext  aStartupContext 
= aTerminationContext
->aStartupContext
; 
  65                 /* Get the exit status */ 
  67                         aPID 
= StartupItemGetPID(anItem
); 
  69                                 rPID 
= waitpid(aPID
, &aStatus
, 0); 
  71                 if (aStartupContext
) { 
  72                         --aStartupContext
->aRunningCount
; 
  74                         /* Record the item's status */ 
  75                         if (aStartupContext
->aStatusDict
) { 
  76                                 StartupItemExit(aStartupContext
->aStatusDict
, anItem
, (WIFEXITED(aStatus
) && WEXITSTATUS(aStatus
) == 0)); 
  78                                         CF_syslog(LOG_WARNING
, CFSTR("%@ (%d) did not complete successfully"), CFDictionaryGetValue(anItem
, CFSTR("Description")), aPID
); 
  80                                         CF_syslog(LOG_DEBUG
, CFSTR("Finished %@ (%d)"), CFDictionaryGetValue(anItem
, CFSTR("Description")), aPID
); 
  84                          * If the item failed to start, then add it to the 
  87                         if (WEXITSTATUS(aStatus
) || WTERMSIG(aStatus
) || WCOREDUMP(aStatus
)) { 
  88                                 CFDictionarySetValue(anItem
, kErrorKey
, kErrorReturnNonZero
); 
  89                                 AddItemToFailedList(aStartupContext
, anItem
); 
  92                          * Remove the item from the waiting list regardless 
  93                          * if it was successful or it failed. 
  95                         RemoveItemFromWaitingList(aStartupContext
, anItem
); 
  98         if (aTerminationContext
) 
  99                 free(aTerminationContext
); 
 103 MonitorStartupItem(StartupContext aStartupContext
, CFMutableDictionaryRef anItem
) 
 105         pid_t           aPID 
= StartupItemGetPID(anItem
); 
 106         if (anItem 
&& aPID 
> 0) { 
 108                 kern_return_t   aResult
; 
 109                 CFMachPortContext aContext
; 
 110                 CFMachPortRef   aMachPort
; 
 111                 CFRunLoopSourceRef aSource
; 
 112                 TerminationContext aTerminationContext 
= (TerminationContext
) malloc(sizeof(struct TerminationContextStorage
)); 
 114                 aTerminationContext
->aStartupContext 
= aStartupContext
; 
 115                 aTerminationContext
->anItem 
= anItem
; 
 117                 aContext
.version 
= 0; 
 118                 aContext
.info 
= aTerminationContext
; 
 120                 aContext
.release 
= 0; 
 122                 if ((aResult 
= task_name_for_pid(mach_task_self(), aPID
, &aPort
)) != KERN_SUCCESS
) 
 125                 if (!(aMachPort 
= CFMachPortCreateWithPort(NULL
, aPort
, NULL
, &aContext
, NULL
))) 
 128                 if (!(aSource 
= CFMachPortCreateRunLoopSource(NULL
, aMachPort
, 0))) { 
 129                         CFRelease(aMachPort
); 
 132                 CFMachPortSetInvalidationCallBack(aMachPort
, startupItemTerminated
); 
 133                 CFRunLoopAddSource(CFRunLoopGetCurrent(), aSource
, kCFRunLoopCommonModes
); 
 135                 CFRelease(aMachPort
); 
 139                  * The assumption is something failed, the task already 
 142                 startupItemTerminated(NULL
, aTerminationContext
); 
 147  * Returns a reference to an item based on tokens passed in an IPC message. 
 148  * This is useful for figuring out which item the message came from. 
 150  * Currently two tokens are supported: 
 151  * kIPCProcessIDKey - the pid of the running startup script 
 152  * kIPCServiceNameKey - a name of a service that the item provides.  This 
 153  *      takes precedence over the pid key when both are present. 
 155 static CFMutableDictionaryRef 
 
 156 itemFromIPCMessage(StartupContext aStartupContext
, CFDictionaryRef anIPCMessage
) 
 158         CFMutableDictionaryRef anItem 
= NULL
; 
 160         if (aStartupContext 
&& anIPCMessage
) { 
 161                 CFStringRef     aServiceName 
= CFDictionaryGetValue(anIPCMessage
, kIPCServiceNameKey
); 
 163                 CFNumberRef     aPIDNumber 
= CFDictionaryGetValue(anIPCMessage
, kIPCProcessIDKey
); 
 165                 if (aServiceName 
&& CFGetTypeID(aServiceName
) == CFStringGetTypeID()) { 
 166                         anItem 
= StartupItemListGetProvider(aStartupContext
->aWaitingList
, aServiceName
); 
 167                 } else if (aPIDNumber 
&& 
 168                            CFGetTypeID(aPIDNumber
) == CFNumberGetTypeID() && 
 169                 CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &aPID
)) { 
 170                         anItem 
= StartupItemWithPID(aStartupContext
->aWaitingList
, aPID
); 
 177  * Displays a message on the console. 
 178  * aConsoleMessage will be localized according to the dictionary in the specified item. 
 179  * Running tems may be specified by their current process id.  Items may also be specified 
 180  * by one of the service names they provide. 
 183 consoleMessage(StartupContext aStartupContext
, CFDictionaryRef anIPCMessage
) 
 185         if (aStartupContext 
&& anIPCMessage
) { 
 186                 CFStringRef     aConsoleMessage 
= CFDictionaryGetValue(anIPCMessage
, kIPCConsoleMessageKey
); 
 188                 if (aConsoleMessage 
&& CFGetTypeID(aConsoleMessage
) == CFStringGetTypeID()) { 
 189                         CF_syslog(LOG_INFO
, CFSTR("%@"), aConsoleMessage
); 
 195  * Records the success or failure or a particular service. 
 196  * If no service name is specified, but a pid is, then all services provided 
 197  * by the item are flagged. 
 200 statusMessage(StartupContext aStartupContext
, CFDictionaryRef anIPCMessage
) 
 202         if (anIPCMessage 
&& aStartupContext 
&& aStartupContext
->aStatusDict
) { 
 203                 CFMutableDictionaryRef anItem 
= itemFromIPCMessage(aStartupContext
, anIPCMessage
); 
 204                 CFStringRef     aServiceName 
= CFDictionaryGetValue(anIPCMessage
, kIPCServiceNameKey
); 
 205                 CFBooleanRef    aStatus 
= CFDictionaryGetValue(anIPCMessage
, kIPCStatusKey
); 
 207                 if (anItem 
&& aStatus 
&& 
 208                     CFGetTypeID(aStatus
) == CFBooleanGetTypeID() && 
 209                     (!aServiceName 
|| CFGetTypeID(aServiceName
) == CFStringGetTypeID())) { 
 210                         StartupItemSetStatus(aStartupContext
->aStatusDict
, anItem
, aServiceName
, CFBooleanGetValue(aStatus
), TRUE
); 
 216  * Queries one of several configuration settings. 
 219 queryConfigSetting(StartupContext aStartupContext
, CFDictionaryRef anIPCMessage
) 
 224                 CFStringRef     aSetting 
= CFDictionaryGetValue(anIPCMessage
, kIPCConfigSettingKey
); 
 226                 if (aSetting 
&& CFGetTypeID(aSetting
) == CFStringGetTypeID()) { 
 227                         if (CFEqual(aSetting
, kIPCConfigSettingVerboseFlag
)) { 
 228                                 aValue 
= gVerboseFlag 
? "-YES-" : "-NO-"; 
 229                         } else if (CFEqual(aSetting
, kIPCConfigSettingNetworkUp
)) { 
 230                                 Boolean         aNetworkUpFlag 
= FALSE
; 
 231                                 if (aStartupContext 
&& aStartupContext
->aStatusDict
) { 
 232                                         aNetworkUpFlag 
= CFDictionaryContainsKey(aStartupContext
->aStatusDict
, CFSTR("Network")); 
 234                                 aValue 
= aNetworkUpFlag 
? "-YES-" : "-NO-"; 
 238         return CFDataCreate(NULL
, (const UInt8 
*)aValue
, strlen(aValue
) + 1);   /* aValue + null */ 
 241 static void    *handleIPCMessage(void *aMsgParam
, CFIndex aMessageSize 
__attribute__((unused
)), CFAllocatorRef anAllocator 
__attribute__((unused
)), void *aMachPort
) { 
 242         SystemStarterIPCMessage 
*aMessage 
= (SystemStarterIPCMessage 
*) aMsgParam
; 
 243         SystemStarterIPCMessage 
*aReplyMessage 
= NULL
; 
 245         CFDataRef       aResult 
= NULL
; 
 246         CFDataRef       aData 
= NULL
; 
 248         if (aMessage
->aHeader
.msgh_bits 
& MACH_MSGH_BITS_COMPLEX
) { 
 249                 syslog(LOG_WARNING
, "Ignoring out-of-line IPC message"); 
 252                 mach_msg_security_trailer_t 
*aSecurityTrailer 
= (mach_msg_security_trailer_t 
*) 
 253                 ((uint8_t *) aMessage 
+ round_msg(sizeof(SystemStarterIPCMessage
) + aMessage
->aByteLength
)); 
 256                  * CFRunLoop includes the format 0 message trailer with the 
 259                 if (aSecurityTrailer
->msgh_trailer_type 
== MACH_MSG_TRAILER_FORMAT_0 
&& 
 260                     aSecurityTrailer
->msgh_sender
.val
[0] != 0) { 
 261                         syslog(LOG_WARNING
, "Ignoring IPC message sent from uid %d", aSecurityTrailer
->msgh_sender
.val
[0]); 
 266         if (aMessage
->aProtocol 
!= kIPCProtocolVersion
) { 
 267                 syslog(LOG_WARNING
, "Unsupported IPC protocol version number: %d.  Message ignored", aMessage
->aProtocol
); 
 270         aData 
= CFDataCreateWithBytesNoCopy(NULL
, 
 271                      (uint8_t *) aMessage 
+ sizeof(SystemStarterIPCMessage
), 
 272                                             aMessage
->aByteLength
, 
 275          * Dispatch the IPC message. 
 278                 StartupContext  aStartupContext 
= NULL
; 
 279                 CFStringRef     anErrorString 
= NULL
; 
 280                 CFDictionaryRef anIPCMessage 
= (CFDictionaryRef
) CFPropertyListCreateFromXMLData(NULL
, aData
, kCFPropertyListImmutable
, &anErrorString
); 
 282                 CF_syslog(LOG_DEBUG
, CFSTR("IPC message = %@"), anIPCMessage
); 
 285                         CFMachPortContext aMachPortContext
; 
 286                         CFMachPortGetContext((CFMachPortRef
) aMachPort
, &aMachPortContext
); 
 287                         aStartupContext 
= (StartupContext
) aMachPortContext
.info
; 
 289                 if (anIPCMessage 
&& CFGetTypeID(anIPCMessage
) == CFDictionaryGetTypeID()) { 
 290                         /* switch on the type of the IPC message */ 
 291                         CFStringRef     anIPCMessageType 
= CFDictionaryGetValue(anIPCMessage
, kIPCMessageKey
); 
 292                         if (anIPCMessageType 
&& CFGetTypeID(anIPCMessageType
) == CFStringGetTypeID()) { 
 293                                 if (CFEqual(anIPCMessageType
, kIPCConsoleMessage
)) { 
 294                                         consoleMessage(aStartupContext
, anIPCMessage
); 
 295                                 } else if (CFEqual(anIPCMessageType
, kIPCStatusMessage
)) { 
 296                                         statusMessage(aStartupContext
, anIPCMessage
); 
 297                                 } else if (CFEqual(anIPCMessageType
, kIPCQueryMessage
)) { 
 298                                         aResult 
= queryConfigSetting(aStartupContext
, anIPCMessage
); 
 302                         CF_syslog(LOG_ERR
, CFSTR("Unable to parse IPC message: %@"), anErrorString
); 
 306                 syslog(LOG_ERR
, "Out of memory.  Could not allocate space for IPC message"); 
 310          * Generate a Mach message for the result data. 
 313                 aResult 
= CFDataCreateWithBytesNoCopy(NULL
, (const UInt8 
*)"", 1, kCFAllocatorNull
); 
 315                 CFIndex         aDataSize 
= CFDataGetLength(aResult
); 
 316                 CFIndex         aReplyMessageSize 
= round_msg(sizeof(SystemStarterIPCMessage
) + aDataSize 
+ 3); 
 317                 aReplyMessage 
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, aReplyMessageSize
, 0); 
 319                         aReplyMessage
->aHeader
.msgh_id 
= -1 * (SInt32
) aMessage
->aHeader
.msgh_id
; 
 320                         aReplyMessage
->aHeader
.msgh_size 
= aReplyMessageSize
; 
 321                         aReplyMessage
->aHeader
.msgh_remote_port 
= aMessage
->aHeader
.msgh_remote_port
; 
 322                         aReplyMessage
->aHeader
.msgh_local_port 
= MACH_PORT_NULL
; 
 323                         aReplyMessage
->aHeader
.msgh_reserved 
= 0; 
 324                         aReplyMessage
->aHeader
.msgh_bits 
= MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE
, 0); 
 325                         aReplyMessage
->aBody
.msgh_descriptor_count 
= 0; 
 326                         aReplyMessage
->aProtocol 
= kIPCProtocolVersion
; 
 327                         aReplyMessage
->aByteLength 
= CFDataGetLength(aResult
); 
 328                         memmove((uint8_t *) aReplyMessage 
+ sizeof(SystemStarterIPCMessage
), 
 329                                 CFDataGetBytePtr(aResult
), 
 330                                 CFDataGetLength(aResult
)); 
 334         if (!aReplyMessage
) { 
 335                 syslog(LOG_ERR
, "Out of memory.  Could not allocate IPC result"); 
 337         return aReplyMessage
; 
 342 getIPCPort(void *anInfo
) 
 344         return anInfo 
? CFMachPortGetPort((CFMachPortRef
) anInfo
) : MACH_PORT_NULL
; 
 348 CreateIPCRunLoopSource(CFStringRef aPortName
, StartupContext aStartupContext
) 
 350         CFRunLoopSourceRef aSource 
= NULL
; 
 351         CFMachPortRef   aMachPort 
= NULL
; 
 352         CFMachPortContext aContext
; 
 353         kern_return_t   aKernReturn 
= KERN_FAILURE
; 
 355         aContext
.version 
= 0; 
 356         aContext
.info 
= (void *) aStartupContext
; 
 358         aContext
.release 
= 0; 
 359         aContext
.copyDescription 
= 0; 
 360         aMachPort 
= CFMachPortCreate(NULL
, NULL
, &aContext
, NULL
); 
 362         if (aMachPort 
&& aPortName
) { 
 363                 CFIndex         aPortNameLength 
= CFStringGetLength(aPortName
); 
 364                 CFIndex         aPortNameSize 
= CFStringGetMaximumSizeForEncoding(aPortNameLength
, kCFStringEncodingUTF8
) + 1; 
 365                 char            *aBuffer 
= CFAllocatorAllocate(NULL
, aPortNameSize
, 0); 
 366                 if (aBuffer 
&& CFStringGetCString(aPortName
, 
 369                                                   kCFStringEncodingUTF8
)) { 
 370                         aKernReturn 
= bootstrap_register(bootstrap_port
, aBuffer
, CFMachPortGetPort(aMachPort
)); 
 373                         CFAllocatorDeallocate(NULL
, aBuffer
); 
 375         if (aMachPort 
&& aKernReturn 
== KERN_SUCCESS
) { 
 376                 CFRunLoopSourceContext1 aSourceContext
; 
 377                 aSourceContext
.version 
= 1; 
 378                 aSourceContext
.info 
= aMachPort
; 
 379                 aSourceContext
.retain 
= CFRetain
; 
 380                 aSourceContext
.release 
= CFRelease
; 
 381                 aSourceContext
.copyDescription 
= CFCopyDescription
; 
 382                 aSourceContext
.equal 
= CFEqual
; 
 383                 aSourceContext
.hash 
= CFHash
; 
 384                 aSourceContext
.getPort 
= getIPCPort
; 
 385                 aSourceContext
.perform 
= (void *) handleIPCMessage
; 
 386                 aSource 
= CFRunLoopSourceCreate(NULL
, 0, (CFRunLoopSourceContext 
*) & aSourceContext
); 
 388         if (aMachPort 
&& (!aSource 
|| aKernReturn 
!= KERN_SUCCESS
)) { 
 389                 CFMachPortInvalidate(aMachPort
); 
 390                 CFRelease(aMachPort
);