]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/process.c
<rdar://problem/15958253> 14A125b: cupsd console output when launching AddPrinter
[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 {
67 #ifdef HAVE_SANDBOX_H
68 cups_file_t *fp; /* File pointer */
69 char profile[1024], /* File containing the profile */
70 cache[1024], /* Quoted CacheDir */
71 request[1024], /* Quoted RequestRoot */
72 root[1024], /* Quoted ServerRoot */
73 temp[1024]; /* Quoted TempDir */
74 const char *nodebug; /* " (with no-log)" for no debug */
75
76
77 if (!UseProfiles)
78 {
79 /*
80 * Only use sandbox profiles as root...
81 */
82
83 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d) = NULL",
84 job_id);
85
86 return (NULL);
87 }
88
89 if ((fp = cupsTempFile2(profile, sizeof(profile))) == NULL)
90 {
91 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d) = NULL",
92 job_id);
93 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create security profile: %s",
94 strerror(errno));
95 return (NULL);
96 }
97
98 fchown(cupsFileNumber(fp), RunUser, Group);
99 fchmod(cupsFileNumber(fp), 0640);
100
101 cupsd_requote(cache, CacheDir, sizeof(cache));
102 cupsd_requote(request, RequestRoot, sizeof(request));
103 cupsd_requote(root, ServerRoot, sizeof(root));
104 cupsd_requote(temp, TempDir, sizeof(temp));
105
106 nodebug = LogLevel < CUPSD_LOG_DEBUG ? " (with no-log)" : "";
107
108 cupsFilePuts(fp, "(version 1)\n");
109 cupsFilePuts(fp, "(allow default)\n");
110 cupsFilePrintf(fp,
111 "(deny file-write* file-read-data file-read-metadata\n"
112 " (regex"
113 " #\"^%s$\"" /* RequestRoot */
114 " #\"^%s/\"" /* RequestRoot/... */
115 ")%s)\n",
116 request, request, nodebug);
117 if (!RunUser)
118 cupsFilePrintf(fp,
119 "(deny file-write* file-read-data file-read-metadata\n"
120 " (regex"
121 " #\"^/Users$\""
122 " #\"^/Users/\""
123 ")%s)\n", nodebug);
124 cupsFilePrintf(fp,
125 "(deny file-write*\n"
126 " (regex"
127 " #\"^%s$\"" /* ServerRoot */
128 " #\"^%s/\"" /* ServerRoot/... */
129 " #\"^/private/etc$\""
130 " #\"^/private/etc/\""
131 " #\"^/usr/local/etc$\""
132 " #\"^/usr/local/etc/\""
133 " #\"^/Library$\""
134 " #\"^/Library/\""
135 " #\"^/System$\""
136 " #\"^/System/\""
137 ")%s)\n",
138 root, root, nodebug);
139 /* Specifically allow applications to stat RequestRoot */
140 cupsFilePrintf(fp,
141 "(allow file-read-metadata\n"
142 " (regex"
143 " #\"^%s$\"" /* RequestRoot */
144 "))\n",
145 request);
146 cupsFilePrintf(fp,
147 "(allow file-write* file-read-data file-read-metadata\n"
148 " (regex"
149 " #\"^%s$\"" /* TempDir */
150 " #\"^%s/\"" /* TempDir/... */
151 " #\"^%s$\"" /* CacheDir */
152 " #\"^%s/\"" /* CacheDir/... */
153 " #\"^%s/Library$\"" /* RequestRoot/Library */
154 " #\"^%s/Library/\"" /* RequestRoot/Library/... */
155 " #\"^/Library/Application Support/\""
156 " #\"^/Library/Caches/\""
157 " #\"^/Library/Preferences/\""
158 " #\"^/Library/Printers/.*/\""
159 " #\"^/Users/Shared/\""
160 "))\n",
161 temp, temp, cache, cache, request, request);
162 cupsFilePrintf(fp,
163 "(deny file-write*\n"
164 " (regex"
165 " #\"^/Library/Printers/PPDs$\""
166 " #\"^/Library/Printers/PPDs/\""
167 " #\"^/Library/Printers/PPD Plugins$\""
168 " #\"^/Library/Printers/PPD Plugins/\""
169 ")%s)\n", nodebug);
170 if (job_id)
171 {
172 /*
173 * Allow job filters to read the spool file(s)...
174 */
175
176 cupsFilePrintf(fp,
177 "(allow file-read-data file-read-metadata\n"
178 " (regex #\"^%s/([ac]%05d|d%05d-[0-9][0-9][0-9])$\"))\n",
179 request, job_id, job_id);
180 }
181 else
182 {
183 /*
184 * Allow email notifications from notifiers...
185 */
186
187 cupsFilePuts(fp,
188 "(allow process-exec\n"
189 " (literal \"/usr/sbin/sendmail\")\n"
190 " (with no-sandbox)\n"
191 ")\n");
192 }
193
194 cupsFileClose(fp);
195
196 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d) = \"%s\"",
197 job_id, profile);
198 return ((void *)strdup(profile));
199
200 #else
201 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d) = NULL",
202 job_id);
203
204 return (NULL);
205 #endif /* HAVE_SANDBOX_H */
206 }
207
208
209 /*
210 * 'cupsdDestroyProfile()' - Delete an execution profile.
211 */
212
213 void
214 cupsdDestroyProfile(void *profile) /* I - Profile */
215 {
216 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeleteProfile(profile=\"%s\")",
217 profile ? (char *)profile : "(null)");
218
219 #ifdef HAVE_SANDBOX_H
220 if (profile)
221 {
222 unlink((char *)profile);
223 free(profile);
224 }
225 #endif /* HAVE_SANDBOX_H */
226 }
227
228
229 /*
230 * 'cupsdEndProcess()' - End a process.
231 */
232
233 int /* O - 0 on success, -1 on failure */
234 cupsdEndProcess(int pid, /* I - Process ID */
235 int force) /* I - Force child to die */
236 {
237 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdEndProcess(pid=%d, force=%d)", pid,
238 force);
239
240 if (!pid)
241 return (0);
242
243 if (!RunUser)
244 {
245 /*
246 * When running as root, cupsd puts child processes in their own process
247 * group. Using "-pid" sends a signal to all processes in the group.
248 */
249
250 pid = -pid;
251 }
252
253 if (force)
254 return (kill(pid, SIGKILL));
255 else
256 return (kill(pid, SIGTERM));
257 }
258
259
260 /*
261 * 'cupsdFinishProcess()' - Finish a process and get its name.
262 */
263
264 const char * /* O - Process name */
265 cupsdFinishProcess(int pid, /* I - Process ID */
266 char *name, /* I - Name buffer */
267 int namelen, /* I - Size of name buffer */
268 int *job_id) /* O - Job ID pointer or NULL */
269 {
270 cupsd_proc_t key, /* Search key */
271 *proc; /* Matching process */
272
273
274 key.pid = pid;
275
276 if ((proc = (cupsd_proc_t *)cupsArrayFind(process_array, &key)) != NULL)
277 {
278 if (job_id)
279 *job_id = proc->job_id;
280
281 strlcpy(name, proc->name, namelen);
282 cupsArrayRemove(process_array, proc);
283 free(proc);
284 }
285 else
286 {
287 if (job_id)
288 *job_id = 0;
289
290 strlcpy(name, "unknown", namelen);
291 }
292
293 cupsdLogMessage(CUPSD_LOG_DEBUG2,
294 "cupsdFinishProcess(pid=%d, name=%p, namelen=%d, "
295 "job_id=%p(%d)) = \"%s\"", pid, name, namelen, job_id,
296 job_id ? *job_id : 0, name);
297
298 return (name);
299 }
300
301
302 /*
303 * 'cupsdStartProcess()' - Start a process.
304 */
305
306 int /* O - Process ID or 0 */
307 cupsdStartProcess(
308 const char *command, /* I - Full path to command */
309 char *argv[], /* I - Command-line arguments */
310 char *envp[], /* I - Environment */
311 int infd, /* I - Standard input file descriptor */
312 int outfd, /* I - Standard output file descriptor */
313 int errfd, /* I - Standard error file descriptor */
314 int backfd, /* I - Backchannel file descriptor */
315 int sidefd, /* I - Sidechannel file descriptor */
316 int root, /* I - Run as root? */
317 void *profile, /* I - Security profile to use */
318 cupsd_job_t *job, /* I - Job associated with process */
319 int *pid) /* O - Process ID */
320 {
321 int i; /* Looping var */
322 const char *exec_path = command; /* Command to be exec'd */
323 char *real_argv[107], /* Real command-line arguments */
324 cups_exec[1024]; /* Path to "cups-exec" program */
325 uid_t user; /* Command UID */
326 cupsd_proc_t *proc; /* New process record */
327 #ifdef HAVE_POSIX_SPAWN
328 posix_spawn_file_actions_t actions; /* Spawn file actions */
329 posix_spawnattr_t attrs; /* Spawn attributes */
330 char user_str[16], /* User string */
331 group_str[16], /* Group string */
332 nice_str[16]; /* FilterNice string */
333 #endif /* HAVE_POSIX_SPAWN */
334 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
335 struct sigaction action; /* POSIX signal handler */
336 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
337 #if defined(__APPLE__)
338 char processPath[1024], /* CFProcessPath environment variable */
339 linkpath[1024]; /* Link path for symlinks... */
340 int linkbytes; /* Bytes for link path */
341 #endif /* __APPLE__ */
342
343
344 *pid = 0;
345
346 /*
347 * Figure out the UID for the child process...
348 */
349
350 if (RunUser)
351 user = RunUser;
352 else if (root)
353 user = 0;
354 else
355 user = User;
356
357 /*
358 * Check the permissions of the command we are running...
359 */
360
361 if (_cupsFileCheck(command, _CUPS_FILE_CHECK_PROGRAM, !RunUser,
362 cupsdLogFCMessage, job ? job->printer : NULL))
363 return (0);
364
365 #if defined(__APPLE__)
366 if (envp)
367 {
368 /*
369 * Add special voodoo magic for OS X - this allows OS X programs to access
370 * their bundle resources properly...
371 */
372
373 if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0)
374 {
375 /*
376 * Yes, this is a symlink to the actual program, nul-terminate and
377 * use it...
378 */
379
380 linkpath[linkbytes] = '\0';
381
382 if (linkpath[0] == '/')
383 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s",
384 linkpath);
385 else
386 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s",
387 dirname((char *)command), linkpath);
388 }
389 else
390 snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", command);
391
392 envp[0] = processPath; /* Replace <CFProcessPath> string */
393 }
394 #endif /* __APPLE__ */
395
396 /*
397 * Use helper program when we have a sandbox profile...
398 */
399
400 #ifndef HAVE_POSIX_SPAWN
401 if (profile)
402 #endif /* !HAVE_POSIX_SPAWN */
403 {
404 snprintf(cups_exec, sizeof(cups_exec), "%s/daemon/cups-exec", ServerBin);
405 snprintf(user_str, sizeof(user_str), "%d", User);
406 snprintf(group_str, sizeof(group_str), "%d", Group);
407 snprintf(nice_str, sizeof(nice_str), "%d", FilterNice);
408
409 real_argv[0] = cups_exec;
410 real_argv[1] = profile;
411 real_argv[2] = user_str;
412 real_argv[3] = group_str;
413 real_argv[4] = nice_str;
414 real_argv[5] = (char *)command;
415
416 for (i = 0;
417 i < (int)(sizeof(real_argv) / sizeof(real_argv[0]) - 7) && argv[i];
418 i ++)
419 real_argv[i + 6] = argv[i];
420
421 real_argv[i + 6] = NULL;
422
423 argv = real_argv;
424 exec_path = cups_exec;
425 }
426
427 if (LogLevel == CUPSD_LOG_DEBUG2)
428 {
429 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Preparing to start \"%s\", arguments:", command);
430
431 for (i = 0; argv[i]; i ++)
432 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: argv[%d] = \"%s\"", i, argv[i]);
433 }
434
435 #ifdef HAVE_POSIX_SPAWN
436 /*
437 * Setup attributes and file actions for the spawn...
438 */
439
440 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting spawn attributes.");
441 posix_spawnattr_init(&attrs);
442 posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF);
443
444 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting file actions.");
445 posix_spawn_file_actions_init(&actions);
446 if (infd != 0)
447 {
448 if (infd < 0)
449 posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_WRONLY, 0);
450 else
451 posix_spawn_file_actions_adddup2(&actions, infd, 0);
452 }
453
454 if (outfd != 1)
455 {
456 if (outfd < 0)
457 posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0);
458 else
459 posix_spawn_file_actions_adddup2(&actions, outfd, 1);
460 }
461
462 if (errfd != 2)
463 {
464 if (errfd < 0)
465 posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0);
466 else
467 posix_spawn_file_actions_adddup2(&actions, errfd, 2);
468 }
469
470 if (backfd != 3 && backfd >= 0)
471 posix_spawn_file_actions_adddup2(&actions, backfd, 3);
472
473 if (sidefd != 4 && sidefd >= 0)
474 posix_spawn_file_actions_adddup2(&actions, sidefd, 4);
475
476 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Calling posix_spawn.");
477
478 if (posix_spawn(pid, exec_path, &actions, &attrs, argv, envp ? envp : environ))
479 {
480 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno));
481
482 *pid = 0;
483 }
484 else
485 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: pid=%d", (int)*pid);
486
487 posix_spawn_file_actions_destroy(&actions);
488 posix_spawnattr_destroy(&attrs);
489
490 #else
491 /*
492 * Block signals before forking...
493 */
494
495 cupsdHoldSignals();
496
497 if ((*pid = fork()) == 0)
498 {
499 /*
500 * Child process goes here; update stderr as needed...
501 */
502
503 if (errfd != 2)
504 {
505 if (errfd < 0)
506 errfd = open("/dev/null", O_WRONLY);
507
508 if (errfd != 2)
509 {
510 dup2(errfd, 2);
511 close(errfd);
512 }
513 }
514
515 /*
516 * Put this process in its own process group so that we can kill any child
517 * processes it creates.
518 */
519
520 # ifdef HAVE_SETPGID
521 if (!RunUser && setpgid(0, 0))
522 exit(errno + 100);
523 # else
524 if (!RunUser && setpgrp())
525 exit(errno + 100);
526 # endif /* HAVE_SETPGID */
527
528 /*
529 * Update the remaining file descriptors as needed...
530 */
531
532 if (infd != 0)
533 {
534 if (infd < 0)
535 infd = open("/dev/null", O_RDONLY);
536
537 if (infd != 0)
538 {
539 dup2(infd, 0);
540 close(infd);
541 }
542 }
543
544 if (outfd != 1)
545 {
546 if (outfd < 0)
547 outfd = open("/dev/null", O_WRONLY);
548
549 if (outfd != 1)
550 {
551 dup2(outfd, 1);
552 close(outfd);
553 }
554 }
555
556 if (backfd != 3 && backfd >= 0)
557 {
558 dup2(backfd, 3);
559 close(backfd);
560 fcntl(3, F_SETFL, O_NDELAY);
561 }
562
563 if (sidefd != 4 && sidefd >= 0)
564 {
565 dup2(sidefd, 4);
566 close(sidefd);
567 fcntl(4, F_SETFL, O_NDELAY);
568 }
569
570 /*
571 * Change the priority of the process based on the FilterNice setting.
572 * (this is not done for root processes...)
573 */
574
575 if (!root)
576 nice(FilterNice);
577
578 /*
579 * Reset group membership to just the main one we belong to.
580 */
581
582 if (!RunUser && setgid(Group))
583 exit(errno + 100);
584
585 if (!RunUser && setgroups(1, &Group))
586 exit(errno + 100);
587
588 /*
589 * Change user to something "safe"...
590 */
591
592 if (!RunUser && user && setuid(user))
593 exit(errno + 100);
594
595 /*
596 * Change umask to restrict permissions on created files...
597 */
598
599 umask(077);
600
601 /*
602 * Unblock signals before doing the exec...
603 */
604
605 # ifdef HAVE_SIGSET
606 sigset(SIGTERM, SIG_DFL);
607 sigset(SIGCHLD, SIG_DFL);
608 sigset(SIGPIPE, SIG_DFL);
609 # elif defined(HAVE_SIGACTION)
610 memset(&action, 0, sizeof(action));
611
612 sigemptyset(&action.sa_mask);
613 action.sa_handler = SIG_DFL;
614
615 sigaction(SIGTERM, &action, NULL);
616 sigaction(SIGCHLD, &action, NULL);
617 sigaction(SIGPIPE, &action, NULL);
618 # else
619 signal(SIGTERM, SIG_DFL);
620 signal(SIGCHLD, SIG_DFL);
621 signal(SIGPIPE, SIG_DFL);
622 # endif /* HAVE_SIGSET */
623
624 cupsdReleaseSignals();
625
626 /*
627 * Execute the command; if for some reason this doesn't work, log an error
628 * exit with a non-zero value...
629 */
630
631 if (envp)
632 execve(exec_path, argv, envp);
633 else
634 execv(exec_path, argv);
635
636 exit(errno + 100);
637 }
638 else if (*pid < 0)
639 {
640 /*
641 * Error - couldn't fork a new process!
642 */
643
644 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command,
645 strerror(errno));
646
647 *pid = 0;
648 }
649
650 cupsdReleaseSignals();
651 #endif /* HAVE_POSIX_SPAWN */
652
653 if (*pid)
654 {
655 if (!process_array)
656 process_array = cupsArrayNew((cups_array_func_t)compare_procs, NULL);
657
658 if (process_array)
659 {
660 if ((proc = calloc(1, sizeof(cupsd_proc_t) + strlen(command))) != NULL)
661 {
662 proc->pid = *pid;
663 proc->job_id = job ? job->id : 0;
664 _cups_strcpy(proc->name, command);
665
666 cupsArrayAdd(process_array, proc);
667 }
668 }
669 }
670
671 cupsdLogMessage(CUPSD_LOG_DEBUG2,
672 "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, "
673 "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, "
674 "profile=%p, job=%p(%d), pid=%p) = %d",
675 command, argv, envp, infd, outfd, errfd, backfd, sidefd,
676 root, profile, job, job ? job->id : 0, pid, *pid);
677
678 return (*pid);
679 }
680
681
682 /*
683 * 'compare_procs()' - Compare two processes.
684 */
685
686 static int /* O - Result of comparison */
687 compare_procs(cupsd_proc_t *a, /* I - First process */
688 cupsd_proc_t *b) /* I - Second process */
689 {
690 return (a->pid - b->pid);
691 }
692
693
694 #ifdef HAVE_SANDBOX_H
695 /*
696 * 'cupsd_requote()' - Make a regular-expression version of a string.
697 */
698
699 static char * /* O - Quoted string */
700 cupsd_requote(char *dst, /* I - Destination buffer */
701 const char *src, /* I - Source string */
702 size_t dstsize) /* I - Size of destination buffer */
703 {
704 int ch; /* Current character */
705 char *dstptr, /* Current position in buffer */
706 *dstend; /* End of destination buffer */
707
708
709 dstptr = dst;
710 dstend = dst + dstsize - 2;
711
712 while (*src && dstptr < dstend)
713 {
714 ch = *src++;
715
716 if (ch == '/' && !*src)
717 break; /* Don't add trailing slash */
718
719 if (strchr(".?*()[]^$\\", ch))
720 *dstptr++ = '\\';
721
722 *dstptr++ = (char)ch;
723 }
724
725 *dstptr = '\0';
726
727 return (dst);
728 }
729 #endif /* HAVE_SANDBOX_H */
730
731
732 /*
733 * End of "$Id$".
734 */