]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDPlugin.c
configd-596.12.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCDPlugin.c
1 /*
2 * Copyright (c) 2002-2006, 2013 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 <pwd.h>
34 #include <pthread.h>
35 #include <unistd.h>
36 #include <sysexits.h>
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
40 #include <sys/wait.h>
41 #include <mach/mach.h>
42 #include <mach/mach_error.h>
43
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <SystemConfiguration/SCDPlugin.h>
46 #include <SystemConfiguration/SCPrivate.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 /*
114 * block additional SIGCHLD's until current children have
115 * been reaped.
116 */
117 blockSignal();
118
119 /*
120 * send message to indicate that at least one child is ready
121 * to be reaped.
122 */
123 _SC_sendMachMessage(CFMachPortGetPort(childReaped), 0);
124
125 return;
126 }
127
128
129 static void
130 childrenReaped(CFMachPortRef port, void *msg, CFIndex size, void *info)
131 {
132 pid_t pid = 0;
133 childInfoRef reapedChildren = NULL;
134
135 do {
136 struct rusage rusage;
137 int status;
138
139 pid = wait4(-1, &status, WNOHANG, &rusage);
140 switch (pid) {
141 case -1 : // if error
142 if (errno != ECHILD) {
143 perror("wait4");
144 }
145 break;
146
147 case 0 : // if no more children
148 break;
149
150 default : {
151 childInfoRef last;
152 childInfoRef this;
153
154 // grab the activeChildren mutex
155 pthread_mutex_lock(&lock);
156
157 last = NULL;
158 this = activeChildren;
159 while (this) {
160 if (this->pid == pid) {
161 /* save exit status & usage */
162 this->status = status;
163 this->rusage = rusage;
164
165 /* remove from activeChildren */
166 if (last) {
167 last->next = this->next;
168 } else {
169 activeChildren = this->next;
170 }
171
172 /* add to reapedChildren */
173 this->next = reapedChildren;
174 reapedChildren = this;
175
176 break;
177 } else {
178 /* if not this child */
179 last = this;
180 this = this->next;
181 }
182 }
183
184 // release the activeChildren mutex
185 pthread_mutex_unlock(&lock);
186
187 break;
188 }
189 }
190 } while (pid > 0);
191
192 /*
193 * we need to know about any new children waiting to be reaped so
194 * re-enable the SIGCHLD handler.
195
196 */
197 unblockSignal();
198
199 while (reapedChildren) {
200 childInfoRef child = reapedChildren;
201
202 reapedChildren = reapedChildren->next;
203 (*child->callout)(child->pid,
204 child->status,
205 &child->rusage,
206 child->context);
207 CFAllocatorDeallocate(NULL, child);
208 }
209
210 return;
211 }
212
213
214 static CFStringRef
215 childReapedMPCopyDescription(const void *info)
216 {
217 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGCHLD MP>"));
218 }
219
220
221 void
222 _SCDPluginExecInit()
223 {
224 struct sigaction act;
225 CFMachPortContext context = { 0
226 , (void *)1
227 , NULL
228 , NULL
229 , childReapedMPCopyDescription
230 };
231
232 CFRunLoopSourceRef rls;
233
234 // create the "a child has been reaped" notification port
235 childReaped = CFMachPortCreate(NULL, childrenReaped, &context, 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 char buf[1024];
282 pid_t pid;
283 struct passwd pwd;
284 struct passwd *result = NULL;
285 char *username = NULL;
286
287 // grab the activeChildren mutex
288 pthread_mutex_lock(&lock);
289
290 // cache the getpwuid_r result here to avoid spinning that can happen
291 // when calling it between fork and execv.
292 if ((getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0) &&
293 (result != NULL)) {
294 username = result->pw_name;
295 }
296
297 // if needed, initialize
298 if (childReaped == NULL) {
299 _SCDPluginExecInit();
300 }
301
302 pid = fork();
303
304 switch (pid) {
305 case -1 : { /* if error */
306
307 int status;
308
309 status = errno;
310 printf("fork() failed: %s\n", strerror(status));
311 errno = status;
312 break;
313 }
314
315 case 0 : { /* if child */
316
317 gid_t egid;
318 uid_t euid;
319 int i;
320 int status;
321
322 if (setup != NULL) {
323 (setup)(pid, setupContext);
324 } else {
325 /* close any open FDs */
326 for (i = getdtablesize()-1; i>=0; i--) close(i);
327 open(_PATH_DEVNULL, O_RDWR, 0);
328 dup(0);
329 dup(0);
330 }
331
332 egid = getegid();
333 euid = geteuid();
334
335 if (egid != gid) {
336 (void) setgid(gid);
337 }
338
339 if (((euid != uid) || (egid != gid)) && username) {
340 initgroups(username, gid);
341 }
342
343 if (euid != uid) {
344 (void) setuid(uid);
345 }
346
347 /* ensure that our PATH environment variable is somewhat reasonable */
348 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) {
349 printf("setenv() failed: %s\n", strerror(errno));
350 exit(EX_OSERR);
351 }
352
353 /* execute requested command */
354 (void) execv(path, argv);
355
356 /* if the execv failed */
357 status = W_EXITCODE(errno, 0);
358 _exit (WEXITSTATUS(status));
359 }
360
361 default : { /* if parent */
362 if (setup != NULL) {
363 (setup)(pid, setupContext);
364 }
365
366 if (callout != NULL) {
367 childInfoRef child;
368
369 // create child process info
370 child = CFAllocatorAllocate(NULL, sizeof(struct childInfo), 0);
371 bzero(child, sizeof(struct childInfo));
372 child->pid = pid;
373 child->callout = callout;
374 child->context = context;
375
376 // add the new child to the activeChildren list
377 child->next = activeChildren;
378 activeChildren = child;
379 }
380 break;
381 }
382 }
383
384 // release the activeChildren mutex
385 pthread_mutex_unlock(&lock);
386
387 return pid;
388 }
389
390
391 pid_t
392 _SCDPluginExecCommand(SCDPluginExecCallBack callout,
393 void *context,
394 uid_t uid,
395 gid_t gid,
396 const char *path,
397 char * const argv[])
398 {
399 return _SCDPluginExecCommand2(callout, context, uid, gid, path, argv, NULL, NULL);
400 }