xref: /titanic_54/usr/src/cmd/lp/cmd/lpsched/exec.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <dial.h>
34 
35 #include <stdlib.h>
36 #include "limits.h"
37 #include "stdarg.h"
38 #include "wait.h"
39 #include "dial.h"
40 #include "lpsched.h"
41 #include <syslog.h>
42 #include <pwd.h>
43 
44 #define Done(EC,ERRNO)	done(((EC) << 8),ERRNO)
45 
46 #define	STRLCAT(dst, src, size) \
47 	if (strlcat((dst), (src), (size)) >= (size)) { \
48 		errno = EINVAL; \
49 		return (-1); \
50 	}
51 
52 static MESG *		ChildMd;
53 
54 static int		ChildPid;
55 static int		WaitedChildPid;
56 static int		slot;
57 static int		do_undial;
58 
59 static char		argbuf[ARG_MAX];
60 
61 static long		key;
62 
63 static void		sigtrap ( int );
64 static void		done ( int , int );
65 static void		cool_heels ( void );
66 static void		addenv (char ***envp, char * , char * );
67 static void		trap_fault_signals ( void );
68 static void		ignore_fault_signals ( void );
69 static void		child_mallocfail ( void );
70 static void		Fork2 ( void );
71 
72 static int		Fork1 ( EXEC * );
73 
74 static void
75 relock(void)
76 {
77 	struct flock		l;
78 
79 	l.l_type = F_WRLCK;
80 	l.l_whence = 1;
81 	l.l_start = 0;
82 	l.l_len = 0;
83 	(void)Fcntl (lock_fd, F_SETLK, &l);
84 	return;
85 }
86 
87 static char *_exec_name(int type)
88 {
89 	static char *_names[] = {
90 	"", "EX_INTERF", "EX_SLOWF", "EX_ALERT", "EX_FALERT", "EX_PALERT",
91 	"EX_NOTIFY", "EX_FAULT_MESSAGE", "EX_FORM_MESSAGE", NULL };
92 
93 	if ((type < 0) || (type > EX_FORM_MESSAGE))
94 		return ("BAD_EXEC_TYPE");
95 	else
96 		return (_names[type]);
97 }
98 
99 /*
100  * This function replaces characters in a string that might be used
101  * to exploit a security hole.  Replace command seperators (`, &, ;, |, ^),
102  * output redirection (>, |), variable expansion ($), and character
103  * escape (\).
104  *
105  * Bugid 4141687
106  * Add ( ) < * ? [
107  * Bugid 4139071
108  * Remove \
109  */
110 void clean_string(char *ptr)
111 {
112 	char *cp;
113 	wchar_t wc;
114 	size_t len;
115 
116 	for (cp = ptr; *cp != NULL; ) {
117 		if ((len = mbtowc(&wc, cp, MB_CUR_MAX)) == -1) {
118 			cp++;
119 			continue;
120 		}
121 
122 		if (len == 1 &&
123 		    ((wc == L'`') || (wc == L'&') || (wc == L';') ||
124 		    (wc == L'|') || (wc == L'>') || (wc == L'^') ||
125 		    (wc == L'$') || (wc == L'(') || (wc == L')') ||
126 		    (wc == L'<') || (wc == L'*') || (wc == L'?') ||
127 		    (wc == L'[')))
128 			*cp = '_';
129 		cp += len;
130 	}
131 }
132 
133 enum trust {TRUSTED, UNTRUSTED};
134 static char *
135 arg_string(enum trust type, char *fmt, ...)
136 {
137 	char buf[BUFSIZ];
138 	va_list	args;
139 
140 	va_start(args, fmt);
141 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
142 	va_end(args);
143 
144 	/*
145 	 * If the string contains data from an untrusted origin (user supplied),
146 	 * clean it up in case one of our progeny is a shell script and isn't
147 	 * careful about checking its input.
148 	 */
149 	if (type == UNTRUSTED)
150 		clean_string(buf);
151 
152 	return (strdup(buf));
153 }
154 
155 /* stolen from libc/gen/port/gen/execvp.c */
156 static const char *
157 execat(const char *s1, const char *s2, char *si)
158 {
159         char    *s;
160         int cnt = PATH_MAX + 1; /* number of characters in s2 */
161 
162         s = si;
163         while (*s1 && *s1 != ':') {
164                 if (cnt > 0) {
165                         *s++ = *s1++;
166                         cnt--;
167                 } else
168                         s1++;
169         }
170         if (si != s && cnt > 0) {
171                 *s++ = '/';
172                 cnt--;
173         }
174         while (*s2 && cnt > 0) {
175                 *s++ = *s2++;
176                 cnt--;
177         }
178         *s = '\0';
179         return (*s1 ? ++s1: 0);
180 }
181 
182 /*
183  * Similiar to execvp(), execpt you can supply an environment and we always
184  * use /bin/sh for shell scripts.  The PATH searched is the PATH in the
185  * current environment, not the environment in the argument list.
186  * This was pretty much stolen from libc/gen/port/execvp.c
187  */
188 static int
189 execvpe(char *name, char *const argv[], char *const envp[])
190 {
191 	char *path;
192 	char fname[PATH_MAX+2];
193 	char *newargs[256];
194 	int i;
195 	const char *cp;
196 	unsigned etxtbsy = 1;
197         int eacces = 0;
198 
199 	if (*name == '\0') {
200 		errno = ENOENT;
201 		return (-1);
202 	}
203 
204 	if ((path = getenv("PATH")) == NULL)
205 		path = "/usr/bin:/bin";
206 
207         cp = strchr(name, '/')? (const char *)"": path;
208 
209         do {
210                 cp = execat(cp, name, fname);
211         retry:
212                 /*
213                  * 4025035 and 4038378
214                  * if a filename begins with a "-" prepend "./" so that
215                  * the shell can't interpret it as an option
216                  */
217                 if (*fname == '-') {
218                         size_t size = strlen(fname) + 1;
219                         if ((size + 2) > sizeof (fname)) {
220                                 errno = E2BIG;
221                                 return (-1);
222                         }
223                         (void) memmove(fname + 2, fname, size);
224                         fname[0] = '.';
225                         fname[1] = '/';
226                 }
227                 (void) execve(fname, argv, envp);
228                 switch (errno) {
229                 case ENOEXEC:
230                         newargs[0] = "sh";
231                         newargs[1] = fname;
232                         for (i = 1; (newargs[i + 1] = argv[i]) != NULL; ++i) {
233                                 if (i >= 254) {
234                                         errno = E2BIG;
235                                         return (-1);
236                                 }
237                         }
238                         (void) execve("/bin/sh", newargs, envp);
239                         return (-1);
240                 case ETXTBSY:
241                         if (++etxtbsy > 5)
242                                 return (-1);
243                         (void) sleep(etxtbsy);
244                         goto retry;
245                 case EACCES:
246                         ++eacces;
247                         break;
248                 case ENOMEM:
249                 case E2BIG:
250                 case EFAULT:
251                         return (-1);
252                 }
253         } while (cp);
254         if (eacces)
255                 errno = EACCES;
256         return (-1);
257 }
258 
259 static char time_buf[50];
260 /**
261  ** exec() - FORK AND EXEC CHILD PROCESS
262  **/
263 
264 /*VARARGS1*/
265 int
266 exec(int type, ...)
267 {
268 	va_list			args;
269 
270 	int			i;
271 	int			procuid;
272 	int			procgid;
273 	int			ret;
274 	int			fr_flg;
275 
276 	char			*cp;
277 	char			*infile;
278 	char			*outfile;
279 	char			*errfile;
280 	char			*sep;
281 
282 	char			**listp;
283 	char			**file_list;
284 	char			*printerName;
285 	char			*printerNameToShow;
286 	static char		nameBuf[100];
287 	char			*clean_title;
288 
289 	PSTATUS			*printer;
290 
291 	RSTATUS			*request;
292 
293 	FSTATUS			*form;
294 
295 	EXEC			*ep;
296 
297 	PWSTATUS		*pwheel;
298 	time_t			now;
299 	struct passwd		*pwp;
300 #ifdef LP_USE_PAPI_ATTR
301 	struct stat		tmpBuf;
302 	char 			tmpName[BUFSIZ];
303 	char			*path = NULL;
304 #endif
305 	char *av[ARG_MAX];
306 	char **envp = NULL;
307 	int ac = 0;
308 
309 	syslog(LOG_DEBUG, "exec(%s)", _exec_name(type));
310 
311 	memset(av, 0, sizeof (*av));
312 
313 	va_start (args, type);
314 
315 	switch (type) {
316 
317 	case EX_INTERF:
318 		printer = va_arg(args, PSTATUS *);
319 		if (printer->status & PS_REMOTE) {
320 			errno = EINVAL;
321 			return (-1);
322 		}
323 		request = printer->request;
324 		ep = printer->exec;
325 		break;
326 
327 	case EX_FAULT_MESSAGE:
328 		printer = va_arg(args, PSTATUS *);
329 		request = va_arg(args, RSTATUS *);
330 		if (! ( printer->status & (PS_FORM_FAULT | PS_SHOW_FAULT))) {
331 			return(0);
332 		}
333 		ep = printer->fault_exec;
334 		printerName = (printer->printer && printer->printer->name
335 				  ? printer->printer->name : "??");
336 			snprintf(nameBuf, sizeof (nameBuf),
337 				"%s (on %s)\n", printerName, Local_System);
338 
339 		printerNameToShow = nameBuf;
340 
341 		(void) time(&now);
342 		(void) strftime(time_buf, sizeof (time_buf),
343 			NULL, localtime(&now));
344 		break;
345 
346 	case EX_SLOWF:
347 		request = va_arg(args, RSTATUS *);
348 		ep = request->exec;
349 		break;
350 
351 	case EX_NOTIFY:
352 		request = va_arg(args, RSTATUS *);
353 		if (request->request->actions & ACT_NOTIFY) {
354 			errno = EINVAL;
355 			return (-1);
356 		}
357 		ep = request->exec;
358 		break;
359 
360 	case EX_ALERT:
361 		printer = va_arg(args, PSTATUS *);
362 		if (!(printer->printer->fault_alert.shcmd)) {
363 			errno = EINVAL;
364 			return(-1);
365 		}
366 		ep = printer->alert->exec;
367 		break;
368 
369 	case EX_PALERT:
370 		pwheel = va_arg(args, PWSTATUS *);
371 		ep = pwheel->alert->exec;
372 		break;
373 
374 	case EX_FORM_MESSAGE:
375 		(void) time(&now);
376 		(void) strftime(time_buf, sizeof (time_buf),
377 			NULL, localtime(&now));
378 
379 		/*FALLTHRU*/
380 	case EX_FALERT:
381 		form = va_arg(args, FSTATUS *);
382 		ep = form->alert->exec;
383 		break;
384 
385 	default:
386 		errno = EINVAL;
387 		return(-1);
388 
389 	}
390 	va_end (args);
391 
392 	if (!ep || (ep->pid > 0)) {
393 		errno = EBUSY;
394 		return(-1);
395 	}
396 
397 	ep->flags = 0;
398 
399 	key = ep->key = getkey();
400 	slot = ep - Exec_Table;
401 
402 	switch ((ep->pid = Fork1(ep))) {
403 
404 	case -1:
405 		relock ();
406 		return(-1);
407 
408 	case 0:
409 		/*
410 		 * We want to be able to tell our parent how we died.
411 		 */
412 		lp_alloc_fail_handler = child_mallocfail;
413 		break;
414 
415 	default:
416 		switch(type) {
417 
418 		case EX_INTERF:
419 			request->request->outcome |= RS_PRINTING;
420 			break;
421 
422 		case EX_NOTIFY:
423 			request->request->outcome |= RS_NOTIFYING;
424 			break;
425 
426 		case EX_SLOWF:
427 			request->request->outcome |= RS_FILTERING;
428 			request->request->outcome &= ~RS_REFILTER;
429 			break;
430 
431 		}
432 		return(0);
433 
434 	}
435 
436 	for (i = 0; i < NSIG; i++)
437 		(void)signal (i, SIG_DFL);
438 	(void)signal (SIGALRM, SIG_IGN);
439 	(void)signal (SIGTERM, sigtrap);
440 
441 	for (i = 0; i < OpenMax; i++)
442 		if (i != ChildMd->writefd)
443 			Close (i);
444 
445 	setpgrp();
446 
447 	/* Set a default path */
448 	addenv (&envp, "PATH", "/usr/lib/lp/bin:/usr/bin:/bin:/usr/sbin:/sbin");
449 	/* copy locale related variables */
450 	addenv (&envp, "TZ", getenv("TZ"));
451 	addenv (&envp, "LANG", getenv("LANG"));
452 	addenv (&envp, "LC_ALL", getenv("LC_ALL"));
453 	addenv (&envp, "LC_COLLATE", getenv("LC_COLLATE"));
454 	addenv (&envp, "LC_CTYPE", getenv("LC_CTYPE"));
455 	addenv (&envp, "LC_MESSAGES", getenv("LC_MESSAGES"));
456 	addenv (&envp, "LC_MONETARY", getenv("LC_MONETARY"));
457 	addenv (&envp, "LC_NUMERIC", getenv("LC_NUMERIC"));
458 	addenv (&envp, "LC_TIME", getenv("LC_TIME"));
459 
460 	sprintf ((cp = BIGGEST_NUMBER_S), "%ld", key);
461 	addenv (&envp, "SPOOLER_KEY", cp);
462 
463 #if	defined(DEBUG)
464 	addenv (&envp, "LPDEBUG", (debug? "1" : "0"));
465 #endif
466 
467 	/*
468 	 * Open the standard input, standard output, and standard error.
469 	 */
470 	switch (type) {
471 
472 	case EX_SLOWF:
473 	case EX_INTERF:
474 		/*
475 		 * stdin:  /dev/null
476 		 * stdout: /dev/null (EX_SLOWF), printer port (EX_INTERF)
477 		 * stderr: req#
478 		 */
479 		infile = 0;
480 		outfile = 0;
481 		errfile = makereqerr(request);
482 		break;
483 
484 	case EX_NOTIFY:
485 		/*
486 		 * stdin:  req#
487 		 * stdout: /dev/null
488 		 * stderr: /dev/null
489 		 */
490 		infile = makereqerr(request);
491 		outfile = 0;
492 		errfile = 0;
493 
494 		break;
495 
496 	case EX_ALERT:
497 	case EX_FALERT:
498 	case EX_PALERT:
499 	case EX_FAULT_MESSAGE:
500 	case EX_FORM_MESSAGE:
501 		/*
502 		 * stdin:  /dev/null
503 		 * stdout: /dev/null
504 		 * stderr: /dev/null
505 		 */
506 		infile = 0;
507 		outfile = 0;
508 		errfile = 0;
509 		break;
510 
511 	}
512 
513 	if (infile) {
514 		if (Open(infile, O_RDONLY) == -1)
515 			Done (EXEC_EXIT_NOPEN, errno);
516 	} else {
517 		if (Open("/dev/null", O_RDONLY) == -1)
518 			Done (EXEC_EXIT_NOPEN, errno);
519 	}
520 
521 	if (outfile) {
522 		if (Open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
523 			Done (EXEC_EXIT_NOPEN, errno);
524 	} else {
525 		/*
526 		 * If EX_INTERF, this is still needed to cause the
527 		 * standard error channel to be #2.
528 		 */
529 		if (Open("/dev/null", O_WRONLY) == -1)
530 			Done (EXEC_EXIT_NOPEN, errno);
531 	}
532 
533 	if (errfile) {
534 		if (Open(errfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
535 			Done (EXEC_EXIT_NOPEN, errno);
536 	} else {
537 		if (Open("/dev/null", O_WRONLY) == -1)
538 			Done (EXEC_EXIT_NOPEN, errno);
539 	}
540 
541 	switch (type) {
542 
543 	case EX_INTERF:
544 		/*
545 		 * Opening a ``port'' can be dangerous to our health:
546 		 *
547 		 *	- Hangups can occur if the line is dropped.
548 		 *	- The printer may send an interrupt.
549 		 *	- A FIFO may be closed, generating SIGPIPE.
550 		 *
551 		 * We catch these so we can complain nicely.
552 		 */
553 		trap_fault_signals ();
554 
555 		(void)Close (1);
556 
557 		if (strchr (request->secure->user, '!'))
558 		{
559 			procuid = Lp_Uid;
560 			procgid = Lp_Gid;
561 		}
562 		else
563 		{
564 			procuid = request->secure->uid;
565 			procgid = request->secure->gid;
566 		}
567 		if (printer->printer->dial_info)
568 		{
569 			ret = open_dialup(request->printer_type,
570 				printer->printer);
571 			if (ret == 0)
572 				do_undial = 1;
573 		}
574 		else
575 		{
576 			ret = open_direct(request->printer_type,
577 				printer->printer);
578 			do_undial = 0;
579 			/* this is a URI */
580 			if (is_printer_uri(printer->printer->device) == 0)
581 				addenv(&envp, "DEVICE_URI",
582 					 printer->printer->device);
583 		}
584 				addenv(&envp, "DEVICE_URI",
585 					 printer->printer->device);
586 		if (ret != 0)
587 			Done (ret, errno);
588 
589 		if (!(request->request->outcome & RS_FILTERED))
590 			file_list = request->request->file_list;
591 
592 		else {
593 			register int		count	= 0;
594 			register char *		num	= BIGGEST_REQID_S;
595 			register char *		prefix;
596 
597 			prefix = makestr(
598 				Lp_Tmp,
599 				"/",
600 				(request->secure && request->secure->system ?
601 					request->secure->system : Local_System),
602 				"/F",
603 				getreqno(request->secure->req_id),
604 				"-",
605 				(char *)0
606 			);
607 
608 			file_list = (char **)Malloc(
609 				(lenlist(request->request->file_list) + 1)
610 			      * sizeof(char *)
611 			);
612 
613 			for (
614 				listp = request->request->file_list;
615 				*listp;
616 				listp++
617 			) {
618 				sprintf (num, "%d", count + 1);
619 				file_list[count] = makestr(
620 					prefix,
621 					num,
622 					(char *)0
623 				);
624 				count++;
625 			}
626 			file_list[count] = 0;
627 		}
628 
629 #ifdef LP_USE_PAPI_ATTR
630 		/*
631 		 * Check if the PAPI job attribute file exists, if it does
632 		 * pass the file's pathname to the printer interface script
633 		 * in an environment variable. This file is created when
634 		 * print jobs are submitted via the PAPI interface.
635 		 */
636 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
637 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
638 		path = makepath(SPOOLDIR, "temp", tmpName, (char *)0);
639 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
640 		{
641 			/*
642 			 * IPP job attribute file exists for this job so
643 			 * set the environment variable
644 			 */
645 			syslog(LOG_DEBUG, "exec(): ATTRPATH='%s'", path);
646 			addenv(&envp, "ATTRPATH", path);
647 		}
648 		Free(path);
649 
650 		/*
651 		 * now set environment variable for the printer's PostScript
652 		 * Printer Description (PPD) file, this is used by the filter
653 		 * when forming the print data for this printer.
654 		 */
655 		if ((request->printer != NULL) &&
656 		    (request->printer->printer != NULL) &&
657 		    (request->printer->printer->name != NULL))
658 		{
659 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
660 				request->printer->printer->name);
661 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
662 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
663 			{
664 				syslog(LOG_DEBUG,
665 					"exec(): Printer PPD='%s'", path);
666 				addenv(&envp, "PPD", path);
667 			}
668 			Free(path);
669 		}
670 #endif
671 
672 		if (request->printer_type)
673 			addenv(&envp, "TERM", request->printer_type);
674 
675 		if (!(printer->printer->daisy)) {
676 			register char *	chset = 0;
677 			register char *	csp;
678 
679 			if (
680 				request->form
681 			     && request->form->form->chset
682 			     && request->form->form->mandatory
683 			     && !STREQU(NAME_ANY, request->form->form->chset)
684 			)
685 				chset = request->form->form->chset;
686 
687 			else if (
688 				request->request->charset
689 			     && !STREQU(NAME_ANY, request->request->charset)
690 			)
691 				chset = request->request->charset;
692 
693 			if (chset) {
694 				csp = search_cslist(
695 					chset,
696 					printer->printer->char_sets
697 				);
698 
699 				/*
700 				 * The "strtok()" below wrecks the string
701 				 * for future use, but this is a child
702 				 * process where it won't be needed again.
703 				 */
704 				addenv (&envp, "CHARSET",
705 					(csp? strtok(csp, "=") : chset)
706 				);
707 			}
708 		}
709 
710 		if (request->fast)
711 			addenv(&envp, "FILTER", request->fast);
712 
713 		/*
714 		*/
715 		if (strcmp (request->secure->user, request->request->user))
716 		{
717 			addenv (&envp, "ALIAS_USERNAME",
718 				request->request->user);
719 		}
720 		/*
721 		 * Add the system name to the user name (ala system!user)
722 		 * unless it is already there. RFS users may have trouble
723 		 * here, sorry!
724 		 */
725 		cp = strchr(request->secure->user, '@');
726 
727 		allTraysWithForm(printer, request->form);
728 
729 		/*
730 		 * Fix for 4137389
731 		 * Remove double quotes from title string.
732 		 */
733 		fr_flg = 1;
734 		clean_title = strdup(NB(request->request->title));
735 		if (clean_title == NULL) {
736 			/*
737 			 * strdup failed. We're probably hosed
738 			 * but try setting clean_title
739 			 * to original title and continuing.
740 			 */
741 			clean_title = NB(request->request->title);
742 			fr_flg = 0;
743 		} else if (strcmp(clean_title, "") != 0) {
744 			char *ct_p;
745 
746 			for (ct_p = clean_title; *ct_p != NULL; ct_p++) {
747 				if (*ct_p == '"')
748 					*ct_p = ' ';
749 			}
750 		}
751 
752 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces,
753 					printer->printer->name);
754 		av[ac++] = arg_string(TRUSTED, "%s", request->secure->req_id);
755 		av[ac++] = arg_string(UNTRUSTED, "%s%s%s",
756 					request->secure->user,
757 					(cp? "" : "@"),
758 					(cp? "" : request->secure->system));
759 		av[ac++] = arg_string(TRUSTED, "%s", clean_title);
760 		av[ac++] = arg_string(TRUSTED, "%d", request->copies);
761 
762 		if (fr_flg)
763 			free (clean_title);
764 
765 		sep = "";
766 
767 		/*
768 		 * Do the administrator defined key=value pair options
769 		 */
770 
771 		argbuf[0] = '\0';
772 
773 		if (printer->printer->options) {
774 			char **tmp = printer->printer->options;
775 			while(*tmp != NULL) {
776 				STRLCAT(argbuf, sep, sizeof (argbuf));
777 				sep = " ";
778 				STRLCAT(argbuf, *tmp++, sizeof (argbuf));
779 			}
780 		}
781 
782 		/*
783 		 * Do the administrator defined ``stty'' stuff before
784 		 * the user's -o options, to allow the user to override.
785 		 */
786 		if (printer->printer->stty) {
787 			STRLCAT (argbuf, sep, sizeof (argbuf));
788 			sep = " ";
789 			STRLCAT (argbuf, "stty='", sizeof (argbuf));
790 			STRLCAT (argbuf, printer->printer->stty,
791 			    sizeof (argbuf));
792 			STRLCAT (argbuf, "'", sizeof (argbuf));
793 		}
794 
795 		/*
796 		 * Do all of the user's options except the cpi/lpi/etc.
797 		 * stuff, which is done separately.
798 		 */
799 		if (request->request->options) {
800 			listp = dashos(request->request->options);
801 			while (*listp) {
802 				if (
803 					!STRNEQU(*listp, "cpi=", 4)
804 				     && !STRNEQU(*listp, "lpi=", 4)
805 				     && !STRNEQU(*listp, "width=", 6)
806 				     && !STRNEQU(*listp, "length=", 7)
807 				) {
808 					STRLCAT (argbuf, sep, sizeof (argbuf));
809 					sep = " ";
810 					STRLCAT (argbuf, *listp,
811 					    sizeof (argbuf));
812 				}
813 				listp++;
814 			}
815 		}
816 
817 		/*
818 		 * The "pickfilter()" routine (from "validate()")
819 		 * stored the cpi/lpi/etc. stuff that should be
820 		 * used for this request. It chose form over user,
821 		 * and user over printer.
822 		 */
823 		if (request->cpi) {
824 			STRLCAT (argbuf, sep, sizeof (argbuf));
825 			sep = " ";
826 			STRLCAT (argbuf, "cpi=", sizeof (argbuf));
827 			STRLCAT (argbuf, request->cpi, sizeof (argbuf));
828 		}
829 		if (request->lpi) {
830 			STRLCAT (argbuf, sep, sizeof (argbuf));
831 			sep = " ";
832 			STRLCAT (argbuf, "lpi=", sizeof (argbuf));
833 			STRLCAT (argbuf, request->lpi, sizeof (argbuf));
834 		}
835 		if (request->pwid) {
836 			STRLCAT (argbuf, sep, sizeof (argbuf));
837 			sep = " ";
838 			STRLCAT (argbuf, "width=", sizeof (argbuf));
839 			STRLCAT (argbuf, request->pwid, sizeof (argbuf));
840 		}
841 		if (request->plen) {
842 			STRLCAT (argbuf, sep, sizeof (argbuf));
843 			sep = " ";
844 			STRLCAT (argbuf, "length=", sizeof (argbuf));
845 			STRLCAT (argbuf, request->plen, sizeof (argbuf));
846 		}
847 
848 		/*
849 		 * Do the ``raw'' bit last, to ensure it gets
850 		 * done. If the user doesn't want this, then he or
851 		 * she can do the correct thing using -o stty=
852 		 * and leaving out the -r option.
853 		 */
854 		if (request->request->actions & ACT_RAW) {
855 			STRLCAT (argbuf, sep, sizeof (argbuf));
856 			sep = " ";
857 			STRLCAT (argbuf, "stty=-opost", sizeof (argbuf));
858 		}
859 
860 
861 		/* the "options" */
862 		av[ac++] = arg_string(UNTRUSTED, "%s", argbuf);
863 
864 		for (listp = file_list; *listp; listp++)
865 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
866 
867 		(void)chfiles (file_list, procuid, procgid);
868 
869 		break;
870 
871 
872 	case EX_SLOWF:
873 		if (request->slow)
874 			addenv(&envp, "FILTER", request->slow);
875 
876 		if (strchr (request->secure->user, '!'))
877 		{
878 			procuid = Lp_Uid;
879 			procgid = Lp_Gid;
880 		}
881 		else
882 		{
883 			procuid = request->secure->uid;
884 			procgid = request->secure->gid;
885 		}
886 		cp = _alloc_files(
887 			lenlist(request->request->file_list),
888 			getreqno(request->secure->req_id),
889 			procuid,
890 			procgid,
891 			(request->secure && request->secure->system ?
892 				request->secure->system : NULL )
893 		);
894 
895 		av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter);
896 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_Tmp,
897 				(request->secure && request->secure->system ?
898 					request->secure->system : Local_System),
899 				cp);
900 		for (listp = request->request->file_list; *listp; listp++)
901 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
902 
903 		(void)chfiles (request->request->file_list, procuid, procgid);
904 
905 #ifdef LP_USE_PAPI_ATTR
906 		/*
907 		 * Check if the PAPI job attribute file exists, if it does
908 		 * pass the file's pathname to the slow-filters in an
909 		 * environment variable. Note: this file is created when
910 		 * print jobs are submitted via the PAPI interface.
911 		 */
912 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
913 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
914 		path = makepath(SPOOLDIR, "temp", tmpName, (char *)0);
915 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
916 		{
917 			/*
918 			 * IPP job attribute file exists for this job so
919 			 * set the environment variable
920 			 */
921 			syslog(LOG_DEBUG, "exec(): ATTRPATH='%s'", path);
922 			addenv(&envp, "ATTRPATH", path);
923 		}
924 		Free(path);
925 
926 
927 		/*
928 		 * now set environment variable for the printer's PostScript
929 		 * Printer Description (PPD) file, this is used by the filter
930 		 * when forming the print data for this printer.
931 		 */
932 		if ((request->printer != NULL) &&
933 		    (request->printer->printer != NULL) &&
934 		    (request->printer->printer->name != NULL))
935 		{
936 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
937 				request->printer->printer->name);
938 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
939 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
940 			{
941 				syslog(LOG_DEBUG,
942 					"exec(): Printer PPD='%s'", path);
943 				addenv(&envp, "PPD", path);
944 			}
945 			Free(path);
946 		}
947 #endif
948 		break;
949 
950 	case EX_ALERT:
951 		procuid = Lp_Uid;
952 		procgid = Lp_Gid;
953 		(void)Chown (printer->alert->msgfile, procuid, procgid);
954 
955 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
956 				printer->printer->name, ALERTSHFILE);
957 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
958 
959 		break;
960 
961 	case EX_PALERT:
962 		procuid = Lp_Uid;
963 		procgid = Lp_Gid;
964 		(void)Chown (pwheel->alert->msgfile, procuid, procgid);
965 
966 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels,
967 				pwheel->pwheel->name, ALERTSHFILE);
968 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
969 
970 		break;
971 
972 	case EX_FALERT:
973 		procuid = Lp_Uid;
974 		procgid = Lp_Gid;
975 		(void)Chown (form->alert->msgfile, procuid, procgid);
976 
977 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
978 				form->form->name, ALERTSHFILE);
979 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
980 
981 		break;
982 
983 	case EX_FORM_MESSAGE:
984 		procuid = Lp_Uid;
985 		procgid = Lp_Gid;
986 
987 		av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults);
988 		av[ac++] = arg_string(TRUSTED, "%s", form->form->name);
989 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
990 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
991 				form->form->name, FORMMESSAGEFILE);
992 
993 		break;
994 
995 	case EX_FAULT_MESSAGE:
996 		procuid = Lp_Uid;
997 		procgid = Lp_Gid;
998 
999 		av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults);
1000 		av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow);
1001 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
1002 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
1003 				printerName, FAULTMESSAGEFILE);
1004 
1005 		break;
1006 
1007 	case EX_NOTIFY:
1008 		if (request->request->alert) {
1009 			if (strchr(request->secure->user, '!')) {
1010 				procuid = Lp_Uid;
1011 				procgid = Lp_Gid;
1012 			} else {
1013 				procuid = request->secure->uid;
1014 				procgid = request->secure->gid;
1015 			}
1016 			av[ac++] = arg_string(TRUSTED, "%s",
1017 					request->request->alert);
1018 		} else {
1019 			char *user = strdup(request->secure->user);
1020 			procuid = Lp_Uid;
1021 			procgid = Lp_Gid;
1022 			clean_string(user);
1023 
1024 			if ((request->request->actions & ACT_WRITE) &&
1025 			    (!request->secure->system ||
1026 			    STREQU(request->secure->system, Local_System))) {
1027 				av[ac++] = arg_string(TRUSTED, "%s", BINWRITE);
1028 				snprintf(argbuf, sizeof (argbuf),
1029 					"%s %s || %s %s",
1030 					BINWRITE, user,
1031 					BINMAIL, user
1032 				);
1033 				av[ac++] = arg_string(TRUSTED, "/bin/sh");
1034 				av[ac++] = arg_string(TRUSTED, "-c");
1035 				av[ac++] = arg_string(TRUSTED, "%s", argbuf);
1036 			} else {
1037 				av[ac++] = arg_string(TRUSTED, "%s", BINMAIL);
1038 				av[ac++] = arg_string(UNTRUSTED, "%s", user);
1039 			}
1040 
1041 			free(user);
1042 		}
1043 		break;
1044 	}
1045 
1046 	av[ac++] = NULL;
1047 
1048 	Fork2 ();
1049 	/* only the child returns */
1050 
1051 	/*
1052 	 * Correctly set up the supplemental group list
1053 	 * for proper file access (before execl the interface program)
1054 	 */
1055 
1056 	pwp = getpwuid(procuid);
1057 	if (pwp == NULL) {
1058 		note("getpwuid(%d) call failed\n", procuid);
1059 	} else if (initgroups(pwp->pw_name, procgid) < 0) {
1060 		note("initgroups() call failed %d\n", errno);
1061 	}
1062 
1063 	setgid (procgid);
1064 	setuid (procuid);
1065 
1066 	/*
1067 	 * The shell doesn't allow the "trap" builtin to set a trap
1068 	 * for a signal ignored when the shell is started. Thus, don't
1069 	 * turn off signals in the last child!
1070 	 */
1071 
1072 	for (i = 0; av[i] != NULL; i++)
1073 		syslog(LOG_DEBUG, "exec: av[%d] = %s", i, av[i]);
1074 	for (i = 0; av[i] != NULL; i++)
1075 		syslog(LOG_DEBUG, "exec: envp[%d] = %s", i, envp[i]);
1076 
1077 	execvpe(av[0], av, envp);
1078 	Done (EXEC_EXIT_NEXEC, errno);
1079 	/*NOTREACHED*/
1080 }
1081 
1082 /**
1083  ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT
1084  **/
1085 
1086 static void
1087 addenv(char ***envp, char *name, char *value)
1088 {
1089 	register char *		cp;
1090 
1091 	if ((name == NULL) || (value == NULL))
1092 		return;
1093 
1094 	if ((cp = makestr(name, "=", value, (char *)0)))
1095 		addlist(envp, cp);
1096 	return;
1097 }
1098 
1099 /**
1100  ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT
1101  **/
1102 
1103 static int
1104 Fork1(EXEC *ep)
1105 {
1106 	int			pid;
1107 	int			fds[2];
1108 
1109 	if (pipe(fds) == -1) {
1110 		note("Failed to create pipe for child process (%s).\n", PERROR);
1111 		errno = EAGAIN ;
1112 		return(-1);
1113 	}
1114 
1115 	ep->md = mconnect((char *)0, fds[0], fds[1]);
1116 
1117 	switch (pid = fork()) {
1118 
1119 	case -1:
1120 		mdisconnect(ep->md);
1121 		close(fds[0]);
1122 		close(fds[1]);
1123 		ep->md = 0;
1124 		return (-1);
1125 
1126 	case 0:
1127 		ChildMd = mconnect(NULL, fds[1], fds[1]);
1128 		return (0);
1129 
1130 	default:
1131 		mlistenadd(ep->md, POLLIN);
1132 		return (pid);
1133 	}
1134 }
1135 
1136 /**
1137  ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT
1138  **/
1139 
1140 static void
1141 Fork2(void)
1142 {
1143 	switch ((ChildPid = fork())) {
1144 
1145 	case -1:
1146 		Done (EXEC_EXIT_NFORK, errno);
1147 		/*NOTREACHED*/
1148 
1149 	case 0:
1150 		return;
1151 
1152 	default:
1153 		/*
1154 		 * Delay calling "ignore_fault_signals()" as long
1155 		 * as possible, to give the child a chance to exec
1156 		 * the interface program and turn on traps.
1157 		 */
1158 
1159 		cool_heels ();
1160 		/*NOTREACHED*/
1161 
1162 	}
1163 }
1164 
1165 
1166 /**
1167  ** cool_heels() - WAIT FOR CHILD TO "DIE"
1168  **/
1169 
1170 static void
1171 cool_heels(void)
1172 {
1173 	int			status;
1174 
1175 	/*
1176 	 * At this point our only job is to wait for the child process.
1177 	 * If we hang out for a bit longer, that's okay.
1178 	 * By delaying before turning off the fault signals,
1179 	 * we increase the chance that the child process has completed
1180 	 * its exec and has turned on the fault traps. Nonetheless,
1181 	 * we can't guarantee a zero chance of missing a fault.
1182 	 * (We don't want to keep trapping the signals because the
1183 	 * interface program is likely to have a better way to handle
1184 	 * them; this process provides only rudimentary handling.)
1185 	 *
1186 	 * Note that on a very busy system, or with a very fast interface
1187 	 * program, the tables could be turned: Our sleep below (coupled
1188 	 * with a delay in the kernel scheduling us) may cause us to
1189 	 * detect the fault instead of the interface program.
1190 	 *
1191 	 * What we need is a way to synchronize with the child process.
1192 	 */
1193 	sleep (1);
1194 	ignore_fault_signals ();
1195 
1196 	WaitedChildPid = 0;
1197 	while ((WaitedChildPid = wait(&status)) != ChildPid)
1198 		;
1199 
1200 	if (
1201 		EXITED(status) > EXEC_EXIT_USER
1202 	     && EXITED(status) != EXEC_EXIT_FAULT
1203 	)
1204 		Done (EXEC_EXIT_EXIT, EXITED(status));
1205 
1206 	done (status, 0);	/* Don't use Done() */
1207 	/*NOTREACHED*/
1208 }
1209 
1210 
1211 /**
1212  ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT
1213  ** ignore_fault_signals() - IGNORE SAME
1214  **/
1215 
1216 static void
1217 trap_fault_signals(void)
1218 {
1219 	signal (SIGHUP, sigtrap);
1220 	signal (SIGINT, sigtrap);
1221 	signal (SIGQUIT, sigtrap);
1222 	signal (SIGPIPE, sigtrap);
1223 	return;
1224 }
1225 
1226 static void
1227 ignore_fault_signals(void)
1228 {
1229 	signal (SIGHUP, SIG_IGN);
1230 	signal (SIGINT, SIG_IGN);
1231 	signal (SIGQUIT, SIG_IGN);
1232 	signal (SIGPIPE, SIG_IGN);
1233 	return;
1234 }
1235 
1236 /**
1237  ** sigtrap() - TRAP VARIOUS SIGNALS
1238  **/
1239 
1240 static void
1241 sigtrap(int sig)
1242 {
1243 	signal (sig, SIG_IGN);
1244 	switch (sig) {
1245 
1246 	case SIGHUP:
1247 		Done (EXEC_EXIT_HUP, 0);
1248 		/*NOTREACHED*/
1249 
1250 	case SIGQUIT:
1251 	case SIGINT:
1252 		Done (EXEC_EXIT_INTR, 0);
1253 		/*NOTREACHED*/
1254 
1255 	case SIGPIPE:
1256 		Done (EXEC_EXIT_PIPE, 0);
1257 		/*NOTREACHED*/
1258 
1259 	case SIGTERM:
1260 		/*
1261 		 * If we were killed with SIGTERM, it should have been
1262 		 * via the Spooler who should have killed the entire
1263 		 * process group. We have to wait for the children,
1264 		 * since we're their parent, but WE MAY HAVE WAITED
1265 		 * FOR THEM ALREADY (in cool_heels()).
1266 		 */
1267 		if (ChildPid != WaitedChildPid) {
1268 			register int		cpid;
1269 
1270 			while (
1271 				(cpid = wait((int *)0)) != ChildPid
1272 			     && (cpid != -1 || errno != ECHILD)
1273 			)
1274 				;
1275 		}
1276 
1277 		/*
1278 		 * We can't rely on getting SIGTERM back in the wait()
1279 		 * above, because, for instance, some shells trap SIGTERM
1280 		 * and exit instead. Thus we force it.
1281 		 */
1282 		done (SIGTERM, 0);	/* Don't use Done() */
1283 		/*NOTREACHED*/
1284 	}
1285 }
1286 
1287 /**
1288  ** done() - TELL SPOOLER THIS CHILD IS DONE
1289  **/
1290 
1291 static void
1292 done(int status, int err)
1293 {
1294 	if (do_undial)
1295 		undial (1);
1296 
1297 	mputm (ChildMd, S_CHILD_DONE, key, slot, status, err);
1298 	mdisconnect (ChildMd);
1299 
1300 	exit (0);
1301 	/*NOTREACHED*/
1302 }
1303 
1304 /**
1305  ** child_mallocfail()
1306  **/
1307 
1308 static void
1309 child_mallocfail(void)
1310 {
1311 	Done (EXEC_EXIT_NOMEM, ENOMEM);
1312 }
1313