]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDPlugin.c
c14e62d42201a2483c746c48b37d02e3b68ad92f
[apple/configd.git] / SystemConfiguration.fproj / SCDPlugin.c
1 /*
2 * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * October 12, 2001 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31 #include <fcntl.h>
32 #include <paths.h>
33 #include <pthread.h>
34 #include <unistd.h>
35 #include <sysexits.h>
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/socket.h>
39 #include <sys/wait.h>
40 #include <mach/mach.h>
41 #include <mach/mach_error.h>
42
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <SystemConfiguration/SCDPlugin.h>
45
46
47
48 typedef struct childInfo *childInfoRef;
49
50 struct childInfo {
51 pid_t pid;
52 SCDPluginExecCallBack callout;
53 void *context;
54 int status;
55 struct rusage rusage;
56 childInfoRef next;
57 };
58
59
60 /*
61 * Mach port used to notify runloop when a child process
62 * has been reaped.
63 */
64 static CFMachPortRef childReaped = NULL;
65
66 /*
67 * The following dictionaries contain information about child
68 * processes, reaped processes, and any associated callback
69 * information.
70 *
71 * Important: Access to these dictionaries should only be
72 * made when in a SIGCHLD handler (or when the
73 * childLock mutex is held *AND* the signal
74 * has been blocked).
75 */
76 static childInfoRef activeChildren = NULL;
77 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
78
79
80 static inline void
81 blockSignal()
82 {
83 sigset_t mask = sigmask(SIGCHLD);
84
85 // block SIGCHLD
86 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
87 perror("sigprocmask(SIG_BLOCK)");
88 }
89
90 return;
91 }
92
93
94 static inline void
95 unblockSignal()
96 {
97 sigset_t mask = sigmask(SIGCHLD);
98
99 // unblock SIGCHLD
100 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) {
101 perror("sigprocmask(SIG_UNBLOCK)");
102 }
103
104 return;
105 }
106
107
108 static void
109 reaper(int sigraised)
110 {
111 mach_msg_empty_send_t msg;
112 mach_msg_option_t options;
113 kern_return_t status;
114
115 /*
116 * block additional SIGCHLD's until current children have
117 * been reaped.
118 */
119 blockSignal();
120
121 /*
122 * send message to indicate that at least one child is ready
123 * to be reaped.
124 */
125 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
126 msg.header.msgh_size = sizeof(msg);
127 msg.header.msgh_remote_port = CFMachPortGetPort(childReaped);
128 msg.header.msgh_local_port = MACH_PORT_NULL;
129 msg.header.msgh_id = 0;
130 options = MACH_SEND_TIMEOUT;
131 status = mach_msg(&msg.header, /* msg */
132 MACH_SEND_MSG|options, /* options */
133 msg.header.msgh_size, /* send_size */
134 0, /* rcv_size */
135 MACH_PORT_NULL, /* rcv_name */
136 0, /* timeout */
137 MACH_PORT_NULL); /* notify */
138 if (status == MACH_SEND_TIMED_OUT) {
139 mach_msg_destroy(&msg.header);
140 }
141
142 return;
143 }
144
145
146 static void
147 childrenReaped(CFMachPortRef port, void *msg, CFIndex size, void *info)
148 {
149 pid_t pid = 0;
150 childInfoRef reapedChildren = NULL;
151
152 do {
153 struct rusage rusage;
154 int status;
155
156 pid = wait4(-1, &status, WNOHANG, &rusage);
157 switch (pid) {
158 case -1 : // if error
159 if (errno != ECHILD) {
160 perror("wait4");
161 }
162 break;
163
164 case 0 : // if no more children
165 break;
166
167 default : {
168 childInfoRef last;
169 childInfoRef this;
170
171 // grab the activeChildren mutex
172 pthread_mutex_lock(&lock);
173
174 last = NULL;
175 this = activeChildren;
176 while (this) {
177 if (this->pid == pid) {
178 /* save exit status & usage */
179 this->status = status;
180 this->rusage = rusage;
181
182 /* remove from activeChildren */
183 if (last) {
184 last->next = this->next;
185 } else {
186 activeChildren = this->next;
187 }
188
189 /* add to reapedChildren */
190 this->next = reapedChildren;
191 reapedChildren = this;
192
193 break;
194 } else {
195 /* if not this child */
196 last = this;
197 this = this->next;
198 }
199 }
200
201 // release the activeChildren mutex
202 pthread_mutex_unlock(&lock);
203
204 break;
205 }
206 }
207 } while (pid > 0);
208
209 /*
210 * we need to know about any new children waiting to be reaped so
211 * re-enable the SIGCHLD handler.
212
213 */
214 unblockSignal();
215
216 while (reapedChildren) {
217 childInfoRef child = reapedChildren;
218
219 reapedChildren = reapedChildren->next;
220 (*child->callout)(child->pid,
221 child->status,
222 &child->rusage,
223 child->context);
224 CFAllocatorDeallocate(NULL, child);
225 }
226
227 return;
228 }
229
230
231 void
232 _SCDPluginExecInit()
233 {
234 struct sigaction act;
235 CFRunLoopSourceRef rls;
236
237 // create the "a child has been reaped" notification port
238 childReaped = CFMachPortCreate(NULL, childrenReaped, NULL, NULL);
239
240 // set queue limit
241 {
242 mach_port_limits_t limits;
243 kern_return_t status;
244
245 limits.mpl_qlimit = 1;
246 status = mach_port_set_attributes(mach_task_self(),
247 CFMachPortGetPort(childReaped),
248 MACH_PORT_LIMITS_INFO,
249 (mach_port_info_t)&limits,
250 MACH_PORT_LIMITS_INFO_COUNT);
251 if (status != KERN_SUCCESS) {
252 perror("mach_port_set_attributes");
253 }
254 }
255
256 // add to our runloop
257 rls = CFMachPortCreateRunLoopSource(NULL, childReaped, 0);
258 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
259 CFRelease(rls);
260
261 // enable signal handler
262 act.sa_handler = reaper;
263 sigemptyset(&act.sa_mask);
264 act.sa_flags = SA_RESTART|SA_NOCLDSTOP;
265 if (sigaction(SIGCHLD, &act, NULL) == -1) {
266 perror("sigaction");
267 }
268
269 return;
270 }
271
272
273 pid_t
274 _SCDPluginExecCommand2(SCDPluginExecCallBack callout,
275 void *context,
276 uid_t uid,
277 gid_t gid,
278 const char *path,
279 char * const argv[],
280 SCDPluginExecSetup setup,
281 void *setupContext
282 )
283 {
284 pid_t pid;
285
286 // grab the activeChildren mutex
287 pthread_mutex_lock(&lock);
288
289 pid = fork();
290
291 switch (pid) {
292 case -1 : { /* if error */
293
294 int status;
295
296 status = errno;
297 printf("fork() failed: %s\n", strerror(status));
298 errno = status;
299 break;
300 }
301
302 case 0 : { /* if child */
303
304 int i;
305 int status;
306
307 if (setup) {
308 (setup)(pid, setupContext);
309 } else {
310 /* close any open FDs */
311 for (i = getdtablesize()-1; i>=0; i--) close(i);
312 open(_PATH_DEVNULL, O_RDWR, 0);
313 dup(0);
314 dup(0);
315 }
316
317 if (gid != getegid()) {
318 (void) setgid(gid);
319 }
320
321 if (uid != geteuid()) {
322 (void) setuid(uid);
323 }
324
325 /* ensure that our PATH environment variable is somewhat reasonable */
326 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) {
327 printf("setenv() failed: %s\n", strerror(errno));
328 exit(EX_OSERR);
329 }
330
331 /* execute requested command */
332 (void) execv(path, argv);
333
334 /* if the execv failed */
335 status = W_EXITCODE(errno, 0);
336 _exit (WEXITSTATUS(status));
337 }
338
339 default : { /* if parent */
340 if (setup) {
341 (setup)(pid, setupContext);
342 }
343
344 if (callout) {
345 childInfoRef child;
346
347 // create child process info
348 child = CFAllocatorAllocate(NULL, sizeof(struct childInfo), 0);
349 bzero(child, sizeof(struct childInfo));
350 child->pid = pid;
351 child->callout = callout;
352 child->context = context;
353
354 // add the new child to the activeChildren list
355 child->next = activeChildren;
356 activeChildren = child;
357 }
358 break;
359 }
360 }
361
362 // release the activeChildren mutex
363 pthread_mutex_unlock(&lock);
364
365 return pid;
366 }
367
368
369 pid_t
370 _SCDPluginExecCommand(SCDPluginExecCallBack callout,
371 void *context,
372 uid_t uid,
373 gid_t gid,
374 const char *path,
375 char * const argv[])
376 {
377 return _SCDPluginExecCommand2(callout, context, uid, gid, path, argv, NULL, NULL);
378 }