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