2 * Copyright (c) 2001-2003 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 */
144 childrenReaped(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
147 childInfoRef reapedChildren
= NULL
;
150 struct rusage rusage
;
153 pid
= wait4(-1, &status
, WNOHANG
, &rusage
);
155 case -1 : // if error
156 if (errno
!= ECHILD
) {
161 case 0 : // if no more children
168 // grab the activeChildren mutex
169 pthread_mutex_lock(&lock
);
172 this = activeChildren
;
174 if (this->pid
== pid
) {
175 /* save exit status & usage */
176 this->status
= status
;
177 this->rusage
= rusage
;
179 /* remove from activeChildren */
181 last
->next
= this->next
;
183 activeChildren
= this->next
;
186 /* add to reapedChildren */
187 this->next
= reapedChildren
;
188 reapedChildren
= this;
192 /* if not this child */
198 // release the activeChildren mutex
199 pthread_mutex_unlock(&lock
);
207 * we need to know about any new children waiting to be reaped so
208 * re-enable the SIGCHLD handler.
213 while (reapedChildren
) {
214 childInfoRef child
= reapedChildren
;
216 reapedChildren
= reapedChildren
->next
;
217 (*child
->callout
)(child
->pid
,
221 CFAllocatorDeallocate(NULL
, child
);
231 struct sigaction act
;
232 CFRunLoopSourceRef rls
;
234 // create the "a child has been reaped" notification port
235 childReaped
= CFMachPortCreate(NULL
, childrenReaped
, NULL
, 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
,
283 // grab the activeChildren mutex
284 pthread_mutex_lock(&lock
);
289 case -1 : { /* if error */
294 printf("fork() failed: %s\n", strerror(status
));
299 case 0 : { /* if child */
301 uid_t curUID
= geteuid();
302 gid_t curGID
= getegid();
315 (setup
)(pid
, setupContext
);
317 /* close any open FDs */
318 for (i
= getdtablesize()-1; i
>=0; i
--) close(i
);
319 open(_PATH_DEVNULL
, O_RDWR
, 0);
324 /* ensure that our PATH environment variable is somewhat reasonable */
325 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) {
326 printf("setenv() failed: %s\n", strerror(errno
));
330 /* execute requested command */
331 (void) execv(path
, argv
);
333 /* if the execv failed */
334 status
= W_EXITCODE(errno
, 0);
335 _exit (WEXITSTATUS(status
));
338 default : { /* if parent */
340 (setup
)(pid
, setupContext
);
346 // create child process info
347 child
= CFAllocatorAllocate(NULL
, sizeof(struct childInfo
), 0);
348 bzero(child
, sizeof(struct childInfo
));
350 child
->callout
= callout
;
351 child
->context
= context
;
353 // add the new child to the activeChildren list
354 child
->next
= activeChildren
;
355 activeChildren
= child
;
361 // release the activeChildren mutex
362 pthread_mutex_unlock(&lock
);
369 _SCDPluginExecCommand(SCDPluginExecCallBack callout
,
376 return _SCDPluginExecCommand2(callout
, context
, uid
, gid
, path
, argv
, NULL
, NULL
);