2 * Copyright (c) 2001-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 * Modification History
29 * October 12, 2001 Allan Nathanson <ajn@apple.com>
38 #include <sys/types.h>
39 #include <sys/ioctl.h>
40 #include <sys/socket.h>
42 #include <mach/mach.h>
43 #include <mach/mach_error.h>
45 #include <CoreFoundation/CoreFoundation.h>
46 #include <SystemConfiguration/SCDPlugin.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
;
85 sigset_t mask
= sigmask(SIGCHLD
);
88 if (sigprocmask(SIG_BLOCK
, &mask
, NULL
) == -1) {
89 perror("sigprocmask(SIG_BLOCK)");
99 sigset_t mask
= sigmask(SIGCHLD
);
102 if (sigprocmask(SIG_UNBLOCK
, &mask
, NULL
) == -1) {
103 perror("sigprocmask(SIG_UNBLOCK)");
111 reaper(int sigraised
)
113 mach_msg_empty_send_t msg
;
114 mach_msg_option_t options
;
115 kern_return_t status
;
118 * block additional SIGCHLD's until current children have
124 * send message to indicate that at least one child is ready
127 msg
.header
.msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND
, 0);
128 msg
.header
.msgh_size
= sizeof(msg
);
129 msg
.header
.msgh_remote_port
= CFMachPortGetPort(childReaped
);
130 msg
.header
.msgh_local_port
= MACH_PORT_NULL
;
131 msg
.header
.msgh_id
= 0;
132 options
= MACH_SEND_TIMEOUT
;
133 status
= mach_msg(&msg
.header
, /* msg */
134 MACH_SEND_MSG
|options
, /* options */
135 msg
.header
.msgh_size
, /* send_size */
137 MACH_PORT_NULL
, /* rcv_name */
139 MACH_PORT_NULL
); /* notify */
146 childrenReaped(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
149 childInfoRef reapedChildren
= NULL
;
152 struct rusage rusage
;
155 pid
= wait4(-1, &status
, WNOHANG
, &rusage
);
157 case -1 : // if error
158 if (errno
!= ECHILD
) {
163 case 0 : // if no more children
170 // grab the activeChildren mutex
171 pthread_mutex_lock(&lock
);
174 this = activeChildren
;
176 if (this->pid
== pid
) {
177 /* save exit status & usage */
178 this->status
= status
;
179 this->rusage
= rusage
;
181 /* remove from activeChildren */
183 last
->next
= this->next
;
185 activeChildren
= this->next
;
188 /* add to reapedChildren */
189 this->next
= reapedChildren
;
190 reapedChildren
= this;
194 /* if not this child */
200 // release the activeChildren mutex
201 pthread_mutex_unlock(&lock
);
209 * we need to know about any new children waiting to be reaped so
210 * re-enable the SIGCHLD handler.
215 while (reapedChildren
) {
216 childInfoRef child
= reapedChildren
;
218 reapedChildren
= reapedChildren
->next
;
219 (*child
->callout
)(child
->pid
,
223 CFAllocatorDeallocate(NULL
, child
);
233 struct sigaction act
;
234 CFRunLoopSourceRef rls
;
236 // create the "a child has been reaped" notification port
237 childReaped
= CFMachPortCreate(NULL
, childrenReaped
, NULL
, NULL
);
241 mach_port_limits_t limits
;
242 kern_return_t status
;
244 limits
.mpl_qlimit
= 1;
245 status
= mach_port_set_attributes(mach_task_self(),
246 CFMachPortGetPort(childReaped
),
247 MACH_PORT_LIMITS_INFO
,
248 (mach_port_info_t
)&limits
,
249 MACH_PORT_LIMITS_INFO_COUNT
);
250 if (status
!= KERN_SUCCESS
) {
251 perror("mach_port_set_attributes");
255 // add to our runloop
256 rls
= CFMachPortCreateRunLoopSource(NULL
, childReaped
, 0);
257 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
260 // enable signal handler
261 act
.sa_handler
= reaper
;
262 sigemptyset(&act
.sa_mask
);
263 act
.sa_flags
= SA_RESTART
|SA_NOCLDSTOP
;
264 if (sigaction(SIGCHLD
, &act
, NULL
) == -1) {
273 _SCDPluginExecCommand2(SCDPluginExecCallBack callout
,
279 SCDPluginExecSetup setup
,
285 // grab the activeChildren mutex
286 pthread_mutex_lock(&lock
);
291 case -1 : { /* if error */
296 printf("fork() failed: %s\n", strerror(status
));
301 case 0 : { /* if child */
303 uid_t curUID
= geteuid();
304 gid_t curGID
= getegid();
317 (setup
)(pid
, setupContext
);
319 /* close any open FDs */
320 for (i
= getdtablesize()-1; i
>=0; i
--) close(i
);
321 open(_PATH_DEVNULL
, O_RDWR
, 0);
326 /* ensure that our PATH environment variable is somewhat reasonable */
327 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) {
328 printf("setenv() failed: %s\n", strerror(errno
));
332 /* execute requested command */
333 (void) execv(path
, argv
);
335 /* if the execv failed */
336 status
= W_EXITCODE(errno
, 0);
337 _exit (WEXITSTATUS(status
));
340 default : { /* if parent */
342 (setup
)(pid
, setupContext
);
348 // create child process info
349 child
= CFAllocatorAllocate(NULL
, sizeof(struct childInfo
), 0);
350 bzero(child
, sizeof(struct childInfo
));
352 child
->callout
= callout
;
353 child
->context
= context
;
355 // add the new child to the activeChildren list
356 child
->next
= activeChildren
;
357 activeChildren
= child
;
363 // release the activeChildren mutex
364 pthread_mutex_unlock(&lock
);
371 _SCDPluginExecCommand(SCDPluginExecCallBack callout
,
378 return _SCDPluginExecCommand2(callout
, context
, uid
, gid
, path
, argv
, NULL
, NULL
);