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