2 * Copyright (c) 2001-2004 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>
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/socket.h>
40 #include <mach/mach.h>
41 #include <mach/mach_error.h>
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <SystemConfiguration/SCDPlugin.h>
48 typedef struct childInfo
*childInfoRef
;
52 SCDPluginExecCallBack callout
;
61 * Mach port used to notify runloop when a child process
64 static CFMachPortRef childReaped
= NULL
;
67 * The following dictionaries contain information about child
68 * processes, reaped processes, and any associated callback
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
76 static childInfoRef activeChildren
= NULL
;
77 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
83 sigset_t mask
= sigmask(SIGCHLD
);
86 if (sigprocmask(SIG_BLOCK
, &mask
, NULL
) == -1) {
87 perror("sigprocmask(SIG_BLOCK)");
97 sigset_t mask
= sigmask(SIGCHLD
);
100 if (sigprocmask(SIG_UNBLOCK
, &mask
, NULL
) == -1) {
101 perror("sigprocmask(SIG_UNBLOCK)");
109 reaper(int sigraised
)
111 mach_msg_empty_send_t msg
;
112 mach_msg_option_t options
;
113 kern_return_t status
;
116 * block additional SIGCHLD's until current children have
122 * send message to indicate that at least one child is ready
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 */
135 MACH_PORT_NULL
, /* rcv_name */
137 MACH_PORT_NULL
); /* notify */
138 if (status
== MACH_SEND_TIMED_OUT
) {
139 mach_msg_destroy(&msg
.header
);
147 childrenReaped(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
150 childInfoRef reapedChildren
= NULL
;
153 struct rusage rusage
;
156 pid
= wait4(-1, &status
, WNOHANG
, &rusage
);
158 case -1 : // if error
159 if (errno
!= ECHILD
) {
164 case 0 : // if no more children
171 // grab the activeChildren mutex
172 pthread_mutex_lock(&lock
);
175 this = activeChildren
;
177 if (this->pid
== pid
) {
178 /* save exit status & usage */
179 this->status
= status
;
180 this->rusage
= rusage
;
182 /* remove from activeChildren */
184 last
->next
= this->next
;
186 activeChildren
= this->next
;
189 /* add to reapedChildren */
190 this->next
= reapedChildren
;
191 reapedChildren
= this;
195 /* if not this child */
201 // release the activeChildren mutex
202 pthread_mutex_unlock(&lock
);
210 * we need to know about any new children waiting to be reaped so
211 * re-enable the SIGCHLD handler.
216 while (reapedChildren
) {
217 childInfoRef child
= reapedChildren
;
219 reapedChildren
= reapedChildren
->next
;
220 (*child
->callout
)(child
->pid
,
224 CFAllocatorDeallocate(NULL
, child
);
234 struct sigaction act
;
235 CFRunLoopSourceRef rls
;
237 // create the "a child has been reaped" notification port
238 childReaped
= CFMachPortCreate(NULL
, childrenReaped
, NULL
, NULL
);
242 mach_port_limits_t limits
;
243 kern_return_t status
;
245 limits
.mpl_qlimit
= 1;
246 status
= mach_port_set_attributes(mach_task_self(),
247 CFMachPortGetPort(childReaped
),
248 MACH_PORT_LIMITS_INFO
,
249 (mach_port_info_t
)&limits
,
250 MACH_PORT_LIMITS_INFO_COUNT
);
251 if (status
!= KERN_SUCCESS
) {
252 perror("mach_port_set_attributes");
256 // add to our runloop
257 rls
= CFMachPortCreateRunLoopSource(NULL
, childReaped
, 0);
258 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
261 // enable signal handler
262 act
.sa_handler
= reaper
;
263 sigemptyset(&act
.sa_mask
);
264 act
.sa_flags
= SA_RESTART
|SA_NOCLDSTOP
;
265 if (sigaction(SIGCHLD
, &act
, NULL
) == -1) {
274 _SCDPluginExecCommand2(SCDPluginExecCallBack callout
,
280 SCDPluginExecSetup setup
,
286 // grab the activeChildren mutex
287 pthread_mutex_lock(&lock
);
292 case -1 : { /* if error */
297 printf("fork() failed: %s\n", strerror(status
));
302 case 0 : { /* if child */
308 (setup
)(pid
, setupContext
);
310 /* close any open FDs */
311 for (i
= getdtablesize()-1; i
>=0; i
--) close(i
);
312 open(_PATH_DEVNULL
, O_RDWR
, 0);
317 if (gid
!= getegid()) {
321 if (uid
!= geteuid()) {
325 /* ensure that our PATH environment variable is somewhat reasonable */
326 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) {
327 printf("setenv() failed: %s\n", strerror(errno
));
331 /* execute requested command */
332 (void) execv(path
, argv
);
334 /* if the execv failed */
335 status
= W_EXITCODE(errno
, 0);
336 _exit (WEXITSTATUS(status
));
339 default : { /* if parent */
341 (setup
)(pid
, setupContext
);
347 // create child process info
348 child
= CFAllocatorAllocate(NULL
, sizeof(struct childInfo
), 0);
349 bzero(child
, sizeof(struct childInfo
));
351 child
->callout
= callout
;
352 child
->context
= context
;
354 // add the new child to the activeChildren list
355 child
->next
= activeChildren
;
356 activeChildren
= child
;
362 // release the activeChildren mutex
363 pthread_mutex_unlock(&lock
);
370 _SCDPluginExecCommand(SCDPluginExecCallBack callout
,
377 return _SCDPluginExecCommand2(callout
, context
, uid
, gid
, path
, argv
, NULL
, NULL
);