]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/IPC.c
2b9350ad6993b9164d0bab09570f57e01d34daea
[apple/launchd.git] / launchd / src / IPC.c
1 /**
2 * IPC.c - System Starter IPC routines
3 * Wilfredo Sanchez | wsanchez@opensource.apple.com
4 * Kevin Van Vechten | kevinvv@uclink4.berkeley.edu
5 * $Apple$
6 **
7 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
8 *
9 * @APPLE_APACHE_LICENSE_HEADER_START@
10 *
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
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
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.
22 *
23 * @APPLE_APACHE_LICENSE_HEADER_END@
24 **/
25
26 #include <sys/wait.h>
27 #include <mach/mach.h>
28 #include <mach/message.h>
29 #include <mach/mach_error.h>
30 #include <CoreFoundation/CoreFoundation.h>
31 #include <syslog.h>
32
33 #include "bootstrap.h"
34
35 #include "IPC.h"
36 #include "StartupItems.h"
37 #include "SystemStarter.h"
38 #include "SystemStarterIPC.h"
39
40 /* Structure to pass StartupContext and anItem to the termination handler. */
41 typedef struct TerminationContextStorage {
42 StartupContext aStartupContext;
43 CFMutableDictionaryRef anItem;
44 } *TerminationContext;
45
46 /**
47 * A CFMachPort invalidation callback that records the termination of
48 * a startup item task. Stops the current run loop to give system_starter
49 * another go at running items.
50 **/
51 static void
52 startupItemTerminated(CFMachPortRef aMachPort, void *anInfo)
53 {
54 TerminationContext aTerminationContext = (TerminationContext) anInfo;
55
56 if (aMachPort) {
57 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(aMachPort));
58 }
59 if (aTerminationContext && aTerminationContext->anItem) {
60 pid_t aPID = 0;
61 pid_t rPID = 0;
62 int aStatus = 0;
63 CFMutableDictionaryRef anItem = aTerminationContext->anItem;
64 StartupContext aStartupContext = aTerminationContext->aStartupContext;
65
66 /* Get the exit status */
67 if (anItem) {
68 aPID = StartupItemGetPID(anItem);
69 if (aPID > 0)
70 rPID = waitpid(aPID, &aStatus, 0);
71 }
72 if (aStartupContext) {
73 --aStartupContext->aRunningCount;
74
75 /* Record the item's status */
76 if (aStartupContext->aStatusDict) {
77 StartupItemExit(aStartupContext->aStatusDict, anItem, (WIFEXITED(aStatus) && WEXITSTATUS(aStatus) == 0));
78 if (aStatus) {
79 CF_syslog(LOG_WARNING, CFSTR("%@ (%d) did not complete successfully"), CFDictionaryGetValue(anItem, CFSTR("Description")), aPID);
80 } else {
81 CF_syslog(LOG_DEBUG, CFSTR("Finished %@ (%d)"), CFDictionaryGetValue(anItem, CFSTR("Description")), aPID);
82 }
83 }
84 /*
85 * If the item failed to start, then add it to the
86 * failed list
87 */
88 if (WEXITSTATUS(aStatus) || WTERMSIG(aStatus) || WCOREDUMP(aStatus)) {
89 CFDictionarySetValue(anItem, kErrorKey, kErrorReturnNonZero);
90 AddItemToFailedList(aStartupContext, anItem);
91 }
92 /*
93 * Remove the item from the waiting list regardless
94 * if it was successful or it failed.
95 */
96 RemoveItemFromWaitingList(aStartupContext, anItem);
97 }
98 }
99 if (aTerminationContext)
100 free(aTerminationContext);
101 }
102
103 void
104 MonitorStartupItem(StartupContext aStartupContext, CFMutableDictionaryRef anItem)
105 {
106 pid_t aPID = StartupItemGetPID(anItem);
107 if (anItem && aPID > 0) {
108 mach_port_t aPort;
109 kern_return_t aResult;
110 CFMachPortContext aContext;
111 CFMachPortRef aMachPort;
112 CFRunLoopSourceRef aSource;
113 TerminationContext aTerminationContext = (TerminationContext) malloc(sizeof(struct TerminationContextStorage));
114
115 aTerminationContext->aStartupContext = aStartupContext;
116 aTerminationContext->anItem = anItem;
117
118 aContext.version = 0;
119 aContext.info = aTerminationContext;
120 aContext.retain = 0;
121 aContext.release = 0;
122
123 if ((aResult = task_name_for_pid(mach_task_self(), aPID, &aPort)) != KERN_SUCCESS)
124 goto out_bad;
125
126 if (!(aMachPort = CFMachPortCreateWithPort(NULL, aPort, NULL, &aContext, NULL)))
127 goto out_bad;
128
129 if (!(aSource = CFMachPortCreateRunLoopSource(NULL, aMachPort, 0))) {
130 CFRelease(aMachPort);
131 goto out_bad;
132 }
133 CFMachPortSetInvalidationCallBack(aMachPort, startupItemTerminated);
134 CFRunLoopAddSource(CFRunLoopGetCurrent(), aSource, kCFRunLoopCommonModes);
135 CFRelease(aSource);
136 CFRelease(aMachPort);
137 return;
138 out_bad:
139 /*
140 * The assumption is something failed, the task already
141 * terminated.
142 */
143 startupItemTerminated(NULL, aTerminationContext);
144 }
145 }