this repo has no description
at fixPythonPipStalling 417 lines 8.9 kB view raw
1/* 2 * Copyright (c) 2002-2006, 2013, 2015, 2017, 2018 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * Modification History 26 * 27 * October 12, 2001 Allan Nathanson <ajn@apple.com> 28 * - initial revision 29 */ 30 31#include <fcntl.h> 32#include <paths.h> 33#include <pwd.h> 34#include <pthread.h> 35#include <unistd.h> 36#include <sysexits.h> 37#include <sys/types.h> 38#include <sys/ioctl.h> 39#include <sys/socket.h> 40#include <sys/wait.h> 41#include <mach/mach.h> 42#include <mach/mach_error.h> 43 44#include <CoreFoundation/CoreFoundation.h> 45#include <SystemConfiguration/SCDPlugin.h> 46#include <SystemConfiguration/SCPrivate.h> 47 48 49 50typedef struct childInfo *childInfoRef; 51 52struct childInfo { 53 pid_t pid; 54 SCDPluginExecCallBack callout; 55 void *context; 56 int status; 57 struct rusage rusage; 58 childInfoRef next; 59}; 60 61 62/* 63 * Mach port used to notify runloop when a child process 64 * has been reaped. 65 */ 66static CFMachPortRef childReaped = NULL; 67 68/* 69 * The following dictionaries contain information about child 70 * processes, reaped processes, and any associated callback 71 * information. 72 * 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 76 * has been blocked). 77 */ 78static childInfoRef activeChildren = NULL; 79static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 80 81 82static __inline__ void 83blockSignal() 84{ 85 sigset_t mask = sigmask(SIGCHLD); 86 87 // block SIGCHLD 88 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { 89 perror("sigprocmask(SIG_BLOCK)"); 90 } 91 92 return; 93} 94 95 96static __inline__ void 97unblockSignal() 98{ 99 sigset_t mask = sigmask(SIGCHLD); 100 101 // unblock SIGCHLD 102 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) { 103 perror("sigprocmask(SIG_UNBLOCK)"); 104 } 105 106 return; 107} 108 109 110static void 111reaper(int sigraised) 112{ 113#pragma unused(sigraised) 114 /* 115 * block additional SIGCHLD's until current children have 116 * been reaped. 117 */ 118 blockSignal(); 119 120 /* 121 * send message to indicate that at least one child is ready 122 * to be reaped. 123 */ 124 _SC_sendMachMessage(CFMachPortGetPort(childReaped), 0); 125 126 return; 127} 128 129 130static void 131childrenReaped(CFMachPortRef port, void *msg, CFIndex size, void *info) 132{ 133#pragma unused(port) 134#pragma unused(msg) 135#pragma unused(size) 136#pragma unused(info) 137 pid_t pid = 0; 138 childInfoRef reapedChildren = NULL; 139 140 do { 141 struct rusage rusage; 142 int status; 143 144 pid = wait4(-1, &status, WNOHANG, &rusage); 145 switch (pid) { 146 case -1 : // if error 147 if (errno != ECHILD) { 148 perror("wait4"); 149 } 150 break; 151 152 case 0 : // if no more children 153 break; 154 155 default : { 156 childInfoRef last; 157 childInfoRef this; 158 159 // grab the activeChildren mutex 160 pthread_mutex_lock(&lock); 161 162 last = NULL; 163 this = activeChildren; 164 while (this) { 165 if (this->pid == pid) { 166 /* save exit status & usage */ 167 this->status = status; 168 this->rusage = rusage; 169 170 /* remove from activeChildren */ 171 if (last) { 172 last->next = this->next; 173 } else { 174 activeChildren = this->next; 175 } 176 177 /* add to reapedChildren */ 178 this->next = reapedChildren; 179 reapedChildren = this; 180 181 break; 182 } else { 183 /* if not this child */ 184 last = this; 185 this = this->next; 186 } 187 } 188 189 // release the activeChildren mutex 190 pthread_mutex_unlock(&lock); 191 192 break; 193 } 194 } 195 } while (pid > 0); 196 197 /* 198 * we need to know about any new children waiting to be reaped so 199 * re-enable the SIGCHLD handler. 200 201 */ 202 unblockSignal(); 203 204 while (reapedChildren) { 205 childInfoRef child = reapedChildren; 206 207 reapedChildren = reapedChildren->next; 208 (*child->callout)(child->pid, 209 child->status, 210 &child->rusage, 211 child->context); 212 CFAllocatorDeallocate(NULL, child); 213 } 214 215 return; 216} 217 218 219static CFStringRef 220childReapedMPCopyDescription(const void *info) 221{ 222#pragma unused(info) 223 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGCHLD MP>")); 224} 225 226 227void 228_SCDPluginExecInit(void) 229{ 230 struct sigaction act; 231 CFMachPortContext context = { 0 232 , (void *)1 233 , NULL 234 , NULL 235 , childReapedMPCopyDescription 236 }; 237 238 CFRunLoopSourceRef rls; 239 240 // create the "a child has been reaped" notification port 241 childReaped = CFMachPortCreate(NULL, childrenReaped, &context, NULL); 242 243 // set queue limit 244 { 245 mach_port_limits_t limits; 246 kern_return_t status; 247 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"); 256 } 257 } 258 259 // add to our runloop 260 rls = CFMachPortCreateRunLoopSource(NULL, childReaped, 0); 261 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 262 CFRelease(rls); 263 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) { 269 perror("sigaction"); 270 } 271 272 return; 273} 274 275 276pid_t 277_SCDPluginExecCommand2(SCDPluginExecCallBack callout, 278 void *context, 279 uid_t uid, 280 gid_t gid, 281 const char *path, 282 char * const argv[], 283 SCDPluginExecSetup setup, 284 void *setupContext 285 ) 286{ 287 char buf[1024]; 288 pid_t pid; 289 struct passwd pwd; 290 struct passwd *result = NULL; 291 char *username = NULL; 292 293 // grab the activeChildren mutex 294 pthread_mutex_lock(&lock); 295 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) && 299 (result != NULL)) { 300 username = result->pw_name; 301 } 302 303 // if needed, initialize 304 if (childReaped == NULL) { 305 _SCDPluginExecInit(); 306 } 307 308 pid = fork(); 309 310 switch (pid) { 311 case -1 : { /* if error */ 312 313 int status; 314 315 status = errno; 316 printf("fork() failed: %s\n", strerror(status)); 317 errno = status; 318 break; 319 } 320 321 case 0 : { /* if child */ 322 323 gid_t egid; 324 uid_t euid; 325 int status; 326 327 if (setup != NULL) { 328 (setup)(pid, setupContext); 329 } else { 330 int fd; 331 int i; 332 333 /* close any open FDs */ 334 for (i = getdtablesize()-1; i>=0; i--) close(i); 335 336 /* stdin, stdout, stderr */ 337 fd = open(_PATH_DEVNULL, O_RDWR, 0); 338 if (fd != -1) { 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)) { 343 (void) close(fd); 344 } 345 346 } 347 } 348 349 egid = getegid(); 350 euid = geteuid(); 351 352 if (egid != gid) { 353 (void) setgid(gid); 354 } 355 356 if (((euid != uid) || (egid != gid)) && username) { 357 initgroups(username, gid); 358 } 359 360 if (euid != uid) { 361 (void) setuid(uid); 362 } 363 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)); 367 exit(EX_OSERR); 368 } 369 370 /* execute requested command */ 371 (void) execv(path, argv); 372 373 /* if the execv failed */ 374 status = W_EXITCODE(errno, 0); 375 _exit (WEXITSTATUS(status)); 376 } 377 378 default : { /* if parent */ 379 if (setup != NULL) { 380 (setup)(pid, setupContext); 381 } 382 383 if (callout != NULL) { 384 childInfoRef child; 385 386 // create child process info 387 child = CFAllocatorAllocate(NULL, sizeof(struct childInfo), 0); 388 memset(child, 0, sizeof(struct childInfo)); 389 child->pid = pid; 390 child->callout = callout; 391 child->context = context; 392 393 // add the new child to the activeChildren list 394 child->next = activeChildren; 395 activeChildren = child; 396 } 397 break; 398 } 399 } 400 401 // release the activeChildren mutex 402 pthread_mutex_unlock(&lock); 403 404 return pid; 405} 406 407 408pid_t 409_SCDPluginExecCommand(SCDPluginExecCallBack callout, 410 void *context, 411 uid_t uid, 412 gid_t gid, 413 const char *path, 414 char * const argv[]) 415{ 416 return _SCDPluginExecCommand2(callout, context, uid, gid, path, argv, NULL, NULL); 417}