]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/process.c
Import CUPS v2.0.1
[thirdparty/cups.git] / scheduler / process.c
1 /*
2 * "$Id: process.c 12252 2014-11-14 17:14:45Z 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>
27 extern char **environ;
28 #endif /* HAVE_POSIX_SPAWN */
29
30
31 /*
32 * Process structure...
33 */
34
35 typedef 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
47 static cups_array_t *process_array = NULL;
48
49
50 /*
51 * Local functions...
52 */
53
54 static int compare_procs(cupsd_proc_t *a, cupsd_proc_t *b);
55 #ifdef HAVE_SANDBOX_H
56 static 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
64 void * /* O - Profile or NULL on error */
65 cupsdCreateProfile(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
354 void
355 cupsdDestroyProfile(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
374 int /* O - 0 on success, -1 on failure */
375 cupsdEndProcess(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
405 const char * /* O - Process name */
406 cupsdFinishProcess(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
444 int /* O - Process ID or 0 */
445 cupsdStartProcess(
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 user_str[16], /* User string */
464 group_str[16], /* Group string */
465 nice_str[16]; /* FilterNice string */
466 uid_t user; /* Command UID */
467 cupsd_proc_t *proc; /* New process record */
468 #if defined(HAVE_POSIX_SPAWN) && !defined(__OpenBSD__)
469 posix_spawn_file_actions_t actions; /* Spawn file actions */
470 posix_spawnattr_t attrs; /* Spawn attributes */
471 sigset_t defsignals; /* Default signals */
472 #elif defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
473 struct sigaction action; /* POSIX signal handler */
474 #endif /* HAVE_POSIX_SPAWN && !__OpenBSD__ */
475 #if defined(__APPLE__)
476 char processPath[1024], /* CFProcessPath environment variable */
477 linkpath[1024]; /* Link path for symlinks... */
478 int linkbytes; /* Bytes for link path */
479 #endif /* __APPLE__ */
480
481
482 *pid = 0;
483
484 /*
485 * Figure out the UID for the child process...
486 */
487
488 if (RunUser)
489 user = RunUser;
490 else if (root)
491 user = 0;
492 else
493 user = User;
494
495 /*
496 * Check the permissions of the command we are running...
497 */
498
499 if (_cupsFileCheck(command, _CUPS_FILE_CHECK_PROGRAM, !RunUser,
500 cupsdLogFCMessage, job ? job->printer : NULL))
501 return (0);
502
503 #if defined(__APPLE__)
504 if (envp)
505 {
506 /*
507 * Add special voodoo magic for OS X - this allows OS X programs to access
508 * their bundle resources properly...
509 */
510
511 if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0)
512 {
513 /*
514 * Yes, this is a symlink to the actual program, nul-terminate and
515 * use it...
516 */
517
518 linkpath[linkbytes] = '\0';
519
520 if (linkpath[0] == '/')
521 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s",
522 linkpath);
523 else
524 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s",
525 dirname((char *)command), linkpath);
526 }
527 else
528 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", command);
529
530 envp[0] = processPath; /* Replace <CFProcessPath> string */
531 }
532 #endif /* __APPLE__ */
533
534 /*
535 * Use helper program when we have a sandbox profile...
536 */
537
538 #if !defined(HAVE_POSIX_SPAWN) || defined(__OpenBSD__)
539 if (profile)
540 #endif /* !HAVE_POSIX_SPAWN || __OpenBSD__ */
541 {
542 snprintf(cups_exec, sizeof(cups_exec), "%s/daemon/cups-exec", ServerBin);
543 snprintf(user_str, sizeof(user_str), "%d", user);
544 snprintf(group_str, sizeof(group_str), "%d", Group);
545 snprintf(nice_str, sizeof(nice_str), "%d", FilterNice);
546
547 real_argv[0] = cups_exec;
548 real_argv[1] = (char *)"-g";
549 real_argv[2] = group_str;
550 real_argv[3] = (char *)"-n";
551 real_argv[4] = nice_str;
552 real_argv[5] = (char *)"-u";
553 real_argv[6] = user_str;
554 real_argv[7] = profile ? profile : "none";
555 real_argv[8] = (char *)command;
556
557 for (i = 0;
558 i < (int)(sizeof(real_argv) / sizeof(real_argv[0]) - 10) && argv[i];
559 i ++)
560 real_argv[i + 9] = argv[i];
561
562 real_argv[i + 9] = NULL;
563
564 argv = real_argv;
565 exec_path = cups_exec;
566 }
567
568 if (LogLevel == CUPSD_LOG_DEBUG2)
569 {
570 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Preparing to start \"%s\", arguments:", command);
571
572 for (i = 0; argv[i]; i ++)
573 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: argv[%d] = \"%s\"", i, argv[i]);
574 }
575
576 #if defined(HAVE_POSIX_SPAWN) && !defined(__OpenBSD__) /* OpenBSD posix_spawn is busted with SETSIGDEF */
577 /*
578 * Setup attributes and file actions for the spawn...
579 */
580
581 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting spawn attributes.");
582 sigemptyset(&defsignals);
583 sigaddset(&defsignals, SIGTERM);
584 sigaddset(&defsignals, SIGCHLD);
585 sigaddset(&defsignals, SIGPIPE);
586
587 posix_spawnattr_init(&attrs);
588 posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF);
589 posix_spawnattr_setpgroup(&attrs, 0);
590 posix_spawnattr_setsigdefault(&attrs, &defsignals);
591
592 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting file actions.");
593 posix_spawn_file_actions_init(&actions);
594 if (infd != 0)
595 {
596 if (infd < 0)
597 posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_WRONLY, 0);
598 else
599 posix_spawn_file_actions_adddup2(&actions, infd, 0);
600 }
601
602 if (outfd != 1)
603 {
604 if (outfd < 0)
605 posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0);
606 else
607 posix_spawn_file_actions_adddup2(&actions, outfd, 1);
608 }
609
610 if (errfd != 2)
611 {
612 if (errfd < 0)
613 posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0);
614 else
615 posix_spawn_file_actions_adddup2(&actions, errfd, 2);
616 }
617
618 if (backfd != 3 && backfd >= 0)
619 posix_spawn_file_actions_adddup2(&actions, backfd, 3);
620
621 if (sidefd != 4 && sidefd >= 0)
622 posix_spawn_file_actions_adddup2(&actions, sidefd, 4);
623
624 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Calling posix_spawn.");
625
626 if (posix_spawn(pid, exec_path, &actions, &attrs, argv, envp ? envp : environ))
627 {
628 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno));
629
630 *pid = 0;
631 }
632 else
633 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: pid=%d", (int)*pid);
634
635 posix_spawn_file_actions_destroy(&actions);
636 posix_spawnattr_destroy(&attrs);
637
638 #else
639 /*
640 * Block signals before forking...
641 */
642
643 cupsdHoldSignals();
644
645 if ((*pid = fork()) == 0)
646 {
647 /*
648 * Child process goes here; update stderr as needed...
649 */
650
651 if (errfd != 2)
652 {
653 if (errfd < 0)
654 errfd = open("/dev/null", O_WRONLY);
655
656 if (errfd != 2)
657 {
658 dup2(errfd, 2);
659 close(errfd);
660 }
661 }
662
663 /*
664 * Put this process in its own process group so that we can kill any child
665 * processes it creates.
666 */
667
668 # ifdef HAVE_SETPGID
669 if (!RunUser && setpgid(0, 0))
670 exit(errno + 100);
671 # else
672 if (!RunUser && setpgrp())
673 exit(errno + 100);
674 # endif /* HAVE_SETPGID */
675
676 /*
677 * Update the remaining file descriptors as needed...
678 */
679
680 if (infd != 0)
681 {
682 if (infd < 0)
683 infd = open("/dev/null", O_RDONLY);
684
685 if (infd != 0)
686 {
687 dup2(infd, 0);
688 close(infd);
689 }
690 }
691
692 if (outfd != 1)
693 {
694 if (outfd < 0)
695 outfd = open("/dev/null", O_WRONLY);
696
697 if (outfd != 1)
698 {
699 dup2(outfd, 1);
700 close(outfd);
701 }
702 }
703
704 if (backfd != 3 && backfd >= 0)
705 {
706 dup2(backfd, 3);
707 close(backfd);
708 fcntl(3, F_SETFL, O_NDELAY);
709 }
710
711 if (sidefd != 4 && sidefd >= 0)
712 {
713 dup2(sidefd, 4);
714 close(sidefd);
715 fcntl(4, F_SETFL, O_NDELAY);
716 }
717
718 /*
719 * Change the priority of the process based on the FilterNice setting.
720 * (this is not done for root processes...)
721 */
722
723 if (!root)
724 nice(FilterNice);
725
726 /*
727 * Reset group membership to just the main one we belong to.
728 */
729
730 if (!RunUser && setgid(Group))
731 exit(errno + 100);
732
733 if (!RunUser && setgroups(1, &Group))
734 exit(errno + 100);
735
736 /*
737 * Change user to something "safe"...
738 */
739
740 if (!RunUser && user && setuid(user))
741 exit(errno + 100);
742
743 /*
744 * Change umask to restrict permissions on created files...
745 */
746
747 umask(077);
748
749 /*
750 * Unblock signals before doing the exec...
751 */
752
753 # ifdef HAVE_SIGSET
754 sigset(SIGTERM, SIG_DFL);
755 sigset(SIGCHLD, SIG_DFL);
756 sigset(SIGPIPE, SIG_DFL);
757 # elif defined(HAVE_SIGACTION)
758 memset(&action, 0, sizeof(action));
759
760 sigemptyset(&action.sa_mask);
761 action.sa_handler = SIG_DFL;
762
763 sigaction(SIGTERM, &action, NULL);
764 sigaction(SIGCHLD, &action, NULL);
765 sigaction(SIGPIPE, &action, NULL);
766 # else
767 signal(SIGTERM, SIG_DFL);
768 signal(SIGCHLD, SIG_DFL);
769 signal(SIGPIPE, SIG_DFL);
770 # endif /* HAVE_SIGSET */
771
772 cupsdReleaseSignals();
773
774 /*
775 * Execute the command; if for some reason this doesn't work, log an error
776 * exit with a non-zero value...
777 */
778
779 if (envp)
780 execve(exec_path, argv, envp);
781 else
782 execv(exec_path, argv);
783
784 exit(errno + 100);
785 }
786 else if (*pid < 0)
787 {
788 /*
789 * Error - couldn't fork a new process!
790 */
791
792 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command,
793 strerror(errno));
794
795 *pid = 0;
796 }
797
798 cupsdReleaseSignals();
799 #endif /* HAVE_POSIX_SPAWN && !__OpenBSD__ */
800
801 if (*pid)
802 {
803 if (!process_array)
804 process_array = cupsArrayNew((cups_array_func_t)compare_procs, NULL);
805
806 if (process_array)
807 {
808 if ((proc = calloc(1, sizeof(cupsd_proc_t) + strlen(command))) != NULL)
809 {
810 proc->pid = *pid;
811 proc->job_id = job ? job->id : 0;
812 _cups_strcpy(proc->name, command);
813
814 cupsArrayAdd(process_array, proc);
815 }
816 }
817 }
818
819 cupsdLogMessage(CUPSD_LOG_DEBUG2,
820 "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, "
821 "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, "
822 "profile=%p, job=%p(%d), pid=%p) = %d",
823 command, argv, envp, infd, outfd, errfd, backfd, sidefd,
824 root, profile, job, job ? job->id : 0, pid, *pid);
825
826 return (*pid);
827 }
828
829
830 /*
831 * 'compare_procs()' - Compare two processes.
832 */
833
834 static int /* O - Result of comparison */
835 compare_procs(cupsd_proc_t *a, /* I - First process */
836 cupsd_proc_t *b) /* I - Second process */
837 {
838 return (a->pid - b->pid);
839 }
840
841
842 #ifdef HAVE_SANDBOX_H
843 /*
844 * 'cupsd_requote()' - Make a regular-expression version of a string.
845 */
846
847 static char * /* O - Quoted string */
848 cupsd_requote(char *dst, /* I - Destination buffer */
849 const char *src, /* I - Source string */
850 size_t dstsize) /* I - Size of destination buffer */
851 {
852 int ch; /* Current character */
853 char *dstptr, /* Current position in buffer */
854 *dstend; /* End of destination buffer */
855
856
857 dstptr = dst;
858 dstend = dst + dstsize - 2;
859
860 while (*src && dstptr < dstend)
861 {
862 ch = *src++;
863
864 if (ch == '/' && !*src)
865 break; /* Don't add trailing slash */
866
867 if (strchr(".?*()[]^$\\", ch))
868 *dstptr++ = '\\';
869
870 *dstptr++ = (char)ch;
871 }
872
873 *dstptr = '\0';
874
875 return (dst);
876 }
877 #endif /* HAVE_SANDBOX_H */
878
879
880 /*
881 * End of "$Id: process.c 12252 2014-11-14 17:14:45Z msweet $".
882 */