]>
Commit | Line | Data |
---|---|---|
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 | } |