2 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
24 * Modification History
26 * October 12, 2001 Allan Nathanson <ajn@apple.com>
33 #include <sys/types.h>
35 #include <mach/mach.h>
36 #include <mach/mach_error.h>
38 #include <CoreFoundation/CoreFoundation.h>
39 #include <SystemConfiguration/SCDPlugin.h>
43 typedef struct childInfo
*childInfoRef
;
47 SCDPluginExecCallBack callout
;
56 * Mach port used to notify runloop when a child process
59 static CFMachPortRef childReaped
= NULL
;
62 * The following dictionaries contain information about child
63 * processes, reaped processes, and any associated callback
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
71 static childInfoRef activeChildren
= NULL
;
72 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
78 sigset_t mask
= sigmask(SIGCHLD
);
81 if (sigprocmask(SIG_BLOCK
, &mask
, NULL
) == -1) {
82 perror("sigprocmask(SIG_BLOCK)");
92 sigset_t mask
= sigmask(SIGCHLD
);
95 if (sigprocmask(SIG_UNBLOCK
, &mask
, NULL
) == -1) {
96 perror("sigprocmask(SIG_UNBLOCK)");
104 reaper(int sigraised
)
106 mach_msg_empty_send_t msg
;
107 mach_msg_option_t options
;
108 kern_return_t status
;
111 * block additional SIGCHLD's until current children have
117 * send message to indicate that at least one child is ready
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 */
130 MACH_PORT_NULL
, /* rcv_name */
132 MACH_PORT_NULL
); /* notify */
139 childrenReaped(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
142 childInfoRef reapedChildren
= NULL
;
145 struct rusage rusage
;
148 pid
= wait4(-1, &status
, WNOHANG
, &rusage
);
150 case -1 : // if error
151 if (errno
!= ECHILD
) {
156 case 0 : // if no more children
163 // grab the activeChildren mutex
164 pthread_mutex_lock(&lock
);
167 this = activeChildren
;
169 if (this->pid
== pid
) {
170 /* save exit status & usage */
171 this->status
= status
;
172 this->rusage
= rusage
;
174 /* remove from activeChildren */
176 last
->next
= this->next
;
178 activeChildren
= this->next
;
181 /* add to reapedChildren */
182 this->next
= reapedChildren
;
183 reapedChildren
= this;
187 /* if not this child */
193 // release the activeChildren mutex
194 pthread_mutex_unlock(&lock
);
202 * we need to know about any new children waiting to be reaped so
203 * re-enable the SIGCHLD handler.
208 while (reapedChildren
) {
209 childInfoRef child
= reapedChildren
;
211 reapedChildren
= reapedChildren
->next
;
212 (*child
->callout
)(child
->pid
,
216 CFAllocatorDeallocate(NULL
, child
);
226 struct sigaction act
;
227 CFRunLoopSourceRef rls
;
229 // create the "a child has been reaped" notification port
230 childReaped
= CFMachPortCreate(NULL
, childrenReaped
, NULL
, NULL
);
234 mach_port_limits_t limits
;
235 kern_return_t status
;
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");
248 // add to our runloop
249 rls
= CFMachPortCreateRunLoopSource(NULL
, childReaped
, 0);
250 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
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) {
266 _SCDPluginExecCommand(SCDPluginExecCallBack callout
,
275 // grab the activeChildren mutex
276 pthread_mutex_lock(&lock
);
280 case -1 : { /* if error */
285 printf("fork() failed: %s", strerror(status
));
290 case 0 : { /* if child */
292 uid_t curUID
= geteuid();
293 gid_t curGID
= getegid();
305 /* close any open FDs */
306 for (i
= getdtablesize()-1; i
>=0; i
--) close(i
);
307 open("/dev/null", O_RDWR
, 0);
311 /* execute requested command */
312 (void) execv(path
, argv
);
314 /* if the execv failed */
315 status
= W_EXITCODE(errno
, 0);
316 _exit (WEXITSTATUS(status
));
319 default : { /* if parent */
324 // create child process info
325 child
= CFAllocatorAllocate(NULL
, sizeof(struct childInfo
), 0);
326 bzero(child
, sizeof(struct childInfo
));
328 child
->callout
= callout
;
329 child
->context
= context
;
331 // add the new child to the activeChildren list
332 child
->next
= activeChildren
;
333 activeChildren
= child
;
339 // release the activeChildren mutex
340 pthread_mutex_unlock(&lock
);