/* * Process management routines for the CUPS scheduler. * * Copyright © 2021-2022 by OpenPrinting. * Copyright © 2007-2017 by Apple Inc. * Copyright © 1997-2007 by Easy Software Products, all rights reserved. * * Licensed under Apache License v2.0. See the file "LICENSE" for more * information. */ /* * Include necessary headers... */ #include "cupsd.h" #include #ifdef __APPLE__ # include #endif /* __APPLE__ */ #ifdef HAVE_POSIX_SPAWN # include extern char **environ; /* Don't use posix_spawn on systems with bugs in their implementations... */ # if defined(OpenBSD) && OpenBSD < 201505 # define USE_POSIX_SPAWN 0 # elif defined(__UCLIBC__) && __UCLIBC_MAJOR__ == 1 && __UCLIBC_MINOR__ == 0 && __UCLIBC_SUBLEVEL__ < 27 # define USE_POSIX_SPAWN 0 # elif defined(__UCLIBC__) && __UCLIBC_MAJOR__ < 1 # define USE_POSIX_SPAWN 0 # else /* All other platforms */ # define USE_POSIX_SPAWN 1 # endif /* ... */ #else # define USE_POSIX_SPAWN 0 #endif /* HAVE_POSIX_SPAWN */ /* * Process structure... */ typedef struct { int pid, /* Process ID */ job_id; /* Job associated with process */ char name[1]; /* Name of process */ } cupsd_proc_t; /* * Local globals... */ static cups_array_t *process_array = NULL; /* * Local functions... */ static int compare_procs(cupsd_proc_t *a, cupsd_proc_t *b); #ifdef HAVE_SANDBOX_H static char *cupsd_requote(char *dst, const char *src, size_t dstsize); #endif /* HAVE_SANDBOX_H */ /* * 'cupsdCreateProfile()' - Create an execution profile for a subprocess. */ void * /* O - Profile or NULL on error */ cupsdCreateProfile(int job_id, /* I - Job ID or 0 for none */ int allow_networking)/* I - Allow networking off machine? */ { #ifdef HAVE_SANDBOX_H cups_file_t *fp; /* File pointer */ char profile[1024], /* File containing the profile */ bin[1024], /* Quoted ServerBin */ cache[1024], /* Quoted CacheDir */ domain[1024], /* Domain socket, if any */ request[1024], /* Quoted RequestRoot */ root[1024], /* Quoted ServerRoot */ state[1024], /* Quoted StateDir */ temp[1024]; /* Quoted TempDir */ const char *nodebug; /* " (with no-log)" for no debug */ cupsd_listener_t *lis; /* Current listening socket */ if (!UseSandboxing || Sandboxing == CUPSD_SANDBOXING_OFF) { /* * Only use sandbox profiles as root... */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking); return (NULL); } if ((fp = cupsTempFile2(profile, sizeof(profile))) == NULL) { /* * This should never happen, and is fatal when sandboxing is enabled. */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking); cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to create security profile: %s", strerror(errno)); kill(getpid(), SIGTERM); return (NULL); } fchown(cupsFileNumber(fp), RunUser, Group); fchmod(cupsFileNumber(fp), 0640); cupsd_requote(bin, ServerBin, sizeof(bin)); cupsd_requote(cache, CacheDir, sizeof(cache)); cupsd_requote(request, RequestRoot, sizeof(request)); cupsd_requote(root, ServerRoot, sizeof(root)); cupsd_requote(state, StateDir, sizeof(state)); cupsd_requote(temp, TempDir, sizeof(temp)); nodebug = LogLevel < CUPSD_LOG_DEBUG ? " (with no-log)" : ""; cupsFilePuts(fp, "(version 1)\n"); if (Sandboxing == CUPSD_SANDBOXING_STRICT) cupsFilePuts(fp, "(deny default)\n"); else cupsFilePuts(fp, "(allow default)\n"); if (LogLevel >= CUPSD_LOG_DEBUG) cupsFilePuts(fp, "(debug deny)\n"); cupsFilePuts(fp, "(import \"system.sb\")\n"); cupsFilePuts(fp, "(import \"com.apple.corefoundation.sb\")\n"); cupsFilePuts(fp, "(system-network)\n"); cupsFilePuts(fp, "(allow mach-per-user-lookup)\n"); cupsFilePuts(fp, "(allow ipc-posix-sem)\n"); cupsFilePuts(fp, "(allow ipc-posix-shm)\n"); cupsFilePuts(fp, "(allow ipc-sysv-shm)\n"); cupsFilePuts(fp, "(allow mach-lookup)\n"); if (!RunUser) cupsFilePrintf(fp, "(deny file-write* file-read-data file-read-metadata\n" " (regex" " #\"^/Users$\"" " #\"^/Users/\"" ")%s)\n", nodebug); cupsFilePrintf(fp, "(deny file-write*\n" " (regex" " #\"^%s$\"" /* ServerRoot */ " #\"^%s/\"" /* ServerRoot/... */ " #\"^/private/etc$\"" " #\"^/private/etc/\"" " #\"^/usr/local/etc$\"" " #\"^/usr/local/etc/\"" " #\"^/Library$\"" " #\"^/Library/\"" " #\"^/System$\"" " #\"^/System/\"" ")%s)\n", root, root, nodebug); /* Specifically allow applications to stat RequestRoot and some other system folders */ cupsFilePrintf(fp, "(allow file-read-metadata\n" " (regex" " #\"^/$\"" /* / */ " #\"^/usr$\"" /* /usr */ " #\"^/Library$\"" /* /Library */ " #\"^/Library/Printers$\"" /* /Library/Printers */ " #\"^%s$\"" /* RequestRoot */ "))\n", request); /* Read and write TempDir, CacheDir, and other common folders */ cupsFilePuts(fp, "(allow file-write* file-read-data file-read-metadata\n" " (regex" " #\"^/private/var/db/\"" " #\"^/private/var/folders/\"" " #\"^/private/var/lib/\"" " #\"^/private/var/log/\"" " #\"^/private/var/mysql/\"" " #\"^/private/var/run/\"" " #\"^/private/var/spool/\"" " #\"^/Library/Application Support/\"" " #\"^/Library/Caches/\"" " #\"^/Library/Logs/\"" " #\"^/Library/Preferences/\"" " #\"^/Library/WebServer/\"" " #\"^/Users/Shared/\"" "))\n"); cupsFilePrintf(fp, "(deny file-write*\n" " (regex #\"^%s$\")%s)\n", request, nodebug); cupsFilePrintf(fp, "(deny file-write* file-read-data file-read-metadata\n" " (regex #\"^%s/\")%s)\n", request, nodebug); cupsFilePrintf(fp, "(allow file-write* file-read-data file-read-metadata\n" " (regex" " #\"^%s$\"" /* TempDir */ " #\"^%s/\"" /* TempDir/... */ " #\"^%s$\"" /* CacheDir */ " #\"^%s/\"" /* CacheDir/... */ "))\n", temp, temp, cache, cache); /* Read common folders */ cupsFilePrintf(fp, "(allow file-read-data file-read-metadata\n" " (regex" " #\"^/AppleInternal$\"" " #\"^/AppleInternal/\"" " #\"^/bin$\"" /* /bin */ " #\"^/bin/\"" /* /bin/... */ " #\"^/private$\"" " #\"^/private/etc$\"" " #\"^/private/etc/\"" " #\"^/private/tmp$\"" " #\"^/private/tmp/\"" " #\"^/private/var$\"" " #\"^/private/var/db$\"" " #\"^/private/var/folders$\"" " #\"^/private/var/lib$\"" " #\"^/private/var/log$\"" " #\"^/private/var/mysql$\"" " #\"^/private/var/run$\"" " #\"^/private/var/spool$\"" " #\"^/private/var/tmp$\"" " #\"^/private/var/tmp/\"" " #\"^/usr/bin$\"" /* /usr/bin */ " #\"^/usr/bin/\"" /* /usr/bin/... */ " #\"^/usr/libexec/cups$\"" /* /usr/libexec/cups */ " #\"^/usr/libexec/cups/\"" /* /usr/libexec/cups/... */ " #\"^/usr/libexec/fax$\"" /* /usr/libexec/fax */ " #\"^/usr/libexec/fax/\"" /* /usr/libexec/fax/... */ " #\"^/usr/sbin$\"" /* /usr/sbin */ " #\"^/usr/sbin/\"" /* /usr/sbin/... */ " #\"^/Library$\"" /* /Library */ " #\"^/Library/\"" /* /Library/... */ " #\"^/System$\"" /* /System */ " #\"^/System/\"" /* /System/... */ " #\"^%s/Library$\"" /* RequestRoot/Library */ " #\"^%s/Library/\"" /* RequestRoot/Library/... */ " #\"^%s$\"" /* ServerBin */ " #\"^%s/\"" /* ServerBin/... */ " #\"^%s$\"" /* ServerRoot */ " #\"^%s/\"" /* ServerRoot/... */ " #\"^%s$\"" /* StateDir */ " #\"^%s/\"" /* StateDir/... */ "))\n", request, request, bin, bin, root, root, state, state); if (Sandboxing == CUPSD_SANDBOXING_RELAXED) { /* Limited write access to /Library/Printers/... */ cupsFilePuts(fp, "(allow file-write*\n" " (regex" " #\"^/Library/Printers/.*/\"" "))\n"); cupsFilePrintf(fp, "(deny file-write*\n" " (regex" " #\"^/Library/Printers/PPDs$\"" " #\"^/Library/Printers/PPDs/\"" " #\"^/Library/Printers/PPD Plugins$\"" " #\"^/Library/Printers/PPD Plugins/\"" ")%s)\n", nodebug); } /* Allow execution of child processes as long as the programs are not in a user directory */ cupsFilePuts(fp, "(allow process*)\n"); cupsFilePuts(fp, "(deny process-exec (regex #\"^/Users/\"))\n"); if (RunUser && getenv("CUPS_TESTROOT")) { /* Allow source directory access in "make test" environment */ char testroot[1024]; /* Root directory of test files */ cupsd_requote(testroot, getenv("CUPS_TESTROOT"), sizeof(testroot)); cupsFilePrintf(fp, "(allow file-write* file-read-data file-read-metadata\n" " (regex" " #\"^%s$\"" /* CUPS_TESTROOT */ " #\"^%s/\"" /* CUPS_TESTROOT/... */ "))\n", testroot, testroot); cupsFilePrintf(fp, "(allow process-exec\n" " (regex" " #\"^%s/\"" /* CUPS_TESTROOT/... */ "))\n", testroot); cupsFilePrintf(fp, "(allow sysctl*)\n"); } if (job_id) { /* Allow job filters to read the current job files... */ cupsFilePrintf(fp, "(allow file-read-data file-read-metadata\n" " (regex #\"^%s/([ac]%05d|d%05d-[0-9][0-9][0-9])$\"))\n", request, job_id, job_id); } else { /* Allow email notifications from notifiers... */ cupsFilePuts(fp, "(allow process-exec\n" " (literal \"/usr/sbin/sendmail\")\n" " (with no-sandbox))\n"); } /* Allow access to Bluetooth, USB, and notify_post. */ cupsFilePuts(fp, "(allow iokit*)\n"); cupsFilePuts(fp, "(allow distributed-notification-post)\n"); /* Allow outbound networking to local services */ cupsFilePuts(fp, "(allow network-outbound" "\n (regex #\"^/private/var/run/\" #\"^/private/tmp/\" #\"^/private/var/tmp/\")"); for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); lis; lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) { if (httpAddrFamily(&(lis->address)) == AF_LOCAL) { httpAddrString(&(lis->address), domain, sizeof(domain)); cupsFilePrintf(fp, "\n (literal \"%s\")", domain); } } if (allow_networking) { /* Allow TCP and UDP networking off the machine... */ cupsFilePuts(fp, "\n (remote tcp))\n"); cupsFilePuts(fp, "(allow network-bind)\n"); /* for LPD resvport */ cupsFilePuts(fp, "(allow network*\n" " (local udp \"*:*\")\n" " (remote udp \"*:*\"))\n"); /* Also allow access to device files... */ cupsFilePuts(fp, "(allow file-write* file-read-data file-read-metadata file-ioctl\n" " (regex #\"^/dev/\"))\n"); /* And allow kernel extensions to be loaded, e.g., SMB */ cupsFilePuts(fp, "(allow system-kext-load)\n"); } else { /* Only allow SNMP (UDP) and LPD (TCP) off the machine... */ cupsFilePuts(fp, ")\n"); cupsFilePuts(fp, "(allow network-outbound\n" " (remote udp \"*:161\")\n" " (remote tcp \"*:515\"))\n"); cupsFilePuts(fp, "(allow network-inbound\n" " (local udp \"localhost:*\"))\n"); } cupsFileClose(fp); cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d,allow_networking=%d) = \"%s\"", job_id, allow_networking, profile); return ((void *)strdup(profile)); #else cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking); return (NULL); #endif /* HAVE_SANDBOX_H */ } /* * 'cupsdDestroyProfile()' - Delete an execution profile. */ void cupsdDestroyProfile(void *profile) /* I - Profile */ { cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeleteProfile(profile=\"%s\")", profile ? (char *)profile : "(null)"); #ifdef HAVE_SANDBOX_H if (profile) { unlink((char *)profile); free(profile); } #endif /* HAVE_SANDBOX_H */ } /* * 'cupsdEndProcess()' - End a process. */ int /* O - 0 on success, -1 on failure */ cupsdEndProcess(int pid, /* I - Process ID */ int force) /* I - Force child to die */ { cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdEndProcess(pid=%d, force=%d)", pid, force); if (!pid) return (0); if (!RunUser) { /* * When running as root, cupsd puts child processes in their own process * group. Using "-pid" sends a signal to all processes in the group. */ pid = -pid; } if (force) return (kill(pid, SIGKILL)); else return (kill(pid, SIGTERM)); } /* * 'cupsdFinishProcess()' - Finish a process and get its name. */ const char * /* O - Process name */ cupsdFinishProcess(int pid, /* I - Process ID */ char *name, /* I - Name buffer */ size_t namelen, /* I - Size of name buffer */ int *job_id) /* O - Job ID pointer or NULL */ { cupsd_proc_t key, /* Search key */ *proc; /* Matching process */ key.pid = pid; if ((proc = (cupsd_proc_t *)cupsArrayFind(process_array, &key)) != NULL) { if (job_id) *job_id = proc->job_id; strlcpy(name, proc->name, namelen); cupsArrayRemove(process_array, proc); free(proc); } else { if (job_id) *job_id = 0; strlcpy(name, "unknown", namelen); } 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); return (name); } /* * 'cupsdStartProcess()' - Start a process. */ int /* O - Process ID or 0 */ cupsdStartProcess( const char *command, /* I - Full path to command */ char *argv[], /* I - Command-line arguments */ char *envp[], /* I - Environment */ int infd, /* I - Standard input file descriptor */ int outfd, /* I - Standard output file descriptor */ int errfd, /* I - Standard error file descriptor */ int backfd, /* I - Backchannel file descriptor */ int sidefd, /* I - Sidechannel file descriptor */ int root, /* I - Run as root? */ void *profile, /* I - Security profile to use */ cupsd_job_t *job, /* I - Job associated with process */ int *pid) /* O - Process ID */ { int i; /* Looping var */ const char *exec_path = command; /* Command to be exec'd */ char *real_argv[110], /* Real command-line arguments */ cups_exec[1024], /* Path to "cups-exec" program */ user_str[16], /* User string */ group_str[16], /* Group string */ nice_str[16]; /* FilterNice string */ uid_t user; /* Command UID */ cupsd_proc_t *proc; /* New process record */ #if USE_POSIX_SPAWN posix_spawn_file_actions_t actions; /* Spawn file actions */ posix_spawnattr_t attrs; /* Spawn attributes */ sigset_t defsignals; /* Default signals */ #elif defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* POSIX signal handler */ #endif /* USE_POSIX_SPAWN */ #if defined(__APPLE__) char processPath[1024], /* CFProcessPath environment variable */ linkpath[1024]; /* Link path for symlinks... */ int linkbytes; /* Bytes for link path */ #endif /* __APPLE__ */ *pid = 0; /* * Figure out the UID for the child process... */ if (RunUser) user = RunUser; else if (root) user = 0; else user = User; /* * Check the permissions of the command we are running... */ if (_cupsFileCheck(command, _CUPS_FILE_CHECK_PROGRAM, !RunUser, cupsdLogFCMessage, job ? job->printer : NULL)) return (0); #if defined(__APPLE__) if (envp) { /* * Add special voodoo magic for macOS - this allows macOS programs to access * their bundle resources properly... */ if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0) { /* * Yes, this is a symlink to the actual program, nul-terminate and * use it... */ linkpath[linkbytes] = '\0'; if (linkpath[0] == '/') snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", linkpath); else snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s", dirname((char *)command), linkpath); } else snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", command); envp[0] = processPath; /* Replace string */ } #endif /* __APPLE__ */ /* * Use helper program when we have a sandbox profile... */ #if !USE_POSIX_SPAWN if (profile) #endif /* !USE_POSIX_SPAWN */ { snprintf(cups_exec, sizeof(cups_exec), "%s/daemon/cups-exec", ServerBin); snprintf(user_str, sizeof(user_str), "%d", user); snprintf(group_str, sizeof(group_str), "%d", Group); snprintf(nice_str, sizeof(nice_str), "%d", FilterNice); real_argv[0] = cups_exec; real_argv[1] = (char *)"-g"; real_argv[2] = group_str; real_argv[3] = (char *)"-n"; real_argv[4] = nice_str; real_argv[5] = (char *)"-u"; real_argv[6] = user_str; real_argv[7] = profile ? profile : "none"; real_argv[8] = (char *)command; for (i = 0; i < (int)(sizeof(real_argv) / sizeof(real_argv[0]) - 10) && argv[i]; i ++) real_argv[i + 9] = argv[i]; real_argv[i + 9] = NULL; argv = real_argv; exec_path = cups_exec; } if (LogLevel == CUPSD_LOG_DEBUG2) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Preparing to start \"%s\", arguments:", command); for (i = 0; argv[i]; i ++) cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: argv[%d] = \"%s\"", i, argv[i]); } #if USE_POSIX_SPAWN /* * Setup attributes and file actions for the spawn... */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting spawn attributes."); sigemptyset(&defsignals); sigaddset(&defsignals, SIGTERM); sigaddset(&defsignals, SIGCHLD); sigaddset(&defsignals, SIGPIPE); posix_spawnattr_init(&attrs); posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF); posix_spawnattr_setpgroup(&attrs, 0); posix_spawnattr_setsigdefault(&attrs, &defsignals); cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting file actions."); posix_spawn_file_actions_init(&actions); if (infd != 0) { if (infd < 0) posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDONLY, 0); else posix_spawn_file_actions_adddup2(&actions, infd, 0); } if (outfd != 1) { if (outfd < 0) posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0); else posix_spawn_file_actions_adddup2(&actions, outfd, 1); } if (errfd != 2) { if (errfd < 0) posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0); else posix_spawn_file_actions_adddup2(&actions, errfd, 2); } if (backfd != 3 && backfd >= 0) posix_spawn_file_actions_adddup2(&actions, backfd, 3); if (sidefd != 4 && sidefd >= 0) posix_spawn_file_actions_adddup2(&actions, sidefd, 4); cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Calling posix_spawn."); if (posix_spawn(pid, exec_path, &actions, &attrs, argv, envp ? envp : environ)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno)); *pid = 0; } else cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: pid=%d", (int)*pid); posix_spawn_file_actions_destroy(&actions); posix_spawnattr_destroy(&attrs); #else /* * Block signals before forking... */ cupsdHoldSignals(); if ((*pid = fork()) == 0) { /* * Child process goes here; update stderr as needed... */ if (errfd != 2) { if (errfd < 0) errfd = open("/dev/null", O_WRONLY); if (errfd != 2) { dup2(errfd, 2); close(errfd); } } /* * Put this process in its own process group so that we can kill any child * processes it creates. */ # ifdef HAVE_SETPGID if (!RunUser && setpgid(0, 0)) exit(errno + 100); # else if (!RunUser && setpgrp()) exit(errno + 100); # endif /* HAVE_SETPGID */ /* * Update the remaining file descriptors as needed... */ if (infd != 0) { if (infd < 0) infd = open("/dev/null", O_RDONLY); if (infd != 0) { dup2(infd, 0); close(infd); } } if (outfd != 1) { if (outfd < 0) outfd = open("/dev/null", O_WRONLY); if (outfd != 1) { dup2(outfd, 1); close(outfd); } } if (backfd != 3 && backfd >= 0) { dup2(backfd, 3); close(backfd); fcntl(3, F_SETFL, O_NDELAY); } if (sidefd != 4 && sidefd >= 0) { dup2(sidefd, 4); close(sidefd); fcntl(4, F_SETFL, O_NDELAY); } /* * Change the priority of the process based on the FilterNice setting. * (this is not done for root processes...) */ if (!root) nice(FilterNice); /* * Reset group membership to just the main one we belong to. */ if (!RunUser && setgid(Group)) exit(errno + 100); # if CUPS_SNAP if (!RunUser && setgroups(0, NULL)) # else if (!RunUser && setgroups(1, &Group)) # endif /* CUPS_SNAP */ exit(errno + 100); /* * Change user to something "safe"... */ if (!RunUser && user && setuid(user)) exit(errno + 100); /* * Change umask to restrict permissions on created files... */ umask(077); /* * Unblock signals before doing the exec... */ # ifdef HAVE_SIGSET sigset(SIGTERM, SIG_DFL); sigset(SIGCHLD, SIG_DFL); sigset(SIGPIPE, SIG_DFL); # elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); action.sa_handler = SIG_DFL; sigaction(SIGTERM, &action, NULL); sigaction(SIGCHLD, &action, NULL); sigaction(SIGPIPE, &action, NULL); # else signal(SIGTERM, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_DFL); # endif /* HAVE_SIGSET */ cupsdReleaseSignals(); /* * Execute the command; if for some reason this doesn't work, log an error * exit with a non-zero value... */ if (envp) execve(exec_path, argv, envp); else execv(exec_path, argv); exit(errno + 100); } else if (*pid < 0) { /* * Error - couldn't fork a new process! */ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno)); *pid = 0; } cupsdReleaseSignals(); #endif /* USE_POSIX_SPAWN */ if (*pid) { if (!process_array) process_array = cupsArrayNew((cups_array_func_t)compare_procs, NULL); if (process_array) { if ((proc = calloc(1, sizeof(cupsd_proc_t) + strlen(command))) != NULL) { proc->pid = *pid; proc->job_id = job ? job->id : 0; _cups_strcpy(proc->name, command); cupsArrayAdd(process_array, proc); } } } cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, " "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, " "profile=%p, job=%p(%d), pid=%p) = %d", command, argv, envp, infd, outfd, errfd, backfd, sidefd, root, profile, job, job ? job->id : 0, pid, *pid); return (*pid); } /* * 'compare_procs()' - Compare two processes. */ static int /* O - Result of comparison */ compare_procs(cupsd_proc_t *a, /* I - First process */ cupsd_proc_t *b) /* I - Second process */ { return (a->pid - b->pid); } #ifdef HAVE_SANDBOX_H /* * 'cupsd_requote()' - Make a regular-expression version of a string. */ static char * /* O - Quoted string */ cupsd_requote(char *dst, /* I - Destination buffer */ const char *src, /* I - Source string */ size_t dstsize) /* I - Size of destination buffer */ { int ch; /* Current character */ char *dstptr, /* Current position in buffer */ *dstend; /* End of destination buffer */ dstptr = dst; dstend = dst + dstsize - 2; while (*src && dstptr < dstend) { ch = *src++; if (ch == '/' && !*src) break; /* Don't add trailing slash */ if (strchr(".?*()[]^$\\\"", ch)) *dstptr++ = '\\'; *dstptr++ = (char)ch; } *dstptr = '\0'; return (dst); } #endif /* HAVE_SANDBOX_H */