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