Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
8.9 kB · 417 lines
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418/* * Copyright (c) 2002-2006, 2013, 2015, 2017, 2018 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */
/* * Modification History * * October 12, 2001 Allan Nathanson <ajn@apple.com> * - initial revision */
#include <fcntl.h>#include <paths.h>#include <pwd.h>#include <pthread.h>#include <unistd.h>#include <sysexits.h>#include <sys/types.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/wait.h>#include <mach/mach.h>#include <mach/mach_error.h>
#include <CoreFoundation/CoreFoundation.h>#include <SystemConfiguration/SCDPlugin.h>#include <SystemConfiguration/SCPrivate.h>
typedef struct childInfo *childInfoRef;
struct childInfo { pid_t pid; SCDPluginExecCallBack callout; void *context; int status; struct rusage rusage; childInfoRef next;};
/* * Mach port used to notify runloop when a child process * has been reaped. */static CFMachPortRef childReaped = NULL;
/* * The following dictionaries contain information about child * processes, reaped processes, and any associated callback * information. * * Important: Access to these dictionaries should only be * made when in a SIGCHLD handler (or when the * childLock mutex is held *AND* the signal * has been blocked). */static childInfoRef activeChildren = NULL;static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static __inline__ voidblockSignal(){ sigset_t mask = sigmask(SIGCHLD);
// block SIGCHLD if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { perror("sigprocmask(SIG_BLOCK)"); }
return;}
static __inline__ voidunblockSignal(){ sigset_t mask = sigmask(SIGCHLD);
// unblock SIGCHLD if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) { perror("sigprocmask(SIG_UNBLOCK)"); }
return;}
static voidreaper(int sigraised){#pragma unused(sigraised) /* * block additional SIGCHLD's until current children have * been reaped. */ blockSignal();
/* * send message to indicate that at least one child is ready * to be reaped. */ _SC_sendMachMessage(CFMachPortGetPort(childReaped), 0);
return;}
static voidchildrenReaped(CFMachPortRef port, void *msg, CFIndex size, void *info){#pragma unused(port)#pragma unused(msg)#pragma unused(size)#pragma unused(info) pid_t pid = 0; childInfoRef reapedChildren = NULL;
do { struct rusage rusage; int status;
pid = wait4(-1, &status, WNOHANG, &rusage); switch (pid) { case -1 : // if error if (errno != ECHILD) { perror("wait4"); } break;
case 0 : // if no more children break;
default : { childInfoRef last; childInfoRef this;
// grab the activeChildren mutex pthread_mutex_lock(&lock);
last = NULL; this = activeChildren; while (this) { if (this->pid == pid) { /* save exit status & usage */ this->status = status; this->rusage = rusage;
/* remove from activeChildren */ if (last) { last->next = this->next; } else { activeChildren = this->next; }
/* add to reapedChildren */ this->next = reapedChildren; reapedChildren = this;
break; } else { /* if not this child */ last = this; this = this->next; } }
// release the activeChildren mutex pthread_mutex_unlock(&lock);
break; } } } while (pid > 0);
/* * we need to know about any new children waiting to be reaped so * re-enable the SIGCHLD handler.
*/ unblockSignal();
while (reapedChildren) { childInfoRef child = reapedChildren;
reapedChildren = reapedChildren->next; (*child->callout)(child->pid, child->status, &child->rusage, child->context); CFAllocatorDeallocate(NULL, child); }
return;}
static CFStringRefchildReapedMPCopyDescription(const void *info){#pragma unused(info) return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGCHLD MP>"));}
void_SCDPluginExecInit(void){ struct sigaction act; CFMachPortContext context = { 0 , (void *)1 , NULL , NULL , childReapedMPCopyDescription };
CFRunLoopSourceRef rls;
// create the "a child has been reaped" notification port childReaped = CFMachPortCreate(NULL, childrenReaped, &context, NULL);
// set queue limit { mach_port_limits_t limits; kern_return_t status;
limits.mpl_qlimit = 1; status = mach_port_set_attributes(mach_task_self(), CFMachPortGetPort(childReaped), MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT); if (status != KERN_SUCCESS) { perror("mach_port_set_attributes"); } }
// add to our runloop rls = CFMachPortCreateRunLoopSource(NULL, childReaped, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); CFRelease(rls);
// enable signal handler act.sa_handler = reaper; sigemptyset(&act.sa_mask); act.sa_flags = SA_RESTART|SA_NOCLDSTOP; if (sigaction(SIGCHLD, &act, NULL) == -1) { perror("sigaction"); }
return;}
pid_t_SCDPluginExecCommand2(SCDPluginExecCallBack callout, void *context, uid_t uid, gid_t gid, const char *path, char * const argv[], SCDPluginExecSetup setup, void *setupContext ){ char buf[1024]; pid_t pid; struct passwd pwd; struct passwd *result = NULL; char *username = NULL;
// grab the activeChildren mutex pthread_mutex_lock(&lock);
// cache the getpwuid_r result here to avoid spinning that can happen // when calling it between fork and execv. if ((getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0) && (result != NULL)) { username = result->pw_name; }
// if needed, initialize if (childReaped == NULL) { _SCDPluginExecInit(); }
pid = fork();
switch (pid) { case -1 : { /* if error */
int status;
status = errno; printf("fork() failed: %s\n", strerror(status)); errno = status; break; }
case 0 : { /* if child */
gid_t egid; uid_t euid; int status;
if (setup != NULL) { (setup)(pid, setupContext); } else { int fd; int i;
/* close any open FDs */ for (i = getdtablesize()-1; i>=0; i--) close(i);
/* stdin, stdout, stderr */ fd = open(_PATH_DEVNULL, O_RDWR, 0); if (fd != -1) { (void) dup2(fd, STDIN_FILENO); (void) dup2(fd, STDOUT_FILENO); (void) dup2(fd, STDERR_FILENO); if ((fd != STDIN_FILENO) && (fd != STDOUT_FILENO) && (fd != STDERR_FILENO)) { (void) close(fd); }
} }
egid = getegid(); euid = geteuid();
if (egid != gid) { (void) setgid(gid); }
if (((euid != uid) || (egid != gid)) && username) { initgroups(username, gid); }
if (euid != uid) { (void) setuid(uid); }
/* ensure that our PATH environment variable is somewhat reasonable */ if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) { printf("setenv() failed: %s\n", strerror(errno)); exit(EX_OSERR); }
/* execute requested command */ (void) execv(path, argv);
/* if the execv failed */ status = W_EXITCODE(errno, 0); _exit (WEXITSTATUS(status)); }
default : { /* if parent */ if (setup != NULL) { (setup)(pid, setupContext); }
if (callout != NULL) { childInfoRef child;
// create child process info child = CFAllocatorAllocate(NULL, sizeof(struct childInfo), 0); memset(child, 0, sizeof(struct childInfo)); child->pid = pid; child->callout = callout; child->context = context;
// add the new child to the activeChildren list child->next = activeChildren; activeChildren = child; } break; } }
// release the activeChildren mutex pthread_mutex_unlock(&lock);
return pid;}
pid_t_SCDPluginExecCommand(SCDPluginExecCallBack callout, void *context, uid_t uid, gid_t gid, const char *path, char * const argv[]){ return _SCDPluginExecCommand2(callout, context, uid, gid, path, argv, NULL, NULL);}