]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDPlugin.c
configd-1109.101.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCDPlugin.c
1 /*
2 * Copyright (c) 2002-2006, 2013, 2015, 2017, 2018 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 #pragma unused(sigraised)
114 /*
115 * block additional SIGCHLD's until current children have
116 * been reaped.
117 */
118 blockSignal();
119
120 /*
121 * send message to indicate that at least one child is ready
122 * to be reaped.
123 */
124 _SC_sendMachMessage(CFMachPortGetPort(childReaped), 0);
125
126 return;
127 }
128
129
130 static void
131 childrenReaped(CFMachPortRef port, void *msg, CFIndex size, void *info)
132 {
133 #pragma unused(port)
134 #pragma unused(msg)
135 #pragma unused(size)
136 #pragma unused(info)
137 pid_t pid = 0;
138 childInfoRef reapedChildren = NULL;
139
140 do {
141 struct rusage rusage;
142 int status;
143
144 pid = wait4(-1, &status, WNOHANG, &rusage);
145 switch (pid) {
146 case -1 : // if error
147 if (errno != ECHILD) {
148 perror("wait4");
149 }
150 break;
151
152 case 0 : // if no more children
153 break;
154
155 default : {
156 childInfoRef last;
157 childInfoRef this;
158
159 // grab the activeChildren mutex
160 pthread_mutex_lock(&lock);
161
162 last = NULL;
163 this = activeChildren;
164 while (this) {
165 if (this->pid == pid) {
166 /* save exit status & usage */
167 this->status = status;
168 this->rusage = rusage;
169
170 /* remove from activeChildren */
171 if (last) {
172 last->next = this->next;
173 } else {
174 activeChildren = this->next;
175 }
176
177 /* add to reapedChildren */
178 this->next = reapedChildren;
179 reapedChildren = this;
180
181 break;
182 } else {
183 /* if not this child */
184 last = this;
185 this = this->next;
186 }
187 }
188
189 // release the activeChildren mutex
190 pthread_mutex_unlock(&lock);
191
192 break;
193 }
194 }
195 } while (pid > 0);
196
197 /*
198 * we need to know about any new children waiting to be reaped so
199 * re-enable the SIGCHLD handler.
200
201 */
202 unblockSignal();
203
204 while (reapedChildren) {
205 childInfoRef child = reapedChildren;
206
207 reapedChildren = reapedChildren->next;
208 (*child->callout)(child->pid,
209 child->status,
210 &child->rusage,
211 child->context);
212 CFAllocatorDeallocate(NULL, child);
213 }
214
215 return;
216 }
217
218
219 static CFStringRef
220 childReapedMPCopyDescription(const void *info)
221 {
222 #pragma unused(info)
223 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGCHLD MP>"));
224 }
225
226
227 void
228 _SCDPluginExecInit(void)
229 {
230 struct sigaction act;
231 CFMachPortContext context = { 0
232 , (void *)1
233 , NULL
234 , NULL
235 , childReapedMPCopyDescription
236 };
237
238 CFRunLoopSourceRef rls;
239
240 // create the "a child has been reaped" notification port
241 childReaped = CFMachPortCreate(NULL, childrenReaped, &context, NULL);
242
243 // set queue limit
244 {
245 mach_port_limits_t limits;
246 kern_return_t status;
247
248 limits.mpl_qlimit = 1;
249 status = mach_port_set_attributes(mach_task_self(),
250 CFMachPortGetPort(childReaped),
251 MACH_PORT_LIMITS_INFO,
252 (mach_port_info_t)&limits,
253 MACH_PORT_LIMITS_INFO_COUNT);
254 if (status != KERN_SUCCESS) {
255 perror("mach_port_set_attributes");
256 }
257 }
258
259 // add to our runloop
260 rls = CFMachPortCreateRunLoopSource(NULL, childReaped, 0);
261 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
262 CFRelease(rls);
263
264 // enable signal handler
265 act.sa_handler = reaper;
266 sigemptyset(&act.sa_mask);
267 act.sa_flags = SA_RESTART|SA_NOCLDSTOP;
268 if (sigaction(SIGCHLD, &act, NULL) == -1) {
269 perror("sigaction");
270 }
271
272 return;
273 }
274
275
276 pid_t
277 _SCDPluginExecCommand2(SCDPluginExecCallBack callout,
278 void *context,
279 uid_t uid,
280 gid_t gid,
281 const char *path,
282 char * const argv[],
283 SCDPluginExecSetup setup,
284 void *setupContext
285 )
286 {
287 char buf[1024];
288 pid_t pid;
289 struct passwd pwd;
290 struct passwd *result = NULL;
291 char *username = NULL;
292
293 // grab the activeChildren mutex
294 pthread_mutex_lock(&lock);
295
296 // cache the getpwuid_r result here to avoid spinning that can happen
297 // when calling it between fork and execv.
298 if ((getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0) &&
299 (result != NULL)) {
300 username = result->pw_name;
301 }
302
303 // if needed, initialize
304 if (childReaped == NULL) {
305 _SCDPluginExecInit();
306 }
307
308 pid = fork();
309
310 switch (pid) {
311 case -1 : { /* if error */
312
313 int status;
314
315 status = errno;
316 printf("fork() failed: %s\n", strerror(status));
317 errno = status;
318 break;
319 }
320
321 case 0 : { /* if child */
322
323 gid_t egid;
324 uid_t euid;
325 int status;
326
327 if (setup != NULL) {
328 (setup)(pid, setupContext);
329 } else {
330 int fd;
331 int i;
332
333 /* close any open FDs */
334 for (i = getdtablesize()-1; i>=0; i--) close(i);
335
336 /* stdin, stdout, stderr */
337 fd = open(_PATH_DEVNULL, O_RDWR, 0);
338 if (fd != -1) {
339 (void) dup2(fd, STDIN_FILENO);
340 (void) dup2(fd, STDOUT_FILENO);
341 (void) dup2(fd, STDERR_FILENO);
342 if ((fd != STDIN_FILENO) && (fd != STDOUT_FILENO) && (fd != STDERR_FILENO)) {
343 (void) close(fd);
344 }
345
346 }
347 }
348
349 egid = getegid();
350 euid = geteuid();
351
352 if (egid != gid) {
353 (void) setgid(gid);
354 }
355
356 if (((euid != uid) || (egid != gid)) && username) {
357 initgroups(username, gid);
358 }
359
360 if (euid != uid) {
361 (void) setuid(uid);
362 }
363
364 /* ensure that our PATH environment variable is somewhat reasonable */
365 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) {
366 printf("setenv() failed: %s\n", strerror(errno));
367 exit(EX_OSERR);
368 }
369
370 /* execute requested command */
371 (void) execv(path, argv);
372
373 /* if the execv failed */
374 status = W_EXITCODE(errno, 0);
375 _exit (WEXITSTATUS(status));
376 }
377
378 default : { /* if parent */
379 if (setup != NULL) {
380 (setup)(pid, setupContext);
381 }
382
383 if (callout != NULL) {
384 childInfoRef child;
385
386 // create child process info
387 child = CFAllocatorAllocate(NULL, sizeof(struct childInfo), 0);
388 memset(child, 0, sizeof(struct childInfo));
389 child->pid = pid;
390 child->callout = callout;
391 child->context = context;
392
393 // add the new child to the activeChildren list
394 child->next = activeChildren;
395 activeChildren = child;
396 }
397 break;
398 }
399 }
400
401 // release the activeChildren mutex
402 pthread_mutex_unlock(&lock);
403
404 return pid;
405 }
406
407
408 pid_t
409 _SCDPluginExecCommand(SCDPluginExecCallBack callout,
410 void *context,
411 uid_t uid,
412 gid_t gid,
413 const char *path,
414 char * const argv[])
415 {
416 return _SCDPluginExecCommand2(callout, context, uid, gid, path, argv, NULL, NULL);
417 }