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