xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/exec.c (revision 48bbca81)
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  * Copyright (c) 2016 by Delphix. All rights reserved.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #include <pwd.h>
31 #include <zone.h>
32 #if defined PS_FAULTED
33 #undef  PS_FAULTED
34 #endif /* PS_FAULTED */
35 #include <dial.h>
36 
37 #include <stdlib.h>
38 #include "limits.h"
39 #include "stdarg.h"
40 #include "wait.h"
41 #include "dial.h"
42 #include "lpsched.h"
43 #include <syslog.h>
44 #include "tsol/label.h"
45 
46 #define Done(EC,ERRNO)	done(((EC) << 8),ERRNO)
47 
48 #define	STRLCAT(dst, src, size) \
49 	if (strlcat((dst), (src), (size)) >= (size)) { \
50 		errno = EINVAL; \
51 		return (-1); \
52 	}
53 
54 static MESG *		ChildMd;
55 
56 static int		ChildPid;
57 static int		WaitedChildPid;
58 static int		do_undial;
59 
60 static char		argbuf[ARG_MAX];
61 
62 static long		key;
63 
64 static void		sigtrap ( int );
65 static void		done ( int , int );
66 static void		cool_heels ( void );
67 static void		addenv (char ***envp, char * , char * );
68 static void		trap_fault_signals ( void );
69 static void		ignore_fault_signals ( void );
70 static void		child_mallocfail ( void );
71 static void		Fork2 ( void );
72 
73 static int		Fork1 ( EXEC * );
74 
75 static void
76 relock(void)
77 {
78 	struct flock		l;
79 
80 	l.l_type = F_WRLCK;
81 	l.l_whence = 1;
82 	l.l_start = 0;
83 	l.l_len = 0;
84 	(void)Fcntl (lock_fd, F_SETLK, &l);
85 	return;
86 }
87 
88 static char *_exec_name(int type)
89 {
90 	static char *_names[] = {
91 	"", "EX_INTERF", "EX_SLOWF", "EX_ALERT", "EX_FALERT", "EX_PALERT",
92 	"EX_NOTIFY", "EX_FAULT_MESSAGE", "EX_FORM_MESSAGE", NULL };
93 
94 	if ((type < 0) || (type > EX_FORM_MESSAGE))
95 		return ("BAD_EXEC_TYPE");
96 	else
97 		return (_names[type]);
98 }
99 
100 /*
101  * This function replaces characters in a string that might be used
102  * to exploit a security hole.  Replace command seperators (`, &, ;, |, ^),
103  * output redirection (>, |), variable expansion ($), and character
104  * escape (\).
105  *
106  * Bugid 4141687
107  * Add ( ) < * ? [
108  * Bugid 4139071
109  * Remove \
110  */
111 void clean_string(char *ptr)
112 {
113 	char *cp;
114 	wchar_t wc;
115 	size_t len;
116 
117 	for (cp = ptr; *cp != NULL; ) {
118 		if ((len = mbtowc(&wc, cp, MB_CUR_MAX)) == -1) {
119 			cp++;
120 			continue;
121 		}
122 
123 		if (len == 1 &&
124 		    ((wc == L'`') || (wc == L'&') || (wc == L';') ||
125 		    (wc == L'|') || (wc == L'>') || (wc == L'^') ||
126 		    (wc == L'$') || (wc == L'(') || (wc == L')') ||
127 		    (wc == L'<') || (wc == L'*') || (wc == L'?') ||
128 		    (wc == L'[')))
129 			*cp = '_';
130 		cp += len;
131 	}
132 }
133 
134 enum trust {TRUSTED, UNTRUSTED};
135 
136 static char *arg_string(enum trust type, char *fmt, ...) __PRINTFLIKE(2);
137 
138 /* PRINTFLIKE2 */
139 static char *
140 arg_string(enum trust type, char *fmt, ...)
141 {
142 	char buf[BUFSIZ];
143 	va_list	args;
144 
145 	va_start(args, fmt);
146 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
147 	va_end(args);
148 
149 	/*
150 	 * If the string contains data from an untrusted origin (user supplied),
151 	 * clean it up in case one of our progeny is a shell script and isn't
152 	 * careful about checking its input.
153 	 */
154 	if (type == UNTRUSTED)
155 		clean_string(buf);
156 
157 	return (strdup(buf));
158 }
159 
160 /* stolen from libc/gen/port/gen/execvp.c */
161 static const char *
162 execat(const char *s1, const char *s2, char *si)
163 {
164         char    *s;
165         int cnt = PATH_MAX + 1; /* number of characters in s2 */
166 
167         s = si;
168         while (*s1 && *s1 != ':') {
169                 if (cnt > 0) {
170                         *s++ = *s1++;
171                         cnt--;
172                 } else
173                         s1++;
174         }
175         if (si != s && cnt > 0) {
176                 *s++ = '/';
177                 cnt--;
178         }
179         while (*s2 && cnt > 0) {
180                 *s++ = *s2++;
181                 cnt--;
182         }
183         *s = '\0';
184         return (*s1 ? ++s1: 0);
185 }
186 
187 /*
188  * Similiar to execvp(), execpt you can supply an environment and we always
189  * use /bin/sh for shell scripts.  The PATH searched is the PATH in the
190  * current environment, not the environment in the argument list.
191  * This was pretty much stolen from libc/gen/port/execvp.c
192  */
193 static int
194 execvpe(char *name, char *const argv[], char *const envp[])
195 {
196 	char *path;
197 	char fname[PATH_MAX+2];
198 	char *newargs[256];
199 	int i;
200 	const char *cp;
201 	unsigned etxtbsy = 1;
202         int eacces = 0;
203 
204 	if (*name == '\0') {
205 		errno = ENOENT;
206 		return (-1);
207 	}
208 
209 	if ((path = getenv("PATH")) == NULL)
210 		path = "/usr/bin:/bin";
211 
212         cp = strchr(name, '/')? (const char *)"": path;
213 
214         do {
215                 cp = execat(cp, name, fname);
216         retry:
217                 /*
218                  * 4025035 and 4038378
219                  * if a filename begins with a "-" prepend "./" so that
220                  * the shell can't interpret it as an option
221                  */
222                 if (*fname == '-') {
223                         size_t size = strlen(fname) + 1;
224                         if ((size + 2) > sizeof (fname)) {
225                                 errno = E2BIG;
226                                 return (-1);
227                         }
228                         (void) memmove(fname + 2, fname, size);
229                         fname[0] = '.';
230                         fname[1] = '/';
231                 }
232                 (void) execve(fname, argv, envp);
233                 switch (errno) {
234                 case ENOEXEC:
235                         newargs[0] = "sh";
236                         newargs[1] = fname;
237                         for (i = 1; (newargs[i + 1] = argv[i]) != NULL; ++i) {
238                                 if (i >= 254) {
239                                         errno = E2BIG;
240                                         return (-1);
241                                 }
242                         }
243                         (void) execve("/bin/sh", newargs, envp);
244                         return (-1);
245                 case ETXTBSY:
246                         if (++etxtbsy > 5)
247                                 return (-1);
248                         (void) sleep(etxtbsy);
249                         goto retry;
250                 case EACCES:
251                         ++eacces;
252                         break;
253                 case ENOMEM:
254                 case E2BIG:
255                 case EFAULT:
256                         return (-1);
257                 }
258         } while (cp);
259         if (eacces)
260                 errno = EACCES;
261         return (-1);
262 }
263 
264 static char time_buf[50];
265 
266 /**
267  ** exec() - FORK AND EXEC CHILD PROCESS
268  **/
269 
270 /*VARARGS1*/
271 int
272 exec(int type, ...)
273 {
274 	va_list			args;
275 
276 	int			i;
277 	int			procuid;
278 	int			procgid;
279 	int			ret;
280 	int			fr_flg;
281 
282 	char			*cp;
283 	char			*infile;
284 	char			*outfile;
285 	char			*errfile;
286 	char			*sep;
287 
288 	char			**listp;
289 	char			**file_list;
290 	char			*printerName;
291 	char			*printerNameToShow;
292 	static char		nameBuf[100];
293 	char			*clean_title;
294 
295 	PSTATUS			*printer;
296 
297 	RSTATUS			*request;
298 
299 	FSTATUS			*form;
300 
301 	EXEC			*ep;
302 
303 	PWSTATUS		*pwheel;
304 	time_t			now;
305 	struct passwd		*pwp;
306 #ifdef LP_USE_PAPI_ATTR
307 	struct stat		tmpBuf;
308 	char 			tmpName[BUFSIZ];
309 	char			*path = NULL;
310 #endif
311 	char *av[ARG_MAX];
312 	char **envp = NULL;
313 	int ac = 0;
314 	char	*mail_zonename = NULL;
315 	char	*slabel = NULL;
316 	int	setid = 1;
317 	char	*ridno = NULL, *tmprid = 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 		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_Temp,
599 				"/F",
600 				getreqno(request->secure->req_id),
601 				"-",
602 				(char *)0
603 			);
604 
605 			file_list = (char **)Malloc(
606 				(lenlist(request->request->file_list) + 1)
607 			      * sizeof(char *)
608 			);
609 
610 			for (
611 				listp = request->request->file_list;
612 				*listp;
613 				listp++
614 			) {
615 				sprintf (num, "%d", count + 1);
616 				file_list[count] = makestr(
617 					prefix,
618 					num,
619 					(char *)0
620 				);
621 				count++;
622 			}
623 			file_list[count] = 0;
624 		}
625 
626 #ifdef LP_USE_PAPI_ATTR
627 		/*
628 		 * Check if the PAPI job attribute file exists, if it does
629 		 * pass the file's pathname to the printer interface script
630 		 * in an environment variable. This file is created when
631 		 * print jobs are submitted via the PAPI interface.
632 		 */
633 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
634 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
635 		path = makepath(Lp_Temp, tmpName, (char *)0);
636 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
637 		{
638 			/*
639 			 * IPP job attribute file exists for this job so
640 			 * set the environment variable
641 			 */
642 			addenv(&envp, "ATTRPATH", path);
643 		}
644 		Free(path);
645 
646 		/*
647 		 * now set environment variable for the printer's PostScript
648 		 * Printer Description (PPD) file, this is used by the filter
649 		 * when forming the print data for this printer.
650 		 */
651 		if ((request->printer != NULL) &&
652 		    (request->printer->printer != NULL) &&
653 		    (request->printer->printer->name != NULL))
654 		{
655 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
656 				request->printer->printer->name);
657 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
658 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
659 			{
660 				addenv(&envp, "PPD", path);
661 			}
662 			Free(path);
663 		}
664 #endif
665 
666 		if (request->printer_type)
667 			addenv(&envp, "TERM", request->printer_type);
668 
669 		if (!(printer->printer->daisy)) {
670 			register char *	chset = 0;
671 			register char *	csp;
672 
673 			if (
674 				request->form
675 			     && request->form->form->chset
676 			     && request->form->form->mandatory
677 			     && !STREQU(NAME_ANY, request->form->form->chset)
678 			)
679 				chset = request->form->form->chset;
680 
681 			else if (
682 				request->request->charset
683 			     && !STREQU(NAME_ANY, request->request->charset)
684 			)
685 				chset = request->request->charset;
686 
687 			if (chset) {
688 				csp = search_cslist(
689 					chset,
690 					printer->printer->char_sets
691 				);
692 
693 				/*
694 				 * The "strtok()" below wrecks the string
695 				 * for future use, but this is a child
696 				 * process where it won't be needed again.
697 				 */
698 				addenv (&envp, "CHARSET",
699 					(csp? strtok(csp, "=") : chset)
700 				);
701 			}
702 		}
703 
704 		if (request->fast)
705 			addenv(&envp, "FILTER", request->fast);
706 
707 		/*
708 		 * Add the sensitivity label to the environment for
709 		 * banner page and header/footer processing
710 		 */
711 
712 		if (is_system_labeled() && request->secure->slabel != NULL)
713 			addenv(&envp, "SLABEL", request->secure->slabel);
714 
715 		/*
716 		 * Add the system name to the user name (ala system!user)
717 		 * unless it is already there. RFS users may have trouble
718 		 * here, sorry!
719 		 */
720 		cp = strchr(request->secure->user, '@');
721 
722 		allTraysWithForm(printer, request->form);
723 
724 		/*
725 		 * Fix for 4137389
726 		 * Remove double quotes from title string.
727 		 */
728 		fr_flg = 1;
729 		clean_title = strdup(NB(request->request->title));
730 		if (clean_title == NULL) {
731 			/*
732 			 * strdup failed. We're probably hosed
733 			 * but try setting clean_title
734 			 * to original title and continuing.
735 			 */
736 			clean_title = NB(request->request->title);
737 			fr_flg = 0;
738 		} else if (strcmp(clean_title, "") != 0) {
739 			char *ct_p;
740 
741 			for (ct_p = clean_title; *ct_p != NULL; ct_p++) {
742 				if (*ct_p == '"')
743 					*ct_p = ' ';
744 			}
745 		}
746 
747 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces,
748 					printer->printer->name);
749 		/*
750 		 * Read the options field of the request
751 		 * In case of remote lpd request
752 		 * the options field will have
753 		 * job-id-requested. This is the
754 		 * id sent by the client
755 		 */
756 		if (request->request->options != NULL) {
757 			char *options = NULL, *temp = NULL;
758 			options = temp = strdup(request->request->options);
759 
760 			/*
761 			 * Search for job-id-requested in
762 			 * options string
763 			 */
764 			options = strstr(options, "job-id-requested");
765 			if (options != NULL) {
766 				/*
767 				 * Extract the ridno from the string
768 				 * job-id-requested=xxx
769 				 * In this case ridno = xxx
770 				 */
771 				if (STRNEQU(options, "job-id-requested=", 17)) {
772 					ridno = strdup(options + 17);
773 					tmprid = strstr(ridno, " ");
774 					if (ridno != NULL) {
775 						/*
776 						 * Read job-id-requested
777 						 * successfully
778 						 */
779 						tmprid = strstr(ridno, " ");
780 						if (tmprid != NULL)
781 							*tmprid = '\0';
782 
783 						setid = 0;
784 					} else
785 						/*
786 						 * could not read
787 						 * ridno from the string
788 						 * job-id-requested=xxx
789 						 */
790 						setid = 1;
791 				} else
792 					/*
793 					 * could not read
794 					 * ridno from the string
795 					 * job-id-requested=xxx
796 					 */
797 					setid = 1;
798 			} else
799 				/*
800 				 * No job-id-requested in
801 				 * request options
802 				 */
803 				setid = 1;
804 
805 			if (temp != NULL)
806 				free(temp);
807 
808 		} else
809 			/*
810 			 * options field in request structure
811 			 * not set
812 			 */
813 			setid = 1;
814 
815 
816 		/*
817 		 * setid = 1 means the job-id-requested attribute
818 		 * is not set so read the request->secure->req_id
819 		 */
820 		if (setid)
821 			av[ac++] = arg_string(TRUSTED, "%s",
822 			    request->secure->req_id);
823 		else {
824 			/*
825 			 * From request->secure->req_id extract the
826 			 * printer-name.
827 			 * request->secure->req_id = <printer-name>-<req_id>
828 			 * The final req-id will be
829 			 * <printer-name>-<ridno>
830 			 */
831 			char *r1 = NULL, *r2 = NULL, *tmp = NULL;
832 			r1 = r2 = tmp = strdup(request->secure->req_id);
833 			r2 = strrchr(r1, '-');
834 			if (r2 != NULL) {
835 				char *r3 = NULL;
836 				int lr1 = strlen(r1);
837 				int lr2 = strlen(r2);
838 				r1[lr1 - lr2 + 1] = '\0';
839 
840 				/*
841 				 * Now r1 = <printer-name>-
842 				 */
843 				lr1 = strlen(r1);
844 				lr2 = strlen(ridno);
845 
846 				r3 = (char *)malloc(lr1+lr2+1);
847 				if (r3 != NULL) {
848 					strcpy(r3, r1);
849 					strcat(r3, ridno);
850 					/*
851 					 * Here r3 = <printer-name>-<ridno>
852 					 */
853 					av[ac++] = arg_string(TRUSTED,
854 					    "%s", r3);
855 					free(r3);
856 				} else
857 					av[ac++] = arg_string(TRUSTED, "%s",
858 					    request->secure->req_id);
859 
860 			} else
861 				av[ac++] = arg_string(TRUSTED, "%s",
862 				    request->secure->req_id);
863 
864 			if (tmp != NULL)
865 				free(tmp);
866 
867 			if (ridno != NULL)
868 				free(ridno);
869 		}
870 
871 		av[ac++] = arg_string(UNTRUSTED, "%s", request->request->user);
872 		av[ac++] = arg_string(TRUSTED, "%s", clean_title);
873 		av[ac++] = arg_string(TRUSTED, "%d", request->copies);
874 
875 		if (fr_flg)
876 			free (clean_title);
877 
878 		sep = "";
879 
880 		/*
881 		 * Do the administrator defined key=value pair options
882 		 */
883 
884 		argbuf[0] = '\0';
885 
886 		if (printer->printer->options) {
887 			char **tmp = printer->printer->options;
888 			while(*tmp != NULL) {
889 				STRLCAT(argbuf, sep, sizeof (argbuf));
890 				sep = " ";
891 				STRLCAT(argbuf, *tmp++, sizeof (argbuf));
892 			}
893 		}
894 
895 		/*
896 		 * Do the administrator defined ``stty'' stuff before
897 		 * the user's -o options, to allow the user to override.
898 		 */
899 		if (printer->printer->stty) {
900 			STRLCAT (argbuf, sep, sizeof (argbuf));
901 			sep = " ";
902 			STRLCAT (argbuf, "stty='", sizeof (argbuf));
903 			STRLCAT (argbuf, printer->printer->stty,
904 			    sizeof (argbuf));
905 			STRLCAT (argbuf, "'", sizeof (argbuf));
906 		}
907 
908 		/*
909 		 * Do all of the user's options except the cpi/lpi/etc.
910 		 * stuff, which is done separately.
911 		 */
912 		if (request->request->options) {
913 			listp = dashos(request->request->options);
914 			while (*listp) {
915 				if (
916 					!STRNEQU(*listp, "cpi=", 4)
917 				     && !STRNEQU(*listp, "lpi=", 4)
918 				     && !STRNEQU(*listp, "width=", 6)
919 				     && !STRNEQU(*listp, "length=", 7)
920 				) {
921 					STRLCAT (argbuf, sep, sizeof (argbuf));
922 					sep = " ";
923 					STRLCAT (argbuf, *listp,
924 					    sizeof (argbuf));
925 				}
926 				listp++;
927 			}
928 		}
929 
930 		/*
931 		 * The "pickfilter()" routine (from "validate()")
932 		 * stored the cpi/lpi/etc. stuff that should be
933 		 * used for this request. It chose form over user,
934 		 * and user over printer.
935 		 */
936 		if (request->cpi) {
937 			STRLCAT (argbuf, sep, sizeof (argbuf));
938 			sep = " ";
939 			STRLCAT (argbuf, "cpi=", sizeof (argbuf));
940 			STRLCAT (argbuf, request->cpi, sizeof (argbuf));
941 		}
942 		if (request->lpi) {
943 			STRLCAT (argbuf, sep, sizeof (argbuf));
944 			sep = " ";
945 			STRLCAT (argbuf, "lpi=", sizeof (argbuf));
946 			STRLCAT (argbuf, request->lpi, sizeof (argbuf));
947 		}
948 		if (request->pwid) {
949 			STRLCAT (argbuf, sep, sizeof (argbuf));
950 			sep = " ";
951 			STRLCAT (argbuf, "width=", sizeof (argbuf));
952 			STRLCAT (argbuf, request->pwid, sizeof (argbuf));
953 		}
954 		if (request->plen) {
955 			STRLCAT (argbuf, sep, sizeof (argbuf));
956 			sep = " ";
957 			STRLCAT (argbuf, "length=", sizeof (argbuf));
958 			STRLCAT (argbuf, request->plen, sizeof (argbuf));
959 		}
960 
961 		/*
962 		 * Do the ``raw'' bit last, to ensure it gets
963 		 * done. If the user doesn't want this, then they
964 		 * can do the correct thing using -o stty=
965 		 * and leaving out the -r option.
966 		 */
967 		if (request->request->actions & ACT_RAW) {
968 			STRLCAT (argbuf, sep, sizeof (argbuf));
969 			sep = " ";
970 			STRLCAT (argbuf, "stty=-opost", sizeof (argbuf));
971 		}
972 
973 
974 		/* the "options" */
975 		av[ac++] = arg_string(UNTRUSTED, "%s", argbuf);
976 
977 		for (listp = file_list; *listp; listp++)
978 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
979 
980 		(void)chfiles (file_list, procuid, procgid);
981 
982 		break;
983 
984 
985 	case EX_SLOWF:
986 		if (request->slow)
987 			addenv(&envp, "FILTER", request->slow);
988 
989 		procuid = request->secure->uid;
990 		procgid = request->secure->gid;
991 
992 		cp = _alloc_files(
993 			lenlist(request->request->file_list),
994 			getreqno(request->secure->req_id),
995 			procuid, procgid);
996 
997 		av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter);
998 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_Temp, cp);
999 		for (listp = request->request->file_list; *listp; listp++)
1000 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
1001 
1002 		(void)chfiles (request->request->file_list, procuid, procgid);
1003 
1004 #ifdef LP_USE_PAPI_ATTR
1005 		/*
1006 		 * Check if the PAPI job attribute file exists, if it does
1007 		 * pass the file's pathname to the slow-filters in an
1008 		 * environment variable. Note: this file is created when
1009 		 * print jobs are submitted via the PAPI interface.
1010 		 */
1011 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
1012 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
1013 		path = makepath(Lp_Temp, tmpName, (char *)0);
1014 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
1015 		{
1016 			/*
1017 			 * IPP job attribute file exists for this job so
1018 			 * set the environment variable
1019 			 */
1020 			addenv(&envp, "ATTRPATH", path);
1021 		}
1022 		Free(path);
1023 
1024 
1025 		/*
1026 		 * now set environment variable for the printer's PostScript
1027 		 * Printer Description (PPD) file, this is used by the filter
1028 		 * when forming the print data for this printer.
1029 		 */
1030 		if ((request->printer != NULL) &&
1031 		    (request->printer->printer != NULL) &&
1032 		    (request->printer->printer->name != NULL))
1033 		{
1034 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
1035 				request->printer->printer->name);
1036 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
1037 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
1038 			{
1039 				addenv(&envp, "PPD", path);
1040 			}
1041 			Free(path);
1042 		}
1043 #endif
1044 		break;
1045 
1046 	case EX_ALERT:
1047 		procuid = Lp_Uid;
1048 		procgid = Lp_Gid;
1049 		(void)Chown (printer->alert->msgfile, procuid, procgid);
1050 
1051 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
1052 				printer->printer->name, ALERTSHFILE);
1053 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
1054 
1055 		break;
1056 
1057 	case EX_PALERT:
1058 		procuid = Lp_Uid;
1059 		procgid = Lp_Gid;
1060 		(void)Chown (pwheel->alert->msgfile, procuid, procgid);
1061 
1062 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels,
1063 				pwheel->pwheel->name, ALERTSHFILE);
1064 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
1065 
1066 		break;
1067 
1068 	case EX_FALERT:
1069 		procuid = Lp_Uid;
1070 		procgid = Lp_Gid;
1071 		(void)Chown (form->alert->msgfile, procuid, procgid);
1072 
1073 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
1074 				form->form->name, ALERTSHFILE);
1075 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
1076 
1077 		break;
1078 
1079 	case EX_FORM_MESSAGE:
1080 		procuid = Lp_Uid;
1081 		procgid = Lp_Gid;
1082 
1083 		av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults);
1084 		av[ac++] = arg_string(TRUSTED, "%s", form->form->name);
1085 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
1086 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
1087 				form->form->name, FORMMESSAGEFILE);
1088 
1089 		break;
1090 
1091 	case EX_FAULT_MESSAGE:
1092 		procuid = Lp_Uid;
1093 		procgid = Lp_Gid;
1094 
1095 		av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults);
1096 		av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow);
1097 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
1098 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
1099 				printerName, FAULTMESSAGEFILE);
1100 
1101 		break;
1102 
1103 	case EX_NOTIFY:
1104 		if (request->request->alert) {
1105 			procuid = request->secure->uid;
1106 			procgid = request->secure->gid;
1107 
1108 			av[ac++] = arg_string(TRUSTED, "%s",
1109 					request->request->alert);
1110 		} else {
1111 			char *user = strdup(request->request->user);
1112 			clean_string(user);
1113 			slabel = request->secure->slabel;
1114 
1115 			if (request->request->actions & ACT_WRITE) {
1116 				av[ac++] = arg_string(TRUSTED, "%s", BINWRITE);
1117 				snprintf(argbuf, sizeof (argbuf),
1118 					"%s %s || %s %s",
1119 					BINWRITE, user,
1120 					BINMAIL, user
1121 				);
1122 				av[ac++] = arg_string(TRUSTED, "/bin/sh");
1123 				av[ac++] = arg_string(TRUSTED, "-c");
1124 				av[ac++] = arg_string(TRUSTED, "%s", argbuf);
1125 			} else if ((getzoneid() == GLOBAL_ZONEID) &&
1126 				   is_system_labeled() && (slabel != NULL)) {
1127 				/*
1128 				 * If in the global zone and the system is
1129 				 * labeled, mail is handled via a local
1130 				 * labeled zone that is the same label as
1131 				 * the request.
1132 				 */
1133 				if ((mail_zonename =
1134 				    get_labeled_zonename(slabel)) ==
1135 				    (char *)-1) {
1136 					/*
1137 					 * Cannot find labeled zone, just
1138 					 * return 0.
1139 					 */
1140 					return(0);
1141 				}
1142 			}
1143 			if (mail_zonename == NULL) {
1144 				procuid = Lp_Uid;
1145 				procgid = Lp_Gid;
1146 				av[ac++] = arg_string(TRUSTED, "%s", BINMAIL);
1147 				av[ac++] = arg_string(UNTRUSTED, "%s", user);
1148 			} else {
1149 				procuid = getuid();
1150 				procgid = getgid();
1151 				av[ac++] = arg_string(TRUSTED, "%s",
1152 				    "/usr/sbin/zlogin");
1153 				av[ac++] = arg_string(TRUSTED, "%s",
1154 				    mail_zonename);
1155 				av[ac++] = arg_string(TRUSTED, "%s",
1156 				    BINMAIL);
1157 				av[ac++] = arg_string(UNTRUSTED, "%s",
1158 				    user);
1159 				Free(mail_zonename);
1160 			}
1161 
1162 			free(user);
1163 		}
1164 		break;
1165 	}
1166 
1167 	av[ac++] = NULL;
1168 
1169 	Fork2 ();
1170 	/* only the child returns */
1171 
1172 	/*
1173 	 * Correctly set up the supplemental group list
1174 	 * for proper file access (before execl the interface program)
1175 	 */
1176 
1177 	pwp = getpwuid(procuid);
1178 	if (pwp == NULL) {
1179 		note("getpwuid(%d) call failed\n", procuid);
1180 	} else if (initgroups(pwp->pw_name, procgid) < 0) {
1181 		note("initgroups() call failed %d\n", errno);
1182 	}
1183 
1184 	setgid (procgid);
1185 	setuid (procuid);
1186 
1187 	/*
1188 	 * The shell doesn't allow the "trap" builtin to set a trap
1189 	 * for a signal ignored when the shell is started. Thus, don't
1190 	 * turn off signals in the last child!
1191 	 */
1192 
1193 #ifdef DEBUG
1194 	for (i = 0; av[i] != NULL; i++)
1195 		note("exec(%s): av[%d] = %s", _exec_name(type), i, av[i]);
1196 	for (i = 0; envp[i] != NULL; i++)
1197 		note("exec(%s): envp[%d] = %s", _exec_name(type), i, envp[i]);
1198 #endif
1199 
1200 	execvpe(av[0], av, envp);
1201 	Done (EXEC_EXIT_NEXEC, errno);
1202 	/*NOTREACHED*/
1203 	return (0);
1204 }
1205 
1206 /**
1207  ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT
1208  **/
1209 
1210 static void
1211 addenv(char ***envp, char *name, char *value)
1212 {
1213 	register char *		cp;
1214 
1215 	if ((name == NULL) || (value == NULL))
1216 		return;
1217 
1218 	if ((cp = makestr(name, "=", value, (char *)0)))
1219 		addlist(envp, cp);
1220 	return;
1221 }
1222 
1223 /**
1224  ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT
1225  **/
1226 
1227 static int
1228 Fork1(EXEC *ep)
1229 {
1230 	int			pid;
1231 	int			fds[2];
1232 
1233 	if (pipe(fds) == -1) {
1234 		note("Failed to create pipe for child process (%s).\n", PERROR);
1235 		errno = EAGAIN ;
1236 		return(-1);
1237 	}
1238 
1239 	ep->md = mconnect((char *)0, fds[0], fds[1]);
1240 
1241 	switch (pid = fork()) {
1242 
1243 	case -1:
1244 		mdisconnect(ep->md);
1245 		close(fds[0]);
1246 		close(fds[1]);
1247 		ep->md = 0;
1248 		return (-1);
1249 
1250 	case 0:
1251 		ChildMd = mconnect(NULL, fds[1], fds[1]);
1252 		return (0);
1253 
1254 	default:
1255 		mlistenadd(ep->md, POLLIN);
1256 		return (pid);
1257 	}
1258 }
1259 
1260 /**
1261  ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT
1262  **/
1263 
1264 static void
1265 Fork2(void)
1266 {
1267 	switch ((ChildPid = fork())) {
1268 
1269 	case -1:
1270 		Done (EXEC_EXIT_NFORK, errno);
1271 		/*NOTREACHED*/
1272 
1273 	case 0:
1274 		return;
1275 
1276 	default:
1277 		/*
1278 		 * Delay calling "ignore_fault_signals()" as long
1279 		 * as possible, to give the child a chance to exec
1280 		 * the interface program and turn on traps.
1281 		 */
1282 
1283 		cool_heels ();
1284 		/*NOTREACHED*/
1285 
1286 	}
1287 }
1288 
1289 
1290 /**
1291  ** cool_heels() - WAIT FOR CHILD TO "DIE"
1292  **/
1293 
1294 static void
1295 cool_heels(void)
1296 {
1297 	int			status;
1298 
1299 	/*
1300 	 * At this point our only job is to wait for the child process.
1301 	 * If we hang out for a bit longer, that's okay.
1302 	 * By delaying before turning off the fault signals,
1303 	 * we increase the chance that the child process has completed
1304 	 * its exec and has turned on the fault traps. Nonetheless,
1305 	 * we can't guarantee a zero chance of missing a fault.
1306 	 * (We don't want to keep trapping the signals because the
1307 	 * interface program is likely to have a better way to handle
1308 	 * them; this process provides only rudimentary handling.)
1309 	 *
1310 	 * Note that on a very busy system, or with a very fast interface
1311 	 * program, the tables could be turned: Our sleep below (coupled
1312 	 * with a delay in the kernel scheduling us) may cause us to
1313 	 * detect the fault instead of the interface program.
1314 	 *
1315 	 * What we need is a way to synchronize with the child process.
1316 	 */
1317 	sleep (1);
1318 	ignore_fault_signals ();
1319 
1320 	WaitedChildPid = 0;
1321 	while ((WaitedChildPid = wait(&status)) != ChildPid)
1322 		;
1323 
1324 	if (
1325 		EXITED(status) > EXEC_EXIT_USER
1326 	     && EXITED(status) != EXEC_EXIT_FAULT
1327 	)
1328 		Done (EXEC_EXIT_EXIT, EXITED(status));
1329 
1330 	done (status, 0);	/* Don't use Done() */
1331 	/*NOTREACHED*/
1332 }
1333 
1334 
1335 /**
1336  ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT
1337  ** ignore_fault_signals() - IGNORE SAME
1338  **/
1339 
1340 static void
1341 trap_fault_signals(void)
1342 {
1343 	signal (SIGHUP, sigtrap);
1344 	signal (SIGINT, sigtrap);
1345 	signal (SIGQUIT, sigtrap);
1346 	signal (SIGPIPE, sigtrap);
1347 	return;
1348 }
1349 
1350 static void
1351 ignore_fault_signals(void)
1352 {
1353 	signal (SIGHUP, SIG_IGN);
1354 	signal (SIGINT, SIG_IGN);
1355 	signal (SIGQUIT, SIG_IGN);
1356 	signal (SIGPIPE, SIG_IGN);
1357 	return;
1358 }
1359 
1360 /**
1361  ** sigtrap() - TRAP VARIOUS SIGNALS
1362  **/
1363 
1364 static void
1365 sigtrap(int sig)
1366 {
1367 	signal (sig, SIG_IGN);
1368 	switch (sig) {
1369 
1370 	case SIGHUP:
1371 		Done (EXEC_EXIT_HUP, 0);
1372 		/*NOTREACHED*/
1373 
1374 	case SIGQUIT:
1375 	case SIGINT:
1376 		Done (EXEC_EXIT_INTR, 0);
1377 		/*NOTREACHED*/
1378 
1379 	case SIGPIPE:
1380 		Done (EXEC_EXIT_PIPE, 0);
1381 		/*NOTREACHED*/
1382 
1383 	case SIGTERM:
1384 		/*
1385 		 * If we were killed with SIGTERM, it should have been
1386 		 * via the Spooler who should have killed the entire
1387 		 * process group. We have to wait for the children,
1388 		 * since we're their parent, but WE MAY HAVE WAITED
1389 		 * FOR THEM ALREADY (in cool_heels()).
1390 		 */
1391 		if (ChildPid != WaitedChildPid) {
1392 			register int		cpid;
1393 
1394 			while (
1395 				(cpid = wait((int *)0)) != ChildPid
1396 			     && (cpid != -1 || errno != ECHILD)
1397 			)
1398 				;
1399 		}
1400 
1401 		/*
1402 		 * We can't rely on getting SIGTERM back in the wait()
1403 		 * above, because, for instance, some shells trap SIGTERM
1404 		 * and exit instead. Thus we force it.
1405 		 */
1406 		done (SIGTERM, 0);	/* Don't use Done() */
1407 		/*NOTREACHED*/
1408 	}
1409 }
1410 
1411 /**
1412  ** done() - TELL SPOOLER THIS CHILD IS DONE
1413  **/
1414 
1415 static void
1416 done(int status, int err)
1417 {
1418 	if (do_undial)
1419 		undial (1);
1420 
1421 	mputm (ChildMd, S_CHILD_DONE, key, status, err);
1422 	mdisconnect (ChildMd);
1423 
1424 	exit (0);
1425 	/*NOTREACHED*/
1426 }
1427 
1428 /**
1429  ** child_mallocfail()
1430  **/
1431 
1432 static void
1433 child_mallocfail(void)
1434 {
1435 	Done (EXEC_EXIT_NOMEM, ENOMEM);
1436 }
1437