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