2 * Copyright (c) 2002-2006, 2013, 2015, 2017, 2018 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
)
113 #pragma unused(sigraised)
115 * block additional SIGCHLD's until current children have
121 * send message to indicate that at least one child is ready
124 _SC_sendMachMessage(CFMachPortGetPort(childReaped
), 0);
131 childrenReaped(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
138 childInfoRef reapedChildren
= NULL
;
141 struct rusage rusage
;
144 pid
= wait4(-1, &status
, WNOHANG
, &rusage
);
146 case -1 : // if error
147 if (errno
!= ECHILD
) {
152 case 0 : // if no more children
159 // grab the activeChildren mutex
160 pthread_mutex_lock(&lock
);
163 this = activeChildren
;
165 if (this->pid
== pid
) {
166 /* save exit status & usage */
167 this->status
= status
;
168 this->rusage
= rusage
;
170 /* remove from activeChildren */
172 last
->next
= this->next
;
174 activeChildren
= this->next
;
177 /* add to reapedChildren */
178 this->next
= reapedChildren
;
179 reapedChildren
= this;
183 /* if not this child */
189 // release the activeChildren mutex
190 pthread_mutex_unlock(&lock
);
198 * we need to know about any new children waiting to be reaped so
199 * re-enable the SIGCHLD handler.
204 while (reapedChildren
) {
205 childInfoRef child
= reapedChildren
;
207 reapedChildren
= reapedChildren
->next
;
208 (*child
->callout
)(child
->pid
,
212 CFAllocatorDeallocate(NULL
, child
);
220 childReapedMPCopyDescription(const void *info
)
223 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SIGCHLD MP>"));
228 _SCDPluginExecInit(void)
230 struct sigaction act
;
231 CFMachPortContext context
= { 0
235 , childReapedMPCopyDescription
238 CFRunLoopSourceRef rls
;
240 // create the "a child has been reaped" notification port
241 childReaped
= CFMachPortCreate(NULL
, childrenReaped
, &context
, NULL
);
245 mach_port_limits_t limits
;
246 kern_return_t status
;
248 limits
.mpl_qlimit
= 1;
249 status
= mach_port_set_attributes(mach_task_self(),
250 CFMachPortGetPort(childReaped
),
251 MACH_PORT_LIMITS_INFO
,
252 (mach_port_info_t
)&limits
,
253 MACH_PORT_LIMITS_INFO_COUNT
);
254 if (status
!= KERN_SUCCESS
) {
255 perror("mach_port_set_attributes");
259 // add to our runloop
260 rls
= CFMachPortCreateRunLoopSource(NULL
, childReaped
, 0);
261 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
264 // enable signal handler
265 act
.sa_handler
= reaper
;
266 sigemptyset(&act
.sa_mask
);
267 act
.sa_flags
= SA_RESTART
|SA_NOCLDSTOP
;
268 if (sigaction(SIGCHLD
, &act
, NULL
) == -1) {
277 _SCDPluginExecCommand2(SCDPluginExecCallBack callout
,
283 SCDPluginExecSetup setup
,
290 struct passwd
*result
= NULL
;
291 char *username
= NULL
;
293 // grab the activeChildren mutex
294 pthread_mutex_lock(&lock
);
296 // cache the getpwuid_r result here to avoid spinning that can happen
297 // when calling it between fork and execv.
298 if ((getpwuid_r(uid
, &pwd
, buf
, sizeof(buf
), &result
) == 0) &&
300 username
= result
->pw_name
;
303 // if needed, initialize
304 if (childReaped
== NULL
) {
305 _SCDPluginExecInit();
311 case -1 : { /* if error */
316 printf("fork() failed: %s\n", strerror(status
));
321 case 0 : { /* if child */
328 (setup
)(pid
, setupContext
);
333 /* close any open FDs */
334 for (i
= getdtablesize()-1; i
>=0; i
--) close(i
);
336 /* stdin, stdout, stderr */
337 fd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
339 (void) dup2(fd
, STDIN_FILENO
);
340 (void) dup2(fd
, STDOUT_FILENO
);
341 (void) dup2(fd
, STDERR_FILENO
);
342 if ((fd
!= STDIN_FILENO
) && (fd
!= STDOUT_FILENO
) && (fd
!= STDERR_FILENO
)) {
356 if (((euid
!= uid
) || (egid
!= gid
)) && username
) {
357 initgroups(username
, gid
);
364 /* ensure that our PATH environment variable is somewhat reasonable */
365 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) {
366 printf("setenv() failed: %s\n", strerror(errno
));
370 /* execute requested command */
371 (void) execv(path
, argv
);
373 /* if the execv failed */
374 status
= W_EXITCODE(errno
, 0);
375 _exit (WEXITSTATUS(status
));
378 default : { /* if parent */
380 (setup
)(pid
, setupContext
);
383 if (callout
!= NULL
) {
386 // create child process info
387 child
= CFAllocatorAllocate(NULL
, sizeof(struct childInfo
), 0);
388 memset(child
, 0, sizeof(struct childInfo
));
390 child
->callout
= callout
;
391 child
->context
= context
;
393 // add the new child to the activeChildren list
394 child
->next
= activeChildren
;
395 activeChildren
= child
;
401 // release the activeChildren mutex
402 pthread_mutex_unlock(&lock
);
409 _SCDPluginExecCommand(SCDPluginExecCallBack callout
,
416 return _SCDPluginExecCommand2(callout
, context
, uid
, gid
, path
, argv
, NULL
, NULL
);