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_LICENSE_HEADER_START@
11 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
12 * Reserved. This file contains Original Code and/or Modifications of
13 * Original Code as defined in and that are subject to the Apple Public
14 * Source License Version 1.1 (the "License"). You may not use this file
15 * except in compliance with the License. Please obtain a copy of the
16 * License at http://www.apple.com/publicsource and read it before using
19 * The Original Code and all software distributed under the License are
20 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
21 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
22 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
24 * License for the specific language governing rights and limitations
27 * @APPLE_LICENSE_HEADER_END@
31 #include <mach/mach.h>
32 #include <mach/message.h>
33 #include <mach/mach_error.h>
34 #include <servers/bootstrap.h>
35 #include <CoreFoundation/CoreFoundation.h>
38 #include "StartupItems.h"
39 #include "SystemStarter.h"
40 #include "SystemStarterIPC.h"
42 /* Structure to pass StartupContext and anItem to the termination handler. */
43 typedef struct TerminationContextStorage
{
44 StartupContext aStartupContext
;
45 CFMutableDictionaryRef anItem
;
46 } *TerminationContext
;
49 * A CFMachPort invalidation callback that records the termination of
50 * a startup item task. Stops the current run loop to give system_starter
51 * another go at running items.
54 startupItemTerminated(CFMachPortRef aMachPort
, void *anInfo
)
56 TerminationContext aTerminationContext
= (TerminationContext
) anInfo
;
59 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(aMachPort
));
61 if (aTerminationContext
&& aTerminationContext
->anItem
) {
65 CFMutableDictionaryRef anItem
= aTerminationContext
->anItem
;
66 StartupContext aStartupContext
= aTerminationContext
->aStartupContext
;
68 /* Get the exit status */
70 aPID
= StartupItemGetPID(anItem
);
72 rPID
= waitpid(aPID
, &aStatus
, 0);
74 if (aStartupContext
) {
75 --aStartupContext
->aRunningCount
;
77 /* Record the item's status */
78 if (aStartupContext
->aStatusDict
) {
79 StartupItemExit(aStartupContext
->aStatusDict
, anItem
, (WIFEXITED(aStatus
) && WEXITSTATUS(aStatus
) == 0));
81 CF_syslog(LOG_WARNING
, CFSTR("%@ (%d) did not complete successfully"), CFDictionaryGetValue(anItem
, CFSTR("Description")), aPID
);
83 CF_syslog(LOG_DEBUG
, CFSTR("Finished %@ (%d)"), CFDictionaryGetValue(anItem
, CFSTR("Description")), aPID
);
87 * If the item failed to start, then add it to the
90 if (WEXITSTATUS(aStatus
) || WTERMSIG(aStatus
) || WCOREDUMP(aStatus
)) {
91 CFDictionarySetValue(anItem
, kErrorKey
, kErrorReturnNonZero
);
92 AddItemToFailedList(aStartupContext
, anItem
);
95 * Remove the item from the waiting list regardless
96 * if it was successful or it failed.
98 RemoveItemFromWaitingList(aStartupContext
, anItem
);
101 if (aTerminationContext
)
102 free(aTerminationContext
);
106 MonitorStartupItem(StartupContext aStartupContext
, CFMutableDictionaryRef anItem
)
108 pid_t aPID
= StartupItemGetPID(anItem
);
109 if (anItem
&& aPID
> 0) {
111 kern_return_t aResult
;
112 CFMachPortContext aContext
;
113 CFMachPortRef aMachPort
;
114 CFRunLoopSourceRef aSource
;
115 TerminationContext aTerminationContext
= (TerminationContext
) malloc(sizeof(struct TerminationContextStorage
));
117 aTerminationContext
->aStartupContext
= aStartupContext
;
118 aTerminationContext
->anItem
= anItem
;
120 aContext
.version
= 0;
121 aContext
.info
= aTerminationContext
;
123 aContext
.release
= 0;
125 if ((aResult
= task_for_pid(mach_task_self(), aPID
, &aPort
)) != KERN_SUCCESS
)
128 if (!(aMachPort
= CFMachPortCreateWithPort(NULL
, aPort
, NULL
, &aContext
, NULL
)))
131 if (!(aSource
= CFMachPortCreateRunLoopSource(NULL
, aMachPort
, 0))) {
132 CFRelease(aMachPort
);
135 CFMachPortSetInvalidationCallBack(aMachPort
, startupItemTerminated
);
136 CFRunLoopAddSource(CFRunLoopGetCurrent(), aSource
, kCFRunLoopCommonModes
);
138 CFRelease(aMachPort
);
142 * The assumption is something failed, the task already
145 startupItemTerminated(NULL
, aTerminationContext
);
150 * Returns a reference to an item based on tokens passed in an IPC message.
151 * This is useful for figuring out which item the message came from.
153 * Currently two tokens are supported:
154 * kIPCProcessIDKey - the pid of the running startup script
155 * kIPCServiceNameKey - a name of a service that the item provides. This
156 * takes precedence over the pid key when both are present.
158 static CFMutableDictionaryRef
159 itemFromIPCMessage(StartupContext aStartupContext
, CFDictionaryRef anIPCMessage
)
161 CFMutableDictionaryRef anItem
= NULL
;
163 if (aStartupContext
&& anIPCMessage
) {
164 CFStringRef aServiceName
= CFDictionaryGetValue(anIPCMessage
, kIPCServiceNameKey
);
166 CFNumberRef aPIDNumber
= CFDictionaryGetValue(anIPCMessage
, kIPCProcessIDKey
);
168 if (aServiceName
&& CFGetTypeID(aServiceName
) == CFStringGetTypeID()) {
169 anItem
= StartupItemListGetProvider(aStartupContext
->aWaitingList
, aServiceName
);
170 } else if (aPIDNumber
&&
171 CFGetTypeID(aPIDNumber
) == CFNumberGetTypeID() &&
172 CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &aPID
)) {
173 anItem
= StartupItemWithPID(aStartupContext
->aWaitingList
, aPID
);
180 * Displays a message on the console.
181 * aConsoleMessage will be localized according to the dictionary in the specified item.
182 * Running tems may be specified by their current process id. Items may also be specified
183 * by one of the service names they provide.
186 consoleMessage(StartupContext aStartupContext
, CFDictionaryRef anIPCMessage
)
188 if (aStartupContext
&& anIPCMessage
) {
189 CFStringRef aConsoleMessage
= CFDictionaryGetValue(anIPCMessage
, kIPCConsoleMessageKey
);
191 if (aConsoleMessage
&& CFGetTypeID(aConsoleMessage
) == CFStringGetTypeID()) {
192 CF_syslog(LOG_INFO
, CFSTR("%@"), aConsoleMessage
);
198 * Records the success or failure or a particular service.
199 * If no service name is specified, but a pid is, then all services provided
200 * by the item are flagged.
203 statusMessage(StartupContext aStartupContext
, CFDictionaryRef anIPCMessage
)
205 if (anIPCMessage
&& aStartupContext
&& aStartupContext
->aStatusDict
) {
206 CFMutableDictionaryRef anItem
= itemFromIPCMessage(aStartupContext
, anIPCMessage
);
207 CFStringRef aServiceName
= CFDictionaryGetValue(anIPCMessage
, kIPCServiceNameKey
);
208 CFBooleanRef aStatus
= CFDictionaryGetValue(anIPCMessage
, kIPCStatusKey
);
210 if (anItem
&& aStatus
&&
211 CFGetTypeID(aStatus
) == CFBooleanGetTypeID() &&
212 (!aServiceName
|| CFGetTypeID(aServiceName
) == CFStringGetTypeID())) {
213 StartupItemSetStatus(aStartupContext
->aStatusDict
, anItem
, aServiceName
, CFBooleanGetValue(aStatus
), TRUE
);
219 * Queries one of several configuration settings.
222 queryConfigSetting(StartupContext aStartupContext
, CFDictionaryRef anIPCMessage
)
227 CFStringRef aSetting
= CFDictionaryGetValue(anIPCMessage
, kIPCConfigSettingKey
);
229 if (aSetting
&& CFGetTypeID(aSetting
) == CFStringGetTypeID()) {
230 if (CFEqual(aSetting
, kIPCConfigSettingVerboseFlag
)) {
231 aValue
= gVerboseFlag
? "-YES-" : "-NO-";
232 } else if (CFEqual(aSetting
, kIPCConfigSettingNetworkUp
)) {
233 Boolean aNetworkUpFlag
= FALSE
;
234 if (aStartupContext
&& aStartupContext
->aStatusDict
) {
235 aNetworkUpFlag
= CFDictionaryContainsKey(aStartupContext
->aStatusDict
, CFSTR("Network"));
237 aValue
= aNetworkUpFlag
? "-YES-" : "-NO-";
241 return CFDataCreate(NULL
, aValue
, strlen(aValue
) + 1); /* aValue + null */
244 static void *handleIPCMessage(void *aMsgParam
, CFIndex aMessageSize
__attribute__((unused
)), CFAllocatorRef anAllocator
__attribute__((unused
)), void *aMachPort
) {
245 SystemStarterIPCMessage
*aMessage
= (SystemStarterIPCMessage
*) aMsgParam
;
246 SystemStarterIPCMessage
*aReplyMessage
= NULL
;
248 CFDataRef aResult
= NULL
;
249 CFDataRef aData
= NULL
;
251 if (aMessage
->aHeader
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
252 syslog(LOG_WARNING
, "Ignoring out-of-line IPC message");
255 mach_msg_security_trailer_t
*aSecurityTrailer
= (mach_msg_security_trailer_t
*)
256 ((uint8_t *) aMessage
+ round_msg(sizeof(SystemStarterIPCMessage
) + aMessage
->aByteLength
));
259 * CFRunLoop includes the format 0 message trailer with the
262 if (aSecurityTrailer
->msgh_trailer_type
== MACH_MSG_TRAILER_FORMAT_0
&&
263 aSecurityTrailer
->msgh_sender
.val
[0] != 0) {
264 syslog(LOG_WARNING
, "Ignoring IPC message sent from uid %d", aSecurityTrailer
->msgh_sender
.val
[0]);
269 if (aMessage
->aProtocol
!= kIPCProtocolVersion
) {
270 syslog(LOG_WARNING
, "Unsupported IPC protocol version number: %d. Message ignored", aMessage
->aProtocol
);
273 aData
= CFDataCreateWithBytesNoCopy(NULL
,
274 (uint8_t *) aMessage
+ sizeof(SystemStarterIPCMessage
),
275 aMessage
->aByteLength
,
278 * Dispatch the IPC message.
281 StartupContext aStartupContext
= NULL
;
282 CFStringRef anErrorString
= NULL
;
283 CFDictionaryRef anIPCMessage
= (CFDictionaryRef
) CFPropertyListCreateFromXMLData(NULL
, aData
, kCFPropertyListImmutable
, &anErrorString
);
285 CF_syslog(LOG_DEBUG
, CFSTR("IPC message = %@"), anIPCMessage
);
288 CFMachPortContext aMachPortContext
;
289 CFMachPortGetContext((CFMachPortRef
) aMachPort
, &aMachPortContext
);
290 aStartupContext
= (StartupContext
) aMachPortContext
.info
;
292 if (anIPCMessage
&& CFGetTypeID(anIPCMessage
) == CFDictionaryGetTypeID()) {
293 /* switch on the type of the IPC message */
294 CFStringRef anIPCMessageType
= CFDictionaryGetValue(anIPCMessage
, kIPCMessageKey
);
295 if (anIPCMessageType
&& CFGetTypeID(anIPCMessageType
) == CFStringGetTypeID()) {
296 if (CFEqual(anIPCMessageType
, kIPCConsoleMessage
)) {
297 consoleMessage(aStartupContext
, anIPCMessage
);
298 } else if (CFEqual(anIPCMessageType
, kIPCStatusMessage
)) {
299 statusMessage(aStartupContext
, anIPCMessage
);
300 } else if (CFEqual(anIPCMessageType
, kIPCQueryMessage
)) {
301 aResult
= queryConfigSetting(aStartupContext
, anIPCMessage
);
305 CF_syslog(LOG_ERR
, CFSTR("Unable to parse IPC message: %@"), anErrorString
);
309 syslog(LOG_ERR
, "Out of memory. Could not allocate space for IPC message");
313 * Generate a Mach message for the result data.
316 aResult
= CFDataCreateWithBytesNoCopy(NULL
, "", 1, kCFAllocatorNull
);
318 CFIndex aDataSize
= CFDataGetLength(aResult
);
319 CFIndex aReplyMessageSize
= round_msg(sizeof(SystemStarterIPCMessage
) + aDataSize
+ 3);
320 aReplyMessage
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, aReplyMessageSize
, 0);
322 aReplyMessage
->aHeader
.msgh_id
= -1 * (SInt32
) aMessage
->aHeader
.msgh_id
;
323 aReplyMessage
->aHeader
.msgh_size
= aReplyMessageSize
;
324 aReplyMessage
->aHeader
.msgh_remote_port
= aMessage
->aHeader
.msgh_remote_port
;
325 aReplyMessage
->aHeader
.msgh_local_port
= MACH_PORT_NULL
;
326 aReplyMessage
->aHeader
.msgh_reserved
= 0;
327 aReplyMessage
->aHeader
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE
, 0);
328 aReplyMessage
->aBody
.msgh_descriptor_count
= 0;
329 aReplyMessage
->aProtocol
= kIPCProtocolVersion
;
330 aReplyMessage
->aByteLength
= CFDataGetLength(aResult
);
331 memmove((uint8_t *) aReplyMessage
+ sizeof(SystemStarterIPCMessage
),
332 CFDataGetBytePtr(aResult
),
333 CFDataGetLength(aResult
));
337 if (!aReplyMessage
) {
338 syslog(LOG_ERR
, "Out of memory. Could not allocate IPC result");
340 return aReplyMessage
;
345 getIPCPort(void *anInfo
)
347 return anInfo
? CFMachPortGetPort((CFMachPortRef
) anInfo
) : MACH_PORT_NULL
;
351 CreateIPCRunLoopSource(CFStringRef aPortName
, StartupContext aStartupContext
)
353 CFRunLoopSourceRef aSource
= NULL
;
354 CFMachPortRef aMachPort
= NULL
;
355 CFMachPortContext aContext
;
356 kern_return_t aKernReturn
= KERN_FAILURE
;
358 aContext
.version
= 0;
359 aContext
.info
= (void *) aStartupContext
;
361 aContext
.release
= 0;
362 aContext
.copyDescription
= 0;
363 aMachPort
= CFMachPortCreate(NULL
, NULL
, &aContext
, NULL
);
365 if (aMachPort
&& aPortName
) {
366 CFIndex aPortNameLength
= CFStringGetLength(aPortName
);
367 CFIndex aPortNameSize
= CFStringGetMaximumSizeForEncoding(aPortNameLength
, kCFStringEncodingUTF8
) + 1;
368 uint8_t *aBuffer
= CFAllocatorAllocate(NULL
, aPortNameSize
, 0);
369 if (aBuffer
&& CFStringGetCString(aPortName
,
372 kCFStringEncodingUTF8
)) {
373 mach_port_t aBootstrapPort
;
374 task_get_bootstrap_port(mach_task_self(), &aBootstrapPort
);
375 aKernReturn
= bootstrap_register(aBootstrapPort
, aBuffer
, CFMachPortGetPort(aMachPort
));
378 CFAllocatorDeallocate(NULL
, aBuffer
);
380 if (aMachPort
&& aKernReturn
== KERN_SUCCESS
) {
381 CFRunLoopSourceContext1 aSourceContext
;
382 aSourceContext
.version
= 1;
383 aSourceContext
.info
= aMachPort
;
384 aSourceContext
.retain
= CFRetain
;
385 aSourceContext
.release
= CFRelease
;
386 aSourceContext
.copyDescription
= CFCopyDescription
;
387 aSourceContext
.equal
= CFEqual
;
388 aSourceContext
.hash
= CFHash
;
389 aSourceContext
.getPort
= getIPCPort
;
390 aSourceContext
.perform
= (void *) handleIPCMessage
;
391 aSource
= CFRunLoopSourceCreate(NULL
, 0, (CFRunLoopSourceContext
*) & aSourceContext
);
393 if (aMachPort
&& (!aSource
|| aKernReturn
!= KERN_SUCCESS
)) {
394 CFMachPortInvalidate(aMachPort
);
395 CFRelease(aMachPort
);