2 * Copyright (c) 2002-2006, 2013 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * October 12, 2001 Allan Nathanson <ajn@apple.com>
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/socket.h>
41 #include <mach/mach.h>
42 #include <mach/mach_error.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <SystemConfiguration/SCDPlugin.h>
46 #include <SystemConfiguration/SCPrivate.h>
50 typedef struct childInfo
*childInfoRef
;
54 SCDPluginExecCallBack callout
;
63 * Mach port used to notify runloop when a child process
66 static CFMachPortRef childReaped
= NULL
;
69 * The following dictionaries contain information about child
70 * processes, reaped processes, and any associated callback
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
78 static childInfoRef activeChildren
= NULL
;
79 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
82 static __inline__
void
85 sigset_t mask
= sigmask(SIGCHLD
);
88 if (sigprocmask(SIG_BLOCK
, &mask
, NULL
) == -1) {
89 perror("sigprocmask(SIG_BLOCK)");
96 static __inline__
void
99 sigset_t mask
= sigmask(SIGCHLD
);
102 if (sigprocmask(SIG_UNBLOCK
, &mask
, NULL
) == -1) {
103 perror("sigprocmask(SIG_UNBLOCK)");
111 reaper(int sigraised
)
114 * block additional SIGCHLD's until current children have
120 * send message to indicate that at least one child is ready
123 _SC_sendMachMessage(CFMachPortGetPort(childReaped
), 0);
130 childrenReaped(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
133 childInfoRef reapedChildren
= NULL
;
136 struct rusage rusage
;
139 pid
= wait4(-1, &status
, WNOHANG
, &rusage
);
141 case -1 : // if error
142 if (errno
!= ECHILD
) {
147 case 0 : // if no more children
154 // grab the activeChildren mutex
155 pthread_mutex_lock(&lock
);
158 this = activeChildren
;
160 if (this->pid
== pid
) {
161 /* save exit status & usage */
162 this->status
= status
;
163 this->rusage
= rusage
;
165 /* remove from activeChildren */
167 last
->next
= this->next
;
169 activeChildren
= this->next
;
172 /* add to reapedChildren */
173 this->next
= reapedChildren
;
174 reapedChildren
= this;
178 /* if not this child */
184 // release the activeChildren mutex
185 pthread_mutex_unlock(&lock
);
193 * we need to know about any new children waiting to be reaped so
194 * re-enable the SIGCHLD handler.
199 while (reapedChildren
) {
200 childInfoRef child
= reapedChildren
;
202 reapedChildren
= reapedChildren
->next
;
203 (*child
->callout
)(child
->pid
,
207 CFAllocatorDeallocate(NULL
, child
);
215 childReapedMPCopyDescription(const void *info
)
217 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SIGCHLD MP>"));
224 struct sigaction act
;
225 CFMachPortContext context
= { 0
229 , childReapedMPCopyDescription
232 CFRunLoopSourceRef rls
;
234 // create the "a child has been reaped" notification port
235 childReaped
= CFMachPortCreate(NULL
, childrenReaped
, &context
, NULL
);
239 mach_port_limits_t limits
;
240 kern_return_t status
;
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");
253 // add to our runloop
254 rls
= CFMachPortCreateRunLoopSource(NULL
, childReaped
, 0);
255 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
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) {
271 _SCDPluginExecCommand2(SCDPluginExecCallBack callout
,
277 SCDPluginExecSetup setup
,
284 struct passwd
*result
= NULL
;
285 char *username
= NULL
;
287 // grab the activeChildren mutex
288 pthread_mutex_lock(&lock
);
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) &&
294 username
= result
->pw_name
;
297 // if needed, initialize
298 if (childReaped
== NULL
) {
299 _SCDPluginExecInit();
305 case -1 : { /* if error */
310 printf("fork() failed: %s\n", strerror(status
));
315 case 0 : { /* if child */
323 (setup
)(pid
, setupContext
);
325 /* close any open FDs */
326 for (i
= getdtablesize()-1; i
>=0; i
--) close(i
);
327 open(_PATH_DEVNULL
, O_RDWR
, 0);
339 if (((euid
!= uid
) || (egid
!= gid
)) && username
) {
340 initgroups(username
, gid
);
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
));
353 /* execute requested command */
354 (void) execv(path
, argv
);
356 /* if the execv failed */
357 status
= W_EXITCODE(errno
, 0);
358 _exit (WEXITSTATUS(status
));
361 default : { /* if parent */
363 (setup
)(pid
, setupContext
);
366 if (callout
!= NULL
) {
369 // create child process info
370 child
= CFAllocatorAllocate(NULL
, sizeof(struct childInfo
), 0);
371 bzero(child
, sizeof(struct childInfo
));
373 child
->callout
= callout
;
374 child
->context
= context
;
376 // add the new child to the activeChildren list
377 child
->next
= activeChildren
;
378 activeChildren
= child
;
384 // release the activeChildren mutex
385 pthread_mutex_unlock(&lock
);
392 _SCDPluginExecCommand(SCDPluginExecCallBack callout
,
399 return _SCDPluginExecCommand2(callout
, context
, uid
, gid
, path
, argv
, NULL
, NULL
);