]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/process.c
Allow domain sockets in /private/tmp.
[thirdparty/cups.git] / scheduler / process.c
1 /*
2 * "$Id$"
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 temp[1024]; /* Quoted TempDir */
77 const char *nodebug; /* " (with no-log)" for no debug */
78 cupsd_listener_t *lis; /* Current listening socket */
79
80
81 if (!UseSandboxing || Sandboxing == CUPSD_SANDBOXING_OFF)
82 {
83 /*
84 * Only use sandbox profiles as root...
85 */
86
87 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking);
88
89 return (NULL);
90 }
91
92 if ((fp = cupsTempFile2(profile, sizeof(profile))) == NULL)
93 {
94 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking);
95 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create security profile: %s",
96 strerror(errno));
97 return (NULL);
98 }
99
100 fchown(cupsFileNumber(fp), RunUser, Group);
101 fchmod(cupsFileNumber(fp), 0640);
102
103 cupsd_requote(bin, ServerBin, sizeof(bin));
104 cupsd_requote(cache, CacheDir, sizeof(cache));
105 cupsd_requote(request, RequestRoot, sizeof(request));
106 cupsd_requote(root, ServerRoot, sizeof(root));
107 cupsd_requote(temp, TempDir, sizeof(temp));
108
109 nodebug = LogLevel < CUPSD_LOG_DEBUG ? " (with no-log)" : "";
110
111 cupsFilePuts(fp, "(version 1)\n");
112 if (Sandboxing == CUPSD_SANDBOXING_STRICT)
113 cupsFilePuts(fp, "(deny default)\n");
114 else
115 cupsFilePuts(fp, "(allow default)\n");
116 if (LogLevel >= CUPSD_LOG_DEBUG)
117 cupsFilePuts(fp, "(debug deny)\n");
118 cupsFilePuts(fp, "(import \"system.sb\")\n");
119 cupsFilePuts(fp, "(system-network)\n");
120 cupsFilePuts(fp, "(allow mach-per-user-lookup)\n");
121 cupsFilePuts(fp, "(allow ipc-posix-sem)\n");
122 cupsFilePuts(fp, "(allow ipc-posix-shm)\n");
123 cupsFilePuts(fp, "(allow ipc-sysv-shm)\n");
124 cupsFilePuts(fp, "(allow mach-lookup)\n");
125 cupsFilePrintf(fp,
126 "(deny file-write* file-read-data file-read-metadata\n"
127 " (regex"
128 " #\"^%s$\"" /* RequestRoot */
129 " #\"^%s/\"" /* RequestRoot/... */
130 ")%s)\n",
131 request, request, nodebug);
132 if (!RunUser)
133 cupsFilePrintf(fp,
134 "(deny file-write* file-read-data file-read-metadata\n"
135 " (regex"
136 " #\"^/Users$\""
137 " #\"^/Users/\""
138 ")%s)\n", nodebug);
139 cupsFilePrintf(fp,
140 "(deny file-write*\n"
141 " (regex"
142 " #\"^%s$\"" /* ServerRoot */
143 " #\"^%s/\"" /* ServerRoot/... */
144 " #\"^/private/etc$\""
145 " #\"^/private/etc/\""
146 " #\"^/usr/local/etc$\""
147 " #\"^/usr/local/etc/\""
148 " #\"^/Library$\""
149 " #\"^/Library/\""
150 " #\"^/System$\""
151 " #\"^/System/\""
152 ")%s)\n",
153 root, root, nodebug);
154 /* Specifically allow applications to stat RequestRoot and some other system folders */
155 cupsFilePrintf(fp,
156 "(allow file-read-metadata\n"
157 " (regex"
158 " #\"^/$\"" /* / */
159 " #\"^/usr$\"" /* /usr */
160 " #\"^/Library$\"" /* /Library */
161 " #\"^/Library/Printers$\"" /* /Library/Printers */
162 " #\"^%s$\"" /* RequestRoot */
163 "))\n",
164 request);
165 /* Read and write TempDir, CacheDir, and other common folders */
166 cupsFilePrintf(fp,
167 "(allow file-write* file-read-data file-read-metadata\n"
168 " (regex"
169 " #\"^%s$\"" /* TempDir */
170 " #\"^%s/\"" /* TempDir/... */
171 " #\"^%s$\"" /* CacheDir */
172 " #\"^%s/\"" /* CacheDir/... */
173 " #\"^/private/var/db/\""
174 " #\"^/private/var/folders/\""
175 " #\"^/private/var/run/\""
176 " #\"^/Library/Application Support/\""
177 " #\"^/Library/Caches/\""
178 " #\"^/Library/Preferences/\""
179 " #\"^/Users/Shared/\""
180 "))\n",
181 temp, temp, cache, cache);
182 /* Read common folders */
183 cupsFilePrintf(fp,
184 "(allow file-read-data file-read-metadata\n"
185 " (regex"
186 " #\"^/AppleInternal$\""
187 " #\"^/AppleInternal/\""
188 " #\"^/bin$\"" /* /bin */
189 " #\"^/bin/\"" /* /bin/... */
190 " #\"^/private$\""
191 " #\"^/private/etc$\""
192 " #\"^/private/etc/\""
193 " #\"^/private/tmp$\""
194 " #\"^/private/tmp/\""
195 " #\"^/private/var$\""
196 " #\"^/private/var/db$\""
197 " #\"^/private/var/folders$\""
198 " #\"^/private/var/run$\""
199 " #\"^/private/var/run/\""
200 " #\"^/private/var/spool$\""
201 " #\"^/usr/bin$\"" /* /usr/bin */
202 " #\"^/usr/bin/\"" /* /usr/bin/... */
203 " #\"^/usr/libexec/cups$\"" /* /usr/libexec/cups */
204 " #\"^/usr/libexec/cups/\"" /* /usr/libexec/cups/... */
205 " #\"^/usr/libexec/fax$\"" /* /usr/libexec/fax */
206 " #\"^/usr/libexec/fax/\"" /* /usr/libexec/fax/... */
207 " #\"^/usr/sbin$\"" /* /usr/sbin */
208 " #\"^/usr/sbin/\"" /* /usr/sbin/... */
209 " #\"^/Library/Application Support$\""
210 " #\"^/Library/Application Support/\""
211 " #\"^/Library/Caches$\""
212 " #\"^/Library/Fonts$\""
213 " #\"^/Library/Fonts/\""
214 " #\"^/Library/Frameworks$\""
215 " #\"^/Library/Frameworks/\""
216 " #\"^/Library/Keychains$\""
217 " #\"^/Library/Keychains/\""
218 " #\"^/Library/Printers$\""
219 " #\"^/Library/Printers/\""
220 " #\"^/Library/Security$\""
221 " #\"^/Library/Security/\""
222 " #\"^%s/Library$\"" /* RequestRoot/Library */
223 " #\"^%s/Library/\"" /* RequestRoot/Library/... */
224 " #\"^%s$\"" /* ServerBin */
225 " #\"^%s/\"" /* ServerBin/... */
226 " #\"^%s$\"" /* ServerRoot */
227 " #\"^%s/\"" /* ServerRoot/... */
228 "))\n",
229 request, request, bin, bin, root, root);
230 if (Sandboxing == CUPSD_SANDBOXING_RELAXED)
231 {
232 /* Limited write access to /Library/Printers/... */
233 cupsFilePuts(fp,
234 "(allow file-write*\n"
235 " (regex"
236 " #\"^/Library/Printers/.*/\""
237 "))\n");
238 cupsFilePrintf(fp,
239 "(deny file-write*\n"
240 " (regex"
241 " #\"^/Library/Printers/PPDs$\""
242 " #\"^/Library/Printers/PPDs/\""
243 " #\"^/Library/Printers/PPD Plugins$\""
244 " #\"^/Library/Printers/PPD Plugins/\""
245 ")%s)\n", nodebug);
246 }
247 /* Allow execution of child processes */
248 cupsFilePuts(fp, "(allow process-fork)\n");
249 cupsFilePrintf(fp,
250 "(allow process-exec\n"
251 " (regex"
252 " #\"^/bin/\"" /* /bin/... */
253 " #\"^/usr/bin/\"" /* /usr/bin/... */
254 " #\"^/usr/libexec/cups/\"" /* /usr/libexec/cups/... */
255 " #\"^/usr/libexec/fax/\"" /* /usr/libexec/fax/... */
256 " #\"^/usr/sbin/\"" /* /usr/sbin/... */
257 " #\"^%s/\"" /* ServerBin/... */
258 " #\"^/Library/Printers/.*/\""
259 " #\"^/System/Library/Frameworks/Python.framework/\""
260 "))\n",
261 bin);
262 if (RunUser && getenv("CUPS_TESTROOT"))
263 {
264 /* Allow source directory access in "make test" environment */
265 char testroot[1024]; /* Root directory of test files */
266
267 cupsd_requote(testroot, getenv("CUPS_TESTROOT"), sizeof(testroot));
268
269 cupsFilePrintf(fp,
270 "(allow file-write* file-read-data file-read-metadata\n"
271 " (regex"
272 " #\"^%s$\"" /* CUPS_TESTROOT */
273 " #\"^%s/\"" /* CUPS_TESTROOT/... */
274 "))\n",
275 testroot, testroot);
276 cupsFilePrintf(fp,
277 "(allow process-exec\n"
278 " (regex"
279 " #\"^%s/\"" /* CUPS_TESTROOT/... */
280 "))\n",
281 testroot);
282 }
283 if (job_id)
284 {
285 /* Allow job filters to read the current job files... */
286 cupsFilePrintf(fp,
287 "(allow file-read-data file-read-metadata\n"
288 " (regex #\"^%s/([ac]%05d|d%05d-[0-9][0-9][0-9])$\"))\n",
289 request, job_id, job_id);
290 }
291 else
292 {
293 /* Allow email notifications from notifiers... */
294 cupsFilePuts(fp,
295 "(allow process-exec\n"
296 " (literal \"/usr/sbin/sendmail\")\n"
297 " (with no-sandbox))\n");
298 }
299 /* Allow outbound networking to local services */
300 cupsFilePuts(fp, "(allow network-outbound"
301 "\n (regex #\"^/private/var/run/\" #\"^/private/tmp/\")");
302 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
303 lis;
304 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
305 {
306 if (httpAddrFamily(&(lis->address)) == AF_LOCAL)
307 {
308 httpAddrString(&(lis->address), domain, sizeof(domain));
309 cupsFilePrintf(fp, "\n (literal \"%s\")", domain);
310 }
311 }
312 if (allow_networking)
313 {
314 /* Allow TCP and UDP networking off the machine... */
315 cupsFilePuts(fp, "\n (remote tcp))\n");
316 cupsFilePuts(fp, "(allow network-bind)\n"); /* for LPD resvport */
317 cupsFilePuts(fp, "(allow network*\n"
318 " (local udp \"*:*\")\n"
319 " (remote udp \"*:*\"))\n");
320
321 /* Also allow access to Bluetooth, USB, device files, etc. */
322 cupsFilePuts(fp, "(allow iokit*)\n");
323 cupsFilePuts(fp, "(allow file-write* file-read-data file-read-metadata file-ioctl\n"
324 " (regex #\"^/dev/\"))\n");
325 cupsFilePuts(fp, "(allow distributed-notification-post)\n");
326 }
327 else
328 {
329 /* Only allow SNMP (UDP) off the machine... */
330 cupsFilePuts(fp, ")\n");
331 cupsFilePuts(fp, "(allow network-outbound\n"
332 " (remote udp \"*:161\"))\n");
333 cupsFilePuts(fp, "(allow network-inbound\n"
334 " (local udp \"localhost:*\"))\n");
335 cupsFilePuts(fp, "(deny iokit* (with no-report))\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 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
826 static int /* O - Result of comparison */
827 compare_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
839 static char * /* O - Quoted string */
840 cupsd_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$".
874 */