1/* 2 * "$Id: process.c 12104 2014-08-20 15:23:40Z msweet $" 3 * 4 * Process management routines for the CUPS scheduler. 5 * 6 * Copyright 2007-2014 by Apple Inc. 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 */ 15 16/* 17 * Include necessary headers... 18 */ 19 20#include "cupsd.h" 21#include <grp.h> 22#ifdef __APPLE__ 23# include <libgen.h> 24#endif /* __APPLE__ */ 25#ifdef HAVE_POSIX_SPAWN 26# include <spawn.h> 27extern char **environ; 28#endif /* HAVE_POSIX_SPAWN */ 29 30 31/* 32 * Process structure... 33 */ 34 35typedef struct 36{ 37 int pid, /* Process ID */ 38 job_id; /* Job associated with process */ 39 char name[1]; /* Name of process */ 40} cupsd_proc_t; 41 42 43/* 44 * Local globals... 45 */ 46 47static cups_array_t *process_array = NULL; 48 49 50/* 51 * Local functions... 52 */ 53 54static int compare_procs(cupsd_proc_t *a, cupsd_proc_t *b); 55#ifdef HAVE_SANDBOX_H 56static char *cupsd_requote(char *dst, const char *src, size_t dstsize); 57#endif /* HAVE_SANDBOX_H */ 58 59 60/* 61 * 'cupsdCreateProfile()' - Create an execution profile for a subprocess. 62 */ 63 64void * /* O - Profile or NULL on error */ 65cupsdCreateProfile(int job_id, /* I - Job ID or 0 for none */ 66 int allow_networking)/* I - Allow networking off machine? */ 67{ 68#ifdef HAVE_SANDBOX_H 69 cups_file_t *fp; /* File pointer */ 70 char profile[1024], /* File containing the profile */ 71 bin[1024], /* Quoted ServerBin */ 72 cache[1024], /* Quoted CacheDir */ 73 domain[1024], /* Domain socket, if any */ 74 request[1024], /* Quoted RequestRoot */ 75 root[1024], /* Quoted ServerRoot */ 76 state[1024], /* Quoted StateDir */ 77 temp[1024]; /* Quoted TempDir */ 78 const char *nodebug; /* " (with no-log)" for no debug */ 79 cupsd_listener_t *lis; /* Current listening socket */ 80 81 82 if (!UseSandboxing || Sandboxing == CUPSD_SANDBOXING_OFF) 83 { 84 /* 85 * Only use sandbox profiles as root... 86 */ 87 88 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking); 89 90 return (NULL); 91 } 92 93 if ((fp = cupsTempFile2(profile, sizeof(profile))) == NULL) 94 { 95 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking); 96 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create security profile: %s", 97 strerror(errno)); 98 return (NULL); 99 } 100 101 fchown(cupsFileNumber(fp), RunUser, Group); 102 fchmod(cupsFileNumber(fp), 0640); 103 104 cupsd_requote(bin, ServerBin, sizeof(bin)); 105 cupsd_requote(cache, CacheDir, sizeof(cache)); 106 cupsd_requote(request, RequestRoot, sizeof(request)); 107 cupsd_requote(root, ServerRoot, sizeof(root)); 108 cupsd_requote(state, StateDir, sizeof(state)); 109 cupsd_requote(temp, TempDir, sizeof(temp)); 110 111 nodebug = LogLevel < CUPSD_LOG_DEBUG ? " (with no-log)" : ""; 112 113 cupsFilePuts(fp, "(version 1)\n"); 114 if (Sandboxing == CUPSD_SANDBOXING_STRICT) 115 cupsFilePuts(fp, "(deny default)\n"); 116 else 117 cupsFilePuts(fp, "(allow default)\n"); 118 if (LogLevel >= CUPSD_LOG_DEBUG) 119 cupsFilePuts(fp, "(debug deny)\n"); 120 cupsFilePuts(fp, "(import \"system.sb\")\n"); 121 cupsFilePuts(fp, "(system-network)\n"); 122 cupsFilePuts(fp, "(allow mach-per-user-lookup)\n"); 123 cupsFilePuts(fp, "(allow ipc-posix-sem)\n"); 124 cupsFilePuts(fp, "(allow ipc-posix-shm)\n"); 125 cupsFilePuts(fp, "(allow ipc-sysv-shm)\n"); 126 cupsFilePuts(fp, "(allow mach-lookup)\n"); 127 if (!RunUser) 128 cupsFilePrintf(fp, 129 "(deny file-write* file-read-data file-read-metadata\n" 130 " (regex" 131 " #\"^/Users$\"" 132 " #\"^/Users/\"" 133 ")%s)\n", nodebug); 134 cupsFilePrintf(fp, 135 "(deny file-write*\n" 136 " (regex" 137 " #\"^%s$\"" /* ServerRoot */ 138 " #\"^%s/\"" /* ServerRoot/... */ 139 " #\"^/private/etc$\"" 140 " #\"^/private/etc/\"" 141 " #\"^/usr/local/etc$\"" 142 " #\"^/usr/local/etc/\"" 143 " #\"^/Library$\"" 144 " #\"^/Library/\"" 145 " #\"^/System$\"" 146 " #\"^/System/\"" 147 ")%s)\n", 148 root, root, nodebug); 149 /* Specifically allow applications to stat RequestRoot and some other system folders */ 150 cupsFilePrintf(fp, 151 "(allow file-read-metadata\n" 152 " (regex" 153 " #\"^/$\"" /* / */ 154 " #\"^/usr$\"" /* /usr */ 155 " #\"^/Library$\"" /* /Library */ 156 " #\"^/Library/Printers$\"" /* /Library/Printers */ 157 " #\"^%s$\"" /* RequestRoot */ 158 "))\n", 159 request); 160 /* Read and write TempDir, CacheDir, and other common folders */ 161 cupsFilePuts(fp, 162 "(allow file-write* file-read-data file-read-metadata\n" 163 " (regex" 164 " #\"^/private/var/db/\"" 165 " #\"^/private/var/folders/\"" 166 " #\"^/private/var/lib/\"" 167 " #\"^/private/var/log/\"" 168 " #\"^/private/var/mysql/\"" 169 " #\"^/private/var/run/\"" 170 " #\"^/private/var/spool/\"" 171 " #\"^/Library/Application Support/\"" 172 " #\"^/Library/Caches/\"" 173 " #\"^/Library/Logs/\"" 174 " #\"^/Library/Preferences/\"" 175 " #\"^/Library/WebServer/\"" 176 " #\"^/Users/Shared/\"" 177 "))\n"); 178 cupsFilePrintf(fp, 179 "(deny file-write*\n" 180 " (regex #\"^%s$\")%s)\n", 181 request, nodebug); 182 cupsFilePrintf(fp, 183 "(deny file-write* file-read-data file-read-metadata\n" 184 " (regex #\"^%s/\")%s)\n", 185 request, nodebug); 186 cupsFilePrintf(fp, 187 "(allow file-write* file-read-data file-read-metadata\n" 188 " (regex" 189 " #\"^%s$\"" /* TempDir */ 190 " #\"^%s/\"" /* TempDir/... */ 191 " #\"^%s$\"" /* CacheDir */ 192 " #\"^%s/\"" /* CacheDir/... */ 193 " #\"^%s$\"" /* StateDir */ 194 " #\"^%s/\"" /* StateDir/... */ 195 "))\n", 196 temp, temp, cache, cache, state, state); 197 /* Read common folders */ 198 cupsFilePrintf(fp, 199 "(allow file-read-data file-read-metadata\n" 200 " (regex" 201 " #\"^/AppleInternal$\"" 202 " #\"^/AppleInternal/\"" 203 " #\"^/bin$\"" /* /bin */ 204 " #\"^/bin/\"" /* /bin/... */ 205 " #\"^/private$\"" 206 " #\"^/private/etc$\"" 207 " #\"^/private/etc/\"" 208 " #\"^/private/tmp$\"" 209 " #\"^/private/tmp/\"" 210 " #\"^/private/var$\"" 211 " #\"^/private/var/db$\"" 212 " #\"^/private/var/folders$\"" 213 " #\"^/private/var/lib$\"" 214 " #\"^/private/var/log$\"" 215 " #\"^/private/var/mysql$\"" 216 " #\"^/private/var/run$\"" 217 " #\"^/private/var/spool$\"" 218 " #\"^/private/var/tmp$\"" 219 " #\"^/private/var/tmp/\"" 220 " #\"^/usr/bin$\"" /* /usr/bin */ 221 " #\"^/usr/bin/\"" /* /usr/bin/... */ 222 " #\"^/usr/libexec/cups$\"" /* /usr/libexec/cups */ 223 " #\"^/usr/libexec/cups/\"" /* /usr/libexec/cups/... */ 224 " #\"^/usr/libexec/fax$\"" /* /usr/libexec/fax */ 225 " #\"^/usr/libexec/fax/\"" /* /usr/libexec/fax/... */ 226 " #\"^/usr/sbin$\"" /* /usr/sbin */ 227 " #\"^/usr/sbin/\"" /* /usr/sbin/... */ 228 " #\"^/Library$\"" /* /Library */ 229 " #\"^/Library/\"" /* /Library/... */ 230 " #\"^/System$\"" /* /System */ 231 " #\"^/System/\"" /* /System/... */ 232 " #\"^%s/Library$\"" /* RequestRoot/Library */ 233 " #\"^%s/Library/\"" /* RequestRoot/Library/... */ 234 " #\"^%s$\"" /* ServerBin */ 235 " #\"^%s/\"" /* ServerBin/... */ 236 " #\"^%s$\"" /* ServerRoot */ 237 " #\"^%s/\"" /* ServerRoot/... */ 238 "))\n", 239 request, request, bin, bin, root, root); 240 if (Sandboxing == CUPSD_SANDBOXING_RELAXED) 241 { 242 /* Limited write access to /Library/Printers/... */ 243 cupsFilePuts(fp, 244 "(allow file-write*\n" 245 " (regex" 246 " #\"^/Library/Printers/.*/\"" 247 "))\n"); 248 cupsFilePrintf(fp, 249 "(deny file-write*\n" 250 " (regex" 251 " #\"^/Library/Printers/PPDs$\"" 252 " #\"^/Library/Printers/PPDs/\"" 253 " #\"^/Library/Printers/PPD Plugins$\"" 254 " #\"^/Library/Printers/PPD Plugins/\"" 255 ")%s)\n", nodebug); 256 } 257 /* Allow execution of child processes as long as the programs are not in a user directory */ 258 cupsFilePuts(fp, "(allow process*)\n"); 259 cupsFilePuts(fp, "(deny process-exec (regex #\"^/Users/\"))\n"); 260 if (RunUser && getenv("CUPS_TESTROOT")) 261 { 262 /* Allow source directory access in "make test" environment */ 263 char testroot[1024]; /* Root directory of test files */ 264 265 cupsd_requote(testroot, getenv("CUPS_TESTROOT"), sizeof(testroot)); 266 267 cupsFilePrintf(fp, 268 "(allow file-write* file-read-data file-read-metadata\n" 269 " (regex" 270 " #\"^%s$\"" /* CUPS_TESTROOT */ 271 " #\"^%s/\"" /* CUPS_TESTROOT/... */ 272 "))\n", 273 testroot, testroot); 274 cupsFilePrintf(fp, 275 "(allow process-exec\n" 276 " (regex" 277 " #\"^%s/\"" /* CUPS_TESTROOT/... */ 278 "))\n", 279 testroot); 280 cupsFilePrintf(fp, "(allow sysctl*)\n"); 281 } 282 if (job_id) 283 { 284 /* Allow job filters to read the current job files... */ 285 cupsFilePrintf(fp, 286 "(allow file-read-data file-read-metadata\n" 287 " (regex #\"^%s/([ac]%05d|d%05d-[0-9][0-9][0-9])$\"))\n", 288 request, job_id, job_id); 289 } 290 else 291 { 292 /* Allow email notifications from notifiers... */ 293 cupsFilePuts(fp, 294 "(allow process-exec\n" 295 " (literal \"/usr/sbin/sendmail\")\n" 296 " (with no-sandbox))\n"); 297 } 298 /* Allow access to Bluetooth, USB, and notify_post. */ 299 cupsFilePuts(fp, "(allow iokit*)\n"); 300 cupsFilePuts(fp, "(allow distributed-notification-post)\n"); 301 /* Allow outbound networking to local services */ 302 cupsFilePuts(fp, "(allow network-outbound" 303 "\n (regex #\"^/private/var/run/\" #\"^/private/tmp/\" #\"^/private/var/tmp/\")"); 304 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); 305 lis; 306 lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) 307 { 308 if (httpAddrFamily(&(lis->address)) == AF_LOCAL) 309 { 310 httpAddrString(&(lis->address), domain, sizeof(domain)); 311 cupsFilePrintf(fp, "\n (literal \"%s\")", domain); 312 } 313 } 314 if (allow_networking) 315 { 316 /* Allow TCP and UDP networking off the machine... */ 317 cupsFilePuts(fp, "\n (remote tcp))\n"); 318 cupsFilePuts(fp, "(allow network-bind)\n"); /* for LPD resvport */ 319 cupsFilePuts(fp, "(allow network*\n" 320 " (local udp \"*:*\")\n" 321 " (remote udp \"*:*\"))\n"); 322 323 /* Also allow access to device files... */ 324 cupsFilePuts(fp, "(allow file-write* file-read-data file-read-metadata file-ioctl\n" 325 " (regex #\"^/dev/\"))\n"); 326 } 327 else 328 { 329 /* Only allow SNMP (UDP) and LPD (TCP) off the machine... */ 330 cupsFilePuts(fp, ")\n"); 331 cupsFilePuts(fp, "(allow network-outbound\n" 332 " (remote udp \"*:161\")" 333 " (remote tcp \"*:515\"))\n"); 334 cupsFilePuts(fp, "(allow network-inbound\n" 335 " (local udp \"localhost:*\"))\n"); 336 } 337 cupsFileClose(fp); 338 339 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d,allow_networking=%d) = \"%s\"", job_id, allow_networking, profile); 340 return ((void *)strdup(profile)); 341 342#else 343 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking); 344 345 return (NULL); 346#endif /* HAVE_SANDBOX_H */ 347} 348 349 350/* 351 * 'cupsdDestroyProfile()' - Delete an execution profile. 352 */ 353 354void 355cupsdDestroyProfile(void *profile) /* I - Profile */ 356{ 357 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeleteProfile(profile=\"%s\")", 358 profile ? (char *)profile : "(null)"); 359 360#ifdef HAVE_SANDBOX_H 361 if (profile) 362 { 363 unlink((char *)profile); 364 free(profile); 365 } 366#endif /* HAVE_SANDBOX_H */ 367} 368 369 370/* 371 * 'cupsdEndProcess()' - End a process. 372 */ 373 374int /* O - 0 on success, -1 on failure */ 375cupsdEndProcess(int pid, /* I - Process ID */ 376 int force) /* I - Force child to die */ 377{ 378 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdEndProcess(pid=%d, force=%d)", pid, 379 force); 380 381 if (!pid) 382 return (0); 383 384 if (!RunUser) 385 { 386 /* 387 * When running as root, cupsd puts child processes in their own process 388 * group. Using "-pid" sends a signal to all processes in the group. 389 */ 390 391 pid = -pid; 392 } 393 394 if (force) 395 return (kill(pid, SIGKILL)); 396 else 397 return (kill(pid, SIGTERM)); 398} 399 400 401/* 402 * 'cupsdFinishProcess()' - Finish a process and get its name. 403 */ 404 405const char * /* O - Process name */ 406cupsdFinishProcess(int pid, /* I - Process ID */ 407 char *name, /* I - Name buffer */ 408 size_t namelen, /* I - Size of name buffer */ 409 int *job_id) /* O - Job ID pointer or NULL */ 410{ 411 cupsd_proc_t key, /* Search key */ 412 *proc; /* Matching process */ 413 414 415 key.pid = pid; 416 417 if ((proc = (cupsd_proc_t *)cupsArrayFind(process_array, &key)) != NULL) 418 { 419 if (job_id) 420 *job_id = proc->job_id; 421 422 strlcpy(name, proc->name, namelen); 423 cupsArrayRemove(process_array, proc); 424 free(proc); 425 } 426 else 427 { 428 if (job_id) 429 *job_id = 0; 430 431 strlcpy(name, "unknown", namelen); 432 } 433 434 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFinishProcess(pid=%d, name=%p, namelen=" CUPS_LLFMT ", job_id=%p(%d)) = \"%s\"", pid, name, CUPS_LLCAST namelen, job_id, job_id ? *job_id : 0, name); 435 436 return (name); 437} 438 439 440/* 441 * 'cupsdStartProcess()' - Start a process. 442 */ 443 444int /* O - Process ID or 0 */ 445cupsdStartProcess( 446 const char *command, /* I - Full path to command */ 447 char *argv[], /* I - Command-line arguments */ 448 char *envp[], /* I - Environment */ 449 int infd, /* I - Standard input file descriptor */ 450 int outfd, /* I - Standard output file descriptor */ 451 int errfd, /* I - Standard error file descriptor */ 452 int backfd, /* I - Backchannel file descriptor */ 453 int sidefd, /* I - Sidechannel file descriptor */ 454 int root, /* I - Run as root? */ 455 void *profile, /* I - Security profile to use */ 456 cupsd_job_t *job, /* I - Job associated with process */ 457 int *pid) /* O - Process ID */ 458{ 459 int i; /* Looping var */ 460 const char *exec_path = command; /* Command to be exec'd */ 461 char *real_argv[110], /* Real command-line arguments */ 462 cups_exec[1024]; /* Path to "cups-exec" program */ 463 uid_t user; /* Command UID */ 464 cupsd_proc_t *proc; /* New process record */ 465#ifdef HAVE_POSIX_SPAWN 466 posix_spawn_file_actions_t actions; /* Spawn file actions */ 467 posix_spawnattr_t attrs; /* Spawn attributes */ 468 char user_str[16], /* User string */ 469 group_str[16], /* Group string */ 470 nice_str[16]; /* FilterNice string */ 471#elif defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) 472 struct sigaction action; /* POSIX signal handler */ 473#endif /* HAVE_POSIX_SPAWN */ 474#if defined(__APPLE__) 475 char processPath[1024], /* CFProcessPath environment variable */ 476 linkpath[1024]; /* Link path for symlinks... */ 477 int linkbytes; /* Bytes for link path */ 478#endif /* __APPLE__ */ 479 480 481 *pid = 0; 482 483 /* 484 * Figure out the UID for the child process... 485 */ 486 487 if (RunUser) 488 user = RunUser; 489 else if (root) 490 user = 0; 491 else 492 user = User; 493 494 /* 495 * Check the permissions of the command we are running... 496 */ 497 498 if (_cupsFileCheck(command, _CUPS_FILE_CHECK_PROGRAM, !RunUser, 499 cupsdLogFCMessage, job ? job->printer : NULL)) 500 return (0); 501 502#if defined(__APPLE__) 503 if (envp) 504 { 505 /* 506 * Add special voodoo magic for OS X - this allows OS X programs to access 507 * their bundle resources properly... 508 */ 509 510 if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0) 511 { 512 /* 513 * Yes, this is a symlink to the actual program, nul-terminate and 514 * use it... 515 */ 516 517 linkpath[linkbytes] = '\0'; 518 519 if (linkpath[0] == '/') 520 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", 521 linkpath); 522 else 523 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s", 524 dirname((char *)command), linkpath); 525 } 526 else 527 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", command); 528 529 envp[0] = processPath; /* Replace <CFProcessPath> string */ 530 } 531#endif /* __APPLE__ */ 532 533 /* 534 * Use helper program when we have a sandbox profile... 535 */ 536 537#ifndef HAVE_POSIX_SPAWN 538 if (profile) 539#endif /* !HAVE_POSIX_SPAWN */ 540 { 541 snprintf(cups_exec, sizeof(cups_exec), "%s/daemon/cups-exec", ServerBin); 542 snprintf(user_str, sizeof(user_str), "%d", user); 543 snprintf(group_str, sizeof(group_str), "%d", Group); 544 snprintf(nice_str, sizeof(nice_str), "%d", FilterNice); 545 546 real_argv[0] = cups_exec; 547 real_argv[1] = (char *)"-g"; 548 real_argv[2] = group_str; 549 real_argv[3] = (char *)"-n"; 550 real_argv[4] = nice_str; 551 real_argv[5] = (char *)"-u"; 552 real_argv[6] = user_str; 553 real_argv[7] = profile ? profile : "none"; 554 real_argv[8] = (char *)command; 555 556 for (i = 0; 557 i < (int)(sizeof(real_argv) / sizeof(real_argv[0]) - 10) && argv[i]; 558 i ++) 559 real_argv[i + 9] = argv[i]; 560 561 real_argv[i + 9] = NULL; 562 563 argv = real_argv; 564 exec_path = cups_exec; 565 } 566 567 if (LogLevel == CUPSD_LOG_DEBUG2) 568 { 569 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Preparing to start \"%s\", arguments:", command); 570 571 for (i = 0; argv[i]; i ++) 572 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: argv[%d] = \"%s\"", i, argv[i]); 573 } 574 575#ifdef HAVE_POSIX_SPAWN 576 /* 577 * Setup attributes and file actions for the spawn... 578 */ 579 580 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting spawn attributes."); 581 posix_spawnattr_init(&attrs); 582 posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF); 583 584 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting file actions."); 585 posix_spawn_file_actions_init(&actions); 586 if (infd != 0) 587 { 588 if (infd < 0) 589 posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_WRONLY, 0); 590 else 591 posix_spawn_file_actions_adddup2(&actions, infd, 0); 592 } 593 594 if (outfd != 1) 595 { 596 if (outfd < 0) 597 posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0); 598 else 599 posix_spawn_file_actions_adddup2(&actions, outfd, 1); 600 } 601 602 if (errfd != 2) 603 { 604 if (errfd < 0) 605 posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0); 606 else 607 posix_spawn_file_actions_adddup2(&actions, errfd, 2); 608 } 609 610 if (backfd != 3 && backfd >= 0) 611 posix_spawn_file_actions_adddup2(&actions, backfd, 3); 612 613 if (sidefd != 4 && sidefd >= 0) 614 posix_spawn_file_actions_adddup2(&actions, sidefd, 4); 615 616 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Calling posix_spawn."); 617 618 if (posix_spawn(pid, exec_path, &actions, &attrs, argv, envp ? envp : environ)) 619 { 620 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno)); 621 622 *pid = 0; 623 } 624 else 625 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: pid=%d", (int)*pid); 626 627 posix_spawn_file_actions_destroy(&actions); 628 posix_spawnattr_destroy(&attrs); 629 630#else 631 /* 632 * Block signals before forking... 633 */ 634 635 cupsdHoldSignals(); 636 637 if ((*pid = fork()) == 0) 638 { 639 /* 640 * Child process goes here; update stderr as needed... 641 */ 642 643 if (errfd != 2) 644 { 645 if (errfd < 0) 646 errfd = open("/dev/null", O_WRONLY); 647 648 if (errfd != 2) 649 { 650 dup2(errfd, 2); 651 close(errfd); 652 } 653 } 654 655 /* 656 * Put this process in its own process group so that we can kill any child 657 * processes it creates. 658 */ 659 660# ifdef HAVE_SETPGID 661 if (!RunUser && setpgid(0, 0)) 662 exit(errno + 100); 663# else 664 if (!RunUser && setpgrp()) 665 exit(errno + 100); 666# endif /* HAVE_SETPGID */ 667 668 /* 669 * Update the remaining file descriptors as needed... 670 */ 671 672 if (infd != 0) 673 { 674 if (infd < 0) 675 infd = open("/dev/null", O_RDONLY); 676 677 if (infd != 0) 678 { 679 dup2(infd, 0); 680 close(infd); 681 } 682 } 683 684 if (outfd != 1) 685 { 686 if (outfd < 0) 687 outfd = open("/dev/null", O_WRONLY); 688 689 if (outfd != 1) 690 { 691 dup2(outfd, 1); 692 close(outfd); 693 } 694 } 695 696 if (backfd != 3 && backfd >= 0) 697 { 698 dup2(backfd, 3); 699 close(backfd); 700 fcntl(3, F_SETFL, O_NDELAY); 701 } 702 703 if (sidefd != 4 && sidefd >= 0) 704 { 705 dup2(sidefd, 4); 706 close(sidefd); 707 fcntl(4, F_SETFL, O_NDELAY); 708 } 709 710 /* 711 * Change the priority of the process based on the FilterNice setting. 712 * (this is not done for root processes...) 713 */ 714 715 if (!root) 716 nice(FilterNice); 717 718 /* 719 * Reset group membership to just the main one we belong to. 720 */ 721 722 if (!RunUser && setgid(Group)) 723 exit(errno + 100); 724 725 if (!RunUser && setgroups(1, &Group)) 726 exit(errno + 100); 727 728 /* 729 * Change user to something "safe"... 730 */ 731 732 if (!RunUser && user && setuid(user)) 733 exit(errno + 100); 734 735 /* 736 * Change umask to restrict permissions on created files... 737 */ 738 739 umask(077); 740 741 /* 742 * Unblock signals before doing the exec... 743 */ 744 745# ifdef HAVE_SIGSET 746 sigset(SIGTERM, SIG_DFL); 747 sigset(SIGCHLD, SIG_DFL); 748 sigset(SIGPIPE, SIG_DFL); 749# elif defined(HAVE_SIGACTION) 750 memset(&action, 0, sizeof(action)); 751 752 sigemptyset(&action.sa_mask); 753 action.sa_handler = SIG_DFL; 754 755 sigaction(SIGTERM, &action, NULL); 756 sigaction(SIGCHLD, &action, NULL); 757 sigaction(SIGPIPE, &action, NULL); 758# else 759 signal(SIGTERM, SIG_DFL); 760 signal(SIGCHLD, SIG_DFL); 761 signal(SIGPIPE, SIG_DFL); 762# endif /* HAVE_SIGSET */ 763 764 cupsdReleaseSignals(); 765 766 /* 767 * Execute the command; if for some reason this doesn't work, log an error 768 * exit with a non-zero value... 769 */ 770 771 if (envp) 772 execve(exec_path, argv, envp); 773 else 774 execv(exec_path, argv); 775 776 exit(errno + 100); 777 } 778 else if (*pid < 0) 779 { 780 /* 781 * Error - couldn't fork a new process! 782 */ 783 784 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, 785 strerror(errno)); 786 787 *pid = 0; 788 } 789 790 cupsdReleaseSignals(); 791#endif /* HAVE_POSIX_SPAWN */ 792 793 if (*pid) 794 { 795 if (!process_array) 796 process_array = cupsArrayNew((cups_array_func_t)compare_procs, NULL); 797 798 if (process_array) 799 { 800 if ((proc = calloc(1, sizeof(cupsd_proc_t) + strlen(command))) != NULL) 801 { 802 proc->pid = *pid; 803 proc->job_id = job ? job->id : 0; 804 _cups_strcpy(proc->name, command); 805 806 cupsArrayAdd(process_array, proc); 807 } 808 } 809 } 810 811 cupsdLogMessage(CUPSD_LOG_DEBUG2, 812 "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, " 813 "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, " 814 "profile=%p, job=%p(%d), pid=%p) = %d", 815 command, argv, envp, infd, outfd, errfd, backfd, sidefd, 816 root, profile, job, job ? job->id : 0, pid, *pid); 817 818 return (*pid); 819} 820 821 822/* 823 * 'compare_procs()' - Compare two processes. 824 */ 825 826static int /* O - Result of comparison */ 827compare_procs(cupsd_proc_t *a, /* I - First process */ 828 cupsd_proc_t *b) /* I - Second process */ 829{ 830 return (a->pid - b->pid); 831} 832 833 834#ifdef HAVE_SANDBOX_H 835/* 836 * 'cupsd_requote()' - Make a regular-expression version of a string. 837 */ 838 839static char * /* O - Quoted string */ 840cupsd_requote(char *dst, /* I - Destination buffer */ 841 const char *src, /* I - Source string */ 842 size_t dstsize) /* I - Size of destination buffer */ 843{ 844 int ch; /* Current character */ 845 char *dstptr, /* Current position in buffer */ 846 *dstend; /* End of destination buffer */ 847 848 849 dstptr = dst; 850 dstend = dst + dstsize - 2; 851 852 while (*src && dstptr < dstend) 853 { 854 ch = *src++; 855 856 if (ch == '/' && !*src) 857 break; /* Don't add trailing slash */ 858 859 if (strchr(".?*()[]^$\\", ch)) 860 *dstptr++ = '\\'; 861 862 *dstptr++ = (char)ch; 863 } 864 865 *dstptr = '\0'; 866 867 return (dst); 868} 869#endif /* HAVE_SANDBOX_H */ 870 871 872/* 873 * End of "$Id: process.c 12104 2014-08-20 15:23:40Z msweet $". 874 */ 875