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