]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDPlugin.c
a92f51d40dbc57d819511142b3a8f0766a384ddb
[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 * 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
139 return;
140 }
141
142
143 static void
144 childrenReaped(CFMachPortRef port, void *msg, CFIndex size, void *info)
145 {
146 pid_t pid = 0;
147 childInfoRef reapedChildren = NULL;
148
149 do {
150 struct rusage rusage;
151 int status;
152
153 pid = wait4(-1, &status, WNOHANG, &rusage);
154 switch (pid) {
155 case -1 : // if error
156 if (errno != ECHILD) {
157 perror("wait4");
158 }
159 break;
160
161 case 0 : // if no more children
162 break;
163
164 default : {
165 childInfoRef last;
166 childInfoRef this;
167
168 // grab the activeChildren mutex
169 pthread_mutex_lock(&lock);
170
171 last = NULL;
172 this = activeChildren;
173 while (this) {
174 if (this->pid == pid) {
175 /* save exit status & usage */
176 this->status = status;
177 this->rusage = rusage;
178
179 /* remove from activeChildren */
180 if (last) {
181 last->next = this->next;
182 } else {
183 activeChildren = this->next;
184 }
185
186 /* add to reapedChildren */
187 this->next = reapedChildren;
188 reapedChildren = this;
189
190 break;
191 } else {
192 /* if not this child */
193 last = this;
194 this = this->next;
195 }
196 }
197
198 // release the activeChildren mutex
199 pthread_mutex_unlock(&lock);
200
201 break;
202 }
203 }
204 } while (pid > 0);
205
206 /*
207 * we need to know about any new children waiting to be reaped so
208 * re-enable the SIGCHLD handler.
209
210 */
211 unblockSignal();
212
213 while (reapedChildren) {
214 childInfoRef child = reapedChildren;
215
216 reapedChildren = reapedChildren->next;
217 (*child->callout)(child->pid,
218 child->status,
219 &child->rusage,
220 child->context);
221 CFAllocatorDeallocate(NULL, child);
222 }
223
224 return;
225 }
226
227
228 void
229 _SCDPluginExecInit()
230 {
231 struct sigaction act;
232 CFRunLoopSourceRef rls;
233
234 // create the "a child has been reaped" notification port
235 childReaped = CFMachPortCreate(NULL, childrenReaped, NULL, NULL);
236
237 // set queue limit
238 {
239 mach_port_limits_t limits;
240 kern_return_t status;
241
242 limits.mpl_qlimit = 1;
243 status = mach_port_set_attributes(mach_task_self(),
244 CFMachPortGetPort(childReaped),
245 MACH_PORT_LIMITS_INFO,
246 (mach_port_info_t)&limits,
247 MACH_PORT_LIMITS_INFO_COUNT);
248 if (status != KERN_SUCCESS) {
249 perror("mach_port_set_attributes");
250 }
251 }
252
253 // add to our runloop
254 rls = CFMachPortCreateRunLoopSource(NULL, childReaped, 0);
255 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
256 CFRelease(rls);
257
258 // enable signal handler
259 act.sa_handler = reaper;
260 sigemptyset(&act.sa_mask);
261 act.sa_flags = SA_RESTART|SA_NOCLDSTOP;
262 if (sigaction(SIGCHLD, &act, NULL) == -1) {
263 perror("sigaction");
264 }
265
266 return;
267 }
268
269
270 pid_t
271 _SCDPluginExecCommand2(SCDPluginExecCallBack callout,
272 void *context,
273 uid_t uid,
274 gid_t gid,
275 const char *path,
276 char * const argv[],
277 SCDPluginExecSetup setup,
278 void *setupContext
279 )
280 {
281 pid_t pid;
282
283 // grab the activeChildren mutex
284 pthread_mutex_lock(&lock);
285
286 pid = fork();
287
288 switch (pid) {
289 case -1 : { /* if error */
290
291 int status;
292
293 status = errno;
294 printf("fork() failed: %s\n", strerror(status));
295 errno = status;
296 break;
297 }
298
299 case 0 : { /* if child */
300
301 uid_t curUID = geteuid();
302 gid_t curGID = getegid();
303 int i;
304 int status;
305
306 if (curUID != uid) {
307 (void) setuid(uid);
308 }
309
310 if (curGID != gid) {
311 (void) setgid(gid);
312 }
313
314 if (setup) {
315 (setup)(pid, setupContext);
316 } else {
317 /* close any open FDs */
318 for (i = getdtablesize()-1; i>=0; i--) close(i);
319 open(_PATH_DEVNULL, O_RDWR, 0);
320 dup(0);
321 dup(0);
322 }
323
324 /* ensure that our PATH environment variable is somewhat reasonable */
325 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) {
326 printf("setenv() failed: %s\n", strerror(errno));
327 exit(EX_OSERR);
328 }
329
330 /* execute requested command */
331 (void) execv(path, argv);
332
333 /* if the execv failed */
334 status = W_EXITCODE(errno, 0);
335 _exit (WEXITSTATUS(status));
336 }
337
338 default : { /* if parent */
339 if (setup) {
340 (setup)(pid, setupContext);
341 }
342
343 if (callout) {
344 childInfoRef child;
345
346 // create child process info
347 child = CFAllocatorAllocate(NULL, sizeof(struct childInfo), 0);
348 bzero(child, sizeof(struct childInfo));
349 child->pid = pid;
350 child->callout = callout;
351 child->context = context;
352
353 // add the new child to the activeChildren list
354 child->next = activeChildren;
355 activeChildren = child;
356 }
357 break;
358 }
359 }
360
361 // release the activeChildren mutex
362 pthread_mutex_unlock(&lock);
363
364 return pid;
365 }
366
367
368 pid_t
369 _SCDPluginExecCommand(SCDPluginExecCallBack callout,
370 void *context,
371 uid_t uid,
372 gid_t gid,
373 const char *path,
374 char * const argv[])
375 {
376 return _SCDPluginExecCommand2(callout, context, uid, gid, path, argv, NULL, NULL);
377 }