1/* 2 * Copyright (c) 2002-2006, 2013 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 /* 114 * block additional SIGCHLD's until current children have 115 * been reaped. 116 */ 117 blockSignal(); 118 119 /* 120 * send message to indicate that at least one child is ready 121 * to be reaped. 122 */ 123 _SC_sendMachMessage(CFMachPortGetPort(childReaped), 0); 124 125 return; 126} 127 128 129static void 130childrenReaped(CFMachPortRef port, void *msg, CFIndex size, void *info) 131{ 132 pid_t pid = 0; 133 childInfoRef reapedChildren = NULL; 134 135 do { 136 struct rusage rusage; 137 int status; 138 139 pid = wait4(-1, &status, WNOHANG, &rusage); 140 switch (pid) { 141 case -1 : // if error 142 if (errno != ECHILD) { 143 perror("wait4"); 144 } 145 break; 146 147 case 0 : // if no more children 148 break; 149 150 default : { 151 childInfoRef last; 152 childInfoRef this; 153 154 // grab the activeChildren mutex 155 pthread_mutex_lock(&lock); 156 157 last = NULL; 158 this = activeChildren; 159 while (this) { 160 if (this->pid == pid) { 161 /* save exit status & usage */ 162 this->status = status; 163 this->rusage = rusage; 164 165 /* remove from activeChildren */ 166 if (last) { 167 last->next = this->next; 168 } else { 169 activeChildren = this->next; 170 } 171 172 /* add to reapedChildren */ 173 this->next = reapedChildren; 174 reapedChildren = this; 175 176 break; 177 } else { 178 /* if not this child */ 179 last = this; 180 this = this->next; 181 } 182 } 183 184 // release the activeChildren mutex 185 pthread_mutex_unlock(&lock); 186 187 break; 188 } 189 } 190 } while (pid > 0); 191 192 /* 193 * we need to know about any new children waiting to be reaped so 194 * re-enable the SIGCHLD handler. 195 196 */ 197 unblockSignal(); 198 199 while (reapedChildren) { 200 childInfoRef child = reapedChildren; 201 202 reapedChildren = reapedChildren->next; 203 (*child->callout)(child->pid, 204 child->status, 205 &child->rusage, 206 child->context); 207 CFAllocatorDeallocate(NULL, child); 208 } 209 210 return; 211} 212 213 214static CFStringRef 215childReapedMPCopyDescription(const void *info) 216{ 217 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGCHLD MP>")); 218} 219 220 221void 222_SCDPluginExecInit() 223{ 224 struct sigaction act; 225 CFMachPortContext context = { 0 226 , (void *)1 227 , NULL 228 , NULL 229 , childReapedMPCopyDescription 230 }; 231 232 CFRunLoopSourceRef rls; 233 234 // create the "a child has been reaped" notification port 235 childReaped = CFMachPortCreate(NULL, childrenReaped, &context, NULL); 236 237 // set queue limit 238 { 239 mach_port_limits_t limits; 240 kern_return_t status; 241 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"); 250 } 251 } 252 253 // add to our runloop 254 rls = CFMachPortCreateRunLoopSource(NULL, childReaped, 0); 255 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 256 CFRelease(rls); 257 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) { 263 perror("sigaction"); 264 } 265 266 return; 267} 268 269 270pid_t 271_SCDPluginExecCommand2(SCDPluginExecCallBack callout, 272 void *context, 273 uid_t uid, 274 gid_t gid, 275 const char *path, 276 char * const argv[], 277 SCDPluginExecSetup setup, 278 void *setupContext 279 ) 280{ 281 char buf[1024]; 282 pid_t pid; 283 struct passwd pwd; 284 struct passwd *result = NULL; 285 char *username = NULL; 286 287 // grab the activeChildren mutex 288 pthread_mutex_lock(&lock); 289 290 // cache the getpwuid_r result here to avoid spinning that can happen 291 // when calling it between fork and execv. 292 if ((getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0) && 293 (result != NULL)) { 294 username = result->pw_name; 295 } 296 297 // if needed, initialize 298 if (childReaped == NULL) { 299 _SCDPluginExecInit(); 300 } 301 302 pid = fork(); 303 304 switch (pid) { 305 case -1 : { /* if error */ 306 307 int status; 308 309 status = errno; 310 printf("fork() failed: %s\n", strerror(status)); 311 errno = status; 312 break; 313 } 314 315 case 0 : { /* if child */ 316 317 gid_t egid; 318 uid_t euid; 319 int i; 320 int status; 321 322 if (setup != NULL) { 323 (setup)(pid, setupContext); 324 } else { 325 /* close any open FDs */ 326 for (i = getdtablesize()-1; i>=0; i--) close(i); 327 open(_PATH_DEVNULL, O_RDWR, 0); 328 dup(0); 329 dup(0); 330 } 331 332 egid = getegid(); 333 euid = geteuid(); 334 335 if (egid != gid) { 336 (void) setgid(gid); 337 } 338 339 if (((euid != uid) || (egid != gid)) && username) { 340 initgroups(username, gid); 341 } 342 343 if (euid != uid) { 344 (void) setuid(uid); 345 } 346 347 /* ensure that our PATH environment variable is somewhat reasonable */ 348 if (setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 0) == -1) { 349 printf("setenv() failed: %s\n", strerror(errno)); 350 exit(EX_OSERR); 351 } 352 353 /* execute requested command */ 354 (void) execv(path, argv); 355 356 /* if the execv failed */ 357 status = W_EXITCODE(errno, 0); 358 _exit (WEXITSTATUS(status)); 359 } 360 361 default : { /* if parent */ 362 if (setup != NULL) { 363 (setup)(pid, setupContext); 364 } 365 366 if (callout != NULL) { 367 childInfoRef child; 368 369 // create child process info 370 child = CFAllocatorAllocate(NULL, sizeof(struct childInfo), 0); 371 bzero(child, sizeof(struct childInfo)); 372 child->pid = pid; 373 child->callout = callout; 374 child->context = context; 375 376 // add the new child to the activeChildren list 377 child->next = activeChildren; 378 activeChildren = child; 379 } 380 break; 381 } 382 } 383 384 // release the activeChildren mutex 385 pthread_mutex_unlock(&lock); 386 387 return pid; 388} 389 390 391pid_t 392_SCDPluginExecCommand(SCDPluginExecCallBack callout, 393 void *context, 394 uid_t uid, 395 gid_t gid, 396 const char *path, 397 char * const argv[]) 398{ 399 return _SCDPluginExecCommand2(callout, context, uid, gid, path, argv, NULL, NULL); 400} 401