Home | History | Annotate | Download | only in lpsched
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #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;
    318 
    319 	syslog(LOG_DEBUG, "exec(%s)", _exec_name(type));
    320 
    321 	memset(av, 0, sizeof (*av));
    322 
    323 	va_start (args, type);
    324 
    325 	switch (type) {
    326 
    327 	case EX_INTERF:
    328 		printer = va_arg(args, PSTATUS *);
    329 		request = printer->request;
    330 		ep = printer->exec;
    331 		break;
    332 
    333 	case EX_FAULT_MESSAGE:
    334 		printer = va_arg(args, PSTATUS *);
    335 		request = va_arg(args, RSTATUS *);
    336 		if (! ( printer->status & (PS_FORM_FAULT | PS_SHOW_FAULT))) {
    337 			return(0);
    338 		}
    339 		ep = printer->fault_exec;
    340 		printerName = (printer->printer && printer->printer->name
    341 				  ? printer->printer->name : "??");
    342 			snprintf(nameBuf, sizeof (nameBuf),
    343 				"%s (on %s)\n", printerName, Local_System);
    344 
    345 		printerNameToShow = nameBuf;
    346 
    347 		(void) time(&now);
    348 		(void) strftime(time_buf, sizeof (time_buf),
    349 			NULL, localtime(&now));
    350 		break;
    351 
    352 	case EX_SLOWF:
    353 		request = va_arg(args, RSTATUS *);
    354 		ep = request->exec;
    355 		break;
    356 
    357 	case EX_NOTIFY:
    358 		request = va_arg(args, RSTATUS *);
    359 		if (request->request->actions & ACT_NOTIFY) {
    360 			errno = EINVAL;
    361 			return (-1);
    362 		}
    363 		ep = request->exec;
    364 		break;
    365 
    366 	case EX_ALERT:
    367 		printer = va_arg(args, PSTATUS *);
    368 		if (!(printer->printer->fault_alert.shcmd)) {
    369 			errno = EINVAL;
    370 			return(-1);
    371 		}
    372 		ep = printer->alert->exec;
    373 		break;
    374 
    375 	case EX_PALERT:
    376 		pwheel = va_arg(args, PWSTATUS *);
    377 		ep = pwheel->alert->exec;
    378 		break;
    379 
    380 	case EX_FORM_MESSAGE:
    381 		(void) time(&now);
    382 		(void) strftime(time_buf, sizeof (time_buf),
    383 			NULL, localtime(&now));
    384 
    385 		/*FALLTHRU*/
    386 	case EX_FALERT:
    387 		form = va_arg(args, FSTATUS *);
    388 		ep = form->alert->exec;
    389 		break;
    390 
    391 	default:
    392 		errno = EINVAL;
    393 		return(-1);
    394 
    395 	}
    396 	va_end (args);
    397 
    398 	if (!ep || (ep->pid > 0)) {
    399 		errno = EBUSY;
    400 		return(-1);
    401 	}
    402 
    403 	ep->flags = 0;
    404 
    405 	key = ep->key = getkey();
    406 
    407 	switch ((ep->pid = Fork1(ep))) {
    408 
    409 	case -1:
    410 		relock ();
    411 		return(-1);
    412 
    413 	case 0:
    414 		/*
    415 		 * We want to be able to tell our parent how we died.
    416 		 */
    417 		lp_alloc_fail_handler = child_mallocfail;
    418 		break;
    419 
    420 	default:
    421 		switch(type) {
    422 
    423 		case EX_INTERF:
    424 			request->request->outcome |= RS_PRINTING;
    425 			break;
    426 
    427 		case EX_NOTIFY:
    428 			request->request->outcome |= RS_NOTIFYING;
    429 			break;
    430 
    431 		case EX_SLOWF:
    432 			request->request->outcome |= RS_FILTERING;
    433 			request->request->outcome &= ~RS_REFILTER;
    434 			break;
    435 
    436 		}
    437 		return(0);
    438 
    439 	}
    440 
    441 	for (i = 0; i < NSIG; i++)
    442 		(void)signal (i, SIG_DFL);
    443 	(void)signal (SIGALRM, SIG_IGN);
    444 	(void)signal (SIGTERM, sigtrap);
    445 
    446 	closelog();
    447 	for (i = 0; i < OpenMax; i++)
    448 		if (i != ChildMd->writefd)
    449 			Close (i);
    450 	openlog("lpsched", LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR);
    451 
    452 	setpgrp();
    453 
    454 	/* Set a default path */
    455 	addenv (&envp, "PATH", "/usr/lib/lp/bin:/usr/bin:/bin:/usr/sbin:/sbin");
    456 	/* copy locale related variables */
    457 	addenv (&envp, "TZ", getenv("TZ"));
    458 	addenv (&envp, "LANG", getenv("LANG"));
    459 	addenv (&envp, "LC_ALL", getenv("LC_ALL"));
    460 	addenv (&envp, "LC_COLLATE", getenv("LC_COLLATE"));
    461 	addenv (&envp, "LC_CTYPE", getenv("LC_CTYPE"));
    462 	addenv (&envp, "LC_MESSAGES", getenv("LC_MESSAGES"));
    463 	addenv (&envp, "LC_MONETARY", getenv("LC_MONETARY"));
    464 	addenv (&envp, "LC_NUMERIC", getenv("LC_NUMERIC"));
    465 	addenv (&envp, "LC_TIME", getenv("LC_TIME"));
    466 
    467 	sprintf ((cp = BIGGEST_NUMBER_S), "%ld", key);
    468 	addenv (&envp, "SPOOLER_KEY", cp);
    469 
    470 #if	defined(DEBUG)
    471 	addenv (&envp, "LPDEBUG", (debug? "1" : "0"));
    472 #endif
    473 
    474 	/*
    475 	 * Open the standard input, standard output, and standard error.
    476 	 */
    477 	switch (type) {
    478 
    479 	case EX_SLOWF:
    480 	case EX_INTERF:
    481 		/*
    482 		 * stdin:  /dev/null
    483 		 * stdout: /dev/null (EX_SLOWF), printer port (EX_INTERF)
    484 		 * stderr: req#
    485 		 */
    486 		infile = 0;
    487 		outfile = 0;
    488 		errfile = makereqerr(request);
    489 		break;
    490 
    491 	case EX_NOTIFY:
    492 		/*
    493 		 * stdin:  req#
    494 		 * stdout: /dev/null
    495 		 * stderr: /dev/null
    496 		 */
    497 		infile = makereqerr(request);
    498 		outfile = 0;
    499 		errfile = 0;
    500 
    501 		break;
    502 
    503 	case EX_ALERT:
    504 	case EX_FALERT:
    505 	case EX_PALERT:
    506 	case EX_FAULT_MESSAGE:
    507 	case EX_FORM_MESSAGE:
    508 		/*
    509 		 * stdin:  /dev/null
    510 		 * stdout: /dev/null
    511 		 * stderr: /dev/null
    512 		 */
    513 		infile = 0;
    514 		outfile = 0;
    515 		errfile = 0;
    516 		break;
    517 
    518 	}
    519 
    520 	if (infile) {
    521 		if (Open(infile, O_RDONLY) == -1)
    522 			Done (EXEC_EXIT_NOPEN, errno);
    523 	} else {
    524 		if (Open("/dev/null", O_RDONLY) == -1)
    525 			Done (EXEC_EXIT_NOPEN, errno);
    526 	}
    527 
    528 	if (outfile) {
    529 		if (Open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
    530 			Done (EXEC_EXIT_NOPEN, errno);
    531 	} else {
    532 		/*
    533 		 * If EX_INTERF, this is still needed to cause the
    534 		 * standard error channel to be #2.
    535 		 */
    536 		if (Open("/dev/null", O_WRONLY) == -1)
    537 			Done (EXEC_EXIT_NOPEN, errno);
    538 	}
    539 
    540 	if (errfile) {
    541 		if (Open(errfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
    542 			Done (EXEC_EXIT_NOPEN, errno);
    543 	} else {
    544 		if (Open("/dev/null", O_WRONLY) == -1)
    545 			Done (EXEC_EXIT_NOPEN, errno);
    546 	}
    547 
    548 	switch (type) {
    549 
    550 	case EX_INTERF:
    551 		/*
    552 		 * Opening a ``port'' can be dangerous to our health:
    553 		 *
    554 		 *	- Hangups can occur if the line is dropped.
    555 		 *	- The printer may send an interrupt.
    556 		 *	- A FIFO may be closed, generating SIGPIPE.
    557 		 *
    558 		 * We catch these so we can complain nicely.
    559 		 */
    560 		trap_fault_signals ();
    561 
    562 		(void)Close (1);
    563 
    564 		if (strchr (request->request->user, '@'))
    565 		{
    566 			procuid = Lp_Uid;
    567 			procgid = Lp_Gid;
    568 		}
    569 		else
    570 		{
    571 			procuid = request->secure->uid;
    572 			procgid = request->secure->gid;
    573 		}
    574 		if (printer->printer->dial_info)
    575 		{
    576 			ret = open_dialup(request->printer_type,
    577 				printer->printer);
    578 			if (ret == 0)
    579 				do_undial = 1;
    580 		}
    581 		else
    582 		{
    583 			ret = open_direct(request->printer_type,
    584 				printer->printer);
    585 			do_undial = 0;
    586 			/* this is a URI */
    587 			if (is_printer_uri(printer->printer->device) == 0)
    588 				addenv(&envp, "DEVICE_URI",
    589 					 printer->printer->device);
    590 		}
    591 				addenv(&envp, "DEVICE_URI",
    592 					 printer->printer->device);
    593 		if (ret != 0)
    594 			Done (ret, errno);
    595 
    596 		if (!(request->request->outcome & RS_FILTERED))
    597 			file_list = request->request->file_list;
    598 
    599 		else {
    600 			register int		count	= 0;
    601 			register char *		num	= BIGGEST_REQID_S;
    602 			register char *		prefix;
    603 
    604 			prefix = makestr(
    605 				Lp_Temp,
    606 				"/F",
    607 				getreqno(request->secure->req_id),
    608 				"-",
    609 				(char *)0
    610 			);
    611 
    612 			file_list = (char **)Malloc(
    613 				(lenlist(request->request->file_list) + 1)
    614 			      * sizeof(char *)
    615 			);
    616 
    617 			for (
    618 				listp = request->request->file_list;
    619 				*listp;
    620 				listp++
    621 			) {
    622 				sprintf (num, "%d", count + 1);
    623 				file_list[count] = makestr(
    624 					prefix,
    625 					num,
    626 					(char *)0
    627 				);
    628 				count++;
    629 			}
    630 			file_list[count] = 0;
    631 		}
    632 
    633 #ifdef LP_USE_PAPI_ATTR
    634 		/*
    635 		 * Check if the PAPI job attribute file exists, if it does
    636 		 * pass the file's pathname to the printer interface script
    637 		 * in an environment variable. This file is created when
    638 		 * print jobs are submitted via the PAPI interface.
    639 		 */
    640 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
    641 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
    642 		path = makepath(Lp_Temp, tmpName, (char *)0);
    643 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
    644 		{
    645 			/*
    646 			 * IPP job attribute file exists for this job so
    647 			 * set the environment variable
    648 			 */
    649 			addenv(&envp, "ATTRPATH", path);
    650 		}
    651 		Free(path);
    652 
    653 		/*
    654 		 * now set environment variable for the printer's PostScript
    655 		 * Printer Description (PPD) file, this is used by the filter
    656 		 * when forming the print data for this printer.
    657 		 */
    658 		if ((request->printer != NULL) &&
    659 		    (request->printer->printer != NULL) &&
    660 		    (request->printer->printer->name != NULL))
    661 		{
    662 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
    663 				request->printer->printer->name);
    664 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
    665 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
    666 			{
    667 				addenv(&envp, "PPD", path);
    668 			}
    669 			Free(path);
    670 		}
    671 #endif
    672 
    673 		if (request->printer_type)
    674 			addenv(&envp, "TERM", request->printer_type);
    675 
    676 		if (!(printer->printer->daisy)) {
    677 			register char *	chset = 0;
    678 			register char *	csp;
    679 
    680 			if (
    681 				request->form
    682 			     && request->form->form->chset
    683 			     && request->form->form->mandatory
    684 			     && !STREQU(NAME_ANY, request->form->form->chset)
    685 			)
    686 				chset = request->form->form->chset;
    687 
    688 			else if (
    689 				request->request->charset
    690 			     && !STREQU(NAME_ANY, request->request->charset)
    691 			)
    692 				chset = request->request->charset;
    693 
    694 			if (chset) {
    695 				csp = search_cslist(
    696 					chset,
    697 					printer->printer->char_sets
    698 				);
    699 
    700 				/*
    701 				 * The "strtok()" below wrecks the string
    702 				 * for future use, but this is a child
    703 				 * process where it won't be needed again.
    704 				 */
    705 				addenv (&envp, "CHARSET",
    706 					(csp? strtok(csp, "=") : chset)
    707 				);
    708 			}
    709 		}
    710 
    711 		if (request->fast)
    712 			addenv(&envp, "FILTER", request->fast);
    713 
    714 		/*
    715 		 * Add the sensitivity label to the environment for
    716 		 * banner page and header/footer processing
    717 		 */
    718 
    719 		if (is_system_labeled() && request->secure->slabel != NULL)
    720 			addenv(&envp, "SLABEL", request->secure->slabel);
    721 
    722 		/*
    723 		 * Add the system name to the user name (ala system!user)
    724 		 * unless it is already there. RFS users may have trouble
    725 		 * here, sorry!
    726 		 */
    727 		cp = strchr(request->secure->user, '@');
    728 
    729 		allTraysWithForm(printer, request->form);
    730 
    731 		/*
    732 		 * Fix for 4137389
    733 		 * Remove double quotes from title string.
    734 		 */
    735 		fr_flg = 1;
    736 		clean_title = strdup(NB(request->request->title));
    737 		if (clean_title == NULL) {
    738 			/*
    739 			 * strdup failed. We're probably hosed
    740 			 * but try setting clean_title
    741 			 * to original title and continuing.
    742 			 */
    743 			clean_title = NB(request->request->title);
    744 			fr_flg = 0;
    745 		} else if (strcmp(clean_title, "") != 0) {
    746 			char *ct_p;
    747 
    748 			for (ct_p = clean_title; *ct_p != NULL; ct_p++) {
    749 				if (*ct_p == '"')
    750 					*ct_p = ' ';
    751 			}
    752 		}
    753 
    754 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces,
    755 					printer->printer->name);
    756 		/*
    757 		 * Read the options field of the request
    758 		 * In case of remote lpd request
    759 		 * the options field will have
    760 		 * job-id-requested. This is the
    761 		 * id sent by the client
    762 		 */
    763 		if (request->request->options != NULL) {
    764 			char *options = NULL, *temp = NULL;
    765 			options = temp = strdup(request->request->options);
    766 
    767 			/*
    768 			 * Search for job-id-requested in
    769 			 * options string
    770 			 */
    771 			options = strstr(options, "job-id-requested");
    772 			if (options != NULL) {
    773 				/*
    774 				 * Extract the ridno from the string
    775 				 * job-id-requested=xxx
    776 				 * In this case ridno = xxx
    777 				 */
    778 				if (STRNEQU(options, "job-id-requested=", 17)) {
    779 					ridno = strdup(options + 17);
    780 					if (ridno != NULL)
    781 						/*
    782 						 * Read job-id-requested
    783 						 * successfully
    784 						 */
    785 						setid = 0;
    786 					else
    787 						/*
    788 						 * could not read
    789 						 * ridno from the string
    790 						 * job-id-requested=xxx
    791 						 */
    792 						setid = 1;
    793 				} else
    794 					/*
    795 					 * could not read
    796 					 * ridno from the string
    797 					 * job-id-requested=xxx
    798 					 */
    799 					setid = 1;
    800 			} else
    801 				/*
    802 				 * No job-id-requested in
    803 				 * request options
    804 				 */
    805 				setid = 1;
    806 
    807 			if (temp != NULL)
    808 				free(temp);
    809 
    810 		} else
    811 			/*
    812 			 * options field in request structure
    813 			 * not set
    814 			 */
    815 			setid = 1;
    816 
    817 
    818 		/*
    819 		 * setid = 1 means the job-id-requested attribute
    820 		 * is not set so read the request->secure->req_id
    821 		 */
    822 		if (setid)
    823 			av[ac++] = arg_string(TRUSTED, "%s",
    824 			    request->secure->req_id);
    825 		else {
    826 			/*
    827 			 * From request->secure->req_id extract the
    828 			 * printer-name.
    829 			 * request->secure->req_id = <printer-name>-<req_id>
    830 			 * The final req-id will be
    831 			 * <printer-name>-<ridno>
    832 			 */
    833 			char *r1 = NULL, *r2 = NULL, *tmp = NULL;
    834 			r1 = r2 = tmp = strdup(request->secure->req_id);
    835 			r2 = strrchr(r1, '-');
    836 			if (r2 != NULL) {
    837 				char *r3 = NULL;
    838 				int lr1 = strlen(r1);
    839 				int lr2 = strlen(r2);
    840 				r1[lr1 - lr2 + 1] = '\0';
    841 
    842 				/*
    843 				 * Now r1 = <printer-name>-
    844 				 */
    845 				lr1 = strlen(r1);
    846 				lr2 = strlen(ridno);
    847 
    848 				r3 = (char *)malloc(lr1+lr2+1);
    849 				if (r3 != NULL) {
    850 					strcpy(r3, r1);
    851 					strcat(r3, ridno);
    852 					/*
    853 					 * Here r3 = <printer-name>-<ridno>
    854 					 */
    855 					av[ac++] = arg_string(TRUSTED,
    856 					    "%s", r3);
    857 					free(r3);
    858 				} else
    859 					av[ac++] = arg_string(TRUSTED, "%s",
    860 					    request->secure->req_id);
    861 
    862 			} else
    863 				av[ac++] = arg_string(TRUSTED, "%s",
    864 				    request->secure->req_id);
    865 
    866 			if (tmp != NULL)
    867 				free(tmp);
    868 
    869 			if (ridno != NULL)
    870 				free(ridno);
    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 		if (strchr (request->request->user, '@'))
    992 		{
    993 			procuid = Lp_Uid;
    994 			procgid = Lp_Gid;
    995 		}
    996 		else
    997 		{
    998 			procuid = request->secure->uid;
    999 			procgid = request->secure->gid;
   1000 		}
   1001 		cp = _alloc_files(
   1002 			lenlist(request->request->file_list),
   1003 			getreqno(request->secure->req_id),
   1004 			procuid, procgid);
   1005 
   1006 		av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter);
   1007 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_Temp, cp);
   1008 		for (listp = request->request->file_list; *listp; listp++)
   1009 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
   1010 
   1011 		(void)chfiles (request->request->file_list, procuid, procgid);
   1012 
   1013 #ifdef LP_USE_PAPI_ATTR
   1014 		/*
   1015 		 * Check if the PAPI job attribute file exists, if it does
   1016 		 * pass the file's pathname to the slow-filters in an
   1017 		 * environment variable. Note: this file is created when
   1018 		 * print jobs are submitted via the PAPI interface.
   1019 		 */
   1020 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
   1021 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
   1022 		path = makepath(Lp_Temp, tmpName, (char *)0);
   1023 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
   1024 		{
   1025 			/*
   1026 			 * IPP job attribute file exists for this job so
   1027 			 * set the environment variable
   1028 			 */
   1029 			addenv(&envp, "ATTRPATH", path);
   1030 		}
   1031 		Free(path);
   1032 
   1033 
   1034 		/*
   1035 		 * now set environment variable for the printer's PostScript
   1036 		 * Printer Description (PPD) file, this is used by the filter
   1037 		 * when forming the print data for this printer.
   1038 		 */
   1039 		if ((request->printer != NULL) &&
   1040 		    (request->printer->printer != NULL) &&
   1041 		    (request->printer->printer->name != NULL))
   1042 		{
   1043 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
   1044 				request->printer->printer->name);
   1045 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
   1046 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
   1047 			{
   1048 				addenv(&envp, "PPD", path);
   1049 			}
   1050 			Free(path);
   1051 		}
   1052 #endif
   1053 		break;
   1054 
   1055 	case EX_ALERT:
   1056 		procuid = Lp_Uid;
   1057 		procgid = Lp_Gid;
   1058 		(void)Chown (printer->alert->msgfile, procuid, procgid);
   1059 
   1060 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
   1061 				printer->printer->name, ALERTSHFILE);
   1062 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
   1063 
   1064 		break;
   1065 
   1066 	case EX_PALERT:
   1067 		procuid = Lp_Uid;
   1068 		procgid = Lp_Gid;
   1069 		(void)Chown (pwheel->alert->msgfile, procuid, procgid);
   1070 
   1071 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels,
   1072 				pwheel->pwheel->name, ALERTSHFILE);
   1073 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
   1074 
   1075 		break;
   1076 
   1077 	case EX_FALERT:
   1078 		procuid = Lp_Uid;
   1079 		procgid = Lp_Gid;
   1080 		(void)Chown (form->alert->msgfile, procuid, procgid);
   1081 
   1082 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
   1083 				form->form->name, ALERTSHFILE);
   1084 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
   1085 
   1086 		break;
   1087 
   1088 	case EX_FORM_MESSAGE:
   1089 		procuid = Lp_Uid;
   1090 		procgid = Lp_Gid;
   1091 
   1092 		av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults);
   1093 		av[ac++] = arg_string(TRUSTED, "%s", form->form->name);
   1094 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
   1095 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
   1096 				form->form->name, FORMMESSAGEFILE);
   1097 
   1098 		break;
   1099 
   1100 	case EX_FAULT_MESSAGE:
   1101 		procuid = Lp_Uid;
   1102 		procgid = Lp_Gid;
   1103 
   1104 		av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults);
   1105 		av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow);
   1106 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
   1107 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
   1108 				printerName, FAULTMESSAGEFILE);
   1109 
   1110 		break;
   1111 
   1112 	case EX_NOTIFY:
   1113 		if (request->request->alert) {
   1114 			if (strchr(request->request->user, '@')) {
   1115 				procuid = Lp_Uid;
   1116 				procgid = Lp_Gid;
   1117 			} else {
   1118 				procuid = request->secure->uid;
   1119 				procgid = request->secure->gid;
   1120 			}
   1121 			av[ac++] = arg_string(TRUSTED, "%s",
   1122 					request->request->alert);
   1123 		} else {
   1124 			char *user = strdup(request->request->user);
   1125 			clean_string(user);
   1126 			slabel = request->secure->slabel;
   1127 
   1128 			if (request->request->actions & ACT_WRITE) {
   1129 				av[ac++] = arg_string(TRUSTED, "%s", BINWRITE);
   1130 				snprintf(argbuf, sizeof (argbuf),
   1131 					"%s %s || %s %s",
   1132 					BINWRITE, user,
   1133 					BINMAIL, user
   1134 				);
   1135 				av[ac++] = arg_string(TRUSTED, "/bin/sh");
   1136 				av[ac++] = arg_string(TRUSTED, "-c");
   1137 				av[ac++] = arg_string(TRUSTED, "%s", argbuf);
   1138 			} else if ((getzoneid() == GLOBAL_ZONEID) &&
   1139 				   is_system_labeled() && (slabel != NULL)) {
   1140 				/*
   1141 				 * If in the global zone and the system is
   1142 				 * labeled, mail is handled via a local
   1143 				 * labeled zone that is the same label as
   1144 				 * the request.
   1145 				 */
   1146 				if ((mail_zonename =
   1147 				    get_labeled_zonename(slabel)) ==
   1148 				    (char *)-1) {
   1149 					/*
   1150 					 * Cannot find labeled zone, just
   1151 					 * return 0.
   1152 					 */
   1153 					return(0);
   1154 				}
   1155 			}
   1156 			if (mail_zonename == NULL) {
   1157 				procuid = Lp_Uid;
   1158 				procgid = Lp_Gid;
   1159 				av[ac++] = arg_string(TRUSTED, "%s", BINMAIL);
   1160 				av[ac++] = arg_string(UNTRUSTED, "%s", user);
   1161 			} else {
   1162 				procuid = getuid();
   1163 				procgid = getgid();
   1164 				av[ac++] = arg_string(TRUSTED, "%s",
   1165 				    "/usr/sbin/zlogin");
   1166 				av[ac++] = arg_string(TRUSTED, "%s",
   1167 				    mail_zonename);
   1168 				av[ac++] = arg_string(TRUSTED, "%s",
   1169 				    BINMAIL);
   1170 				av[ac++] = arg_string(UNTRUSTED, "%s",
   1171 				    user);
   1172 				Free(mail_zonename);
   1173 			}
   1174 
   1175 			free(user);
   1176 		}
   1177 		break;
   1178 	}
   1179 
   1180 	av[ac++] = NULL;
   1181 
   1182 	Fork2 ();
   1183 	/* only the child returns */
   1184 
   1185 	/*
   1186 	 * Correctly set up the supplemental group list
   1187 	 * for proper file access (before execl the interface program)
   1188 	 */
   1189 
   1190 	pwp = getpwuid(procuid);
   1191 	if (pwp == NULL) {
   1192 		note("getpwuid(%d) call failed\n", procuid);
   1193 	} else if (initgroups(pwp->pw_name, procgid) < 0) {
   1194 		note("initgroups() call failed %d\n", errno);
   1195 	}
   1196 
   1197 	setgid (procgid);
   1198 	setuid (procuid);
   1199 
   1200 	/*
   1201 	 * The shell doesn't allow the "trap" builtin to set a trap
   1202 	 * for a signal ignored when the shell is started. Thus, don't
   1203 	 * turn off signals in the last child!
   1204 	 */
   1205 
   1206 #ifdef DEBUG
   1207 	for (i = 0; av[i] != NULL; i++)
   1208 		note("exec(%s): av[%d] = %s", _exec_name(type), i, av[i]);
   1209 	for (i = 0; envp[i] != NULL; i++)
   1210 		note("exec(%s): envp[%d] = %s", _exec_name(type), i, envp[i]);
   1211 #endif
   1212 
   1213 	execvpe(av[0], av, envp);
   1214 	Done (EXEC_EXIT_NEXEC, errno);
   1215 	/*NOTREACHED*/
   1216 	return (0);
   1217 }
   1218 
   1219 /**
   1220  ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT
   1221  **/
   1222 
   1223 static void
   1224 addenv(char ***envp, char *name, char *value)
   1225 {
   1226 	register char *		cp;
   1227 
   1228 	if ((name == NULL) || (value == NULL))
   1229 		return;
   1230 
   1231 	if ((cp = makestr(name, "=", value, (char *)0)))
   1232 		addlist(envp, cp);
   1233 	return;
   1234 }
   1235 
   1236 /**
   1237  ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT
   1238  **/
   1239 
   1240 static int
   1241 Fork1(EXEC *ep)
   1242 {
   1243 	int			pid;
   1244 	int			fds[2];
   1245 
   1246 	if (pipe(fds) == -1) {
   1247 		note("Failed to create pipe for child process (%s).\n", PERROR);
   1248 		errno = EAGAIN ;
   1249 		return(-1);
   1250 	}
   1251 
   1252 	ep->md = mconnect((char *)0, fds[0], fds[1]);
   1253 
   1254 	switch (pid = fork()) {
   1255 
   1256 	case -1:
   1257 		mdisconnect(ep->md);
   1258 		close(fds[0]);
   1259 		close(fds[1]);
   1260 		ep->md = 0;
   1261 		return (-1);
   1262 
   1263 	case 0:
   1264 		ChildMd = mconnect(NULL, fds[1], fds[1]);
   1265 		return (0);
   1266 
   1267 	default:
   1268 		mlistenadd(ep->md, POLLIN);
   1269 		return (pid);
   1270 	}
   1271 }
   1272 
   1273 /**
   1274  ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT
   1275  **/
   1276 
   1277 static void
   1278 Fork2(void)
   1279 {
   1280 	switch ((ChildPid = fork())) {
   1281 
   1282 	case -1:
   1283 		Done (EXEC_EXIT_NFORK, errno);
   1284 		/*NOTREACHED*/
   1285 
   1286 	case 0:
   1287 		return;
   1288 
   1289 	default:
   1290 		/*
   1291 		 * Delay calling "ignore_fault_signals()" as long
   1292 		 * as possible, to give the child a chance to exec
   1293 		 * the interface program and turn on traps.
   1294 		 */
   1295 
   1296 		cool_heels ();
   1297 		/*NOTREACHED*/
   1298 
   1299 	}
   1300 }
   1301 
   1302 
   1303 /**
   1304  ** cool_heels() - WAIT FOR CHILD TO "DIE"
   1305  **/
   1306 
   1307 static void
   1308 cool_heels(void)
   1309 {
   1310 	int			status;
   1311 
   1312 	/*
   1313 	 * At this point our only job is to wait for the child process.
   1314 	 * If we hang out for a bit longer, that's okay.
   1315 	 * By delaying before turning off the fault signals,
   1316 	 * we increase the chance that the child process has completed
   1317 	 * its exec and has turned on the fault traps. Nonetheless,
   1318 	 * we can't guarantee a zero chance of missing a fault.
   1319 	 * (We don't want to keep trapping the signals because the
   1320 	 * interface program is likely to have a better way to handle
   1321 	 * them; this process provides only rudimentary handling.)
   1322 	 *
   1323 	 * Note that on a very busy system, or with a very fast interface
   1324 	 * program, the tables could be turned: Our sleep below (coupled
   1325 	 * with a delay in the kernel scheduling us) may cause us to
   1326 	 * detect the fault instead of the interface program.
   1327 	 *
   1328 	 * What we need is a way to synchronize with the child process.
   1329 	 */
   1330 	sleep (1);
   1331 	ignore_fault_signals ();
   1332 
   1333 	WaitedChildPid = 0;
   1334 	while ((WaitedChildPid = wait(&status)) != ChildPid)
   1335 		;
   1336 
   1337 	if (
   1338 		EXITED(status) > EXEC_EXIT_USER
   1339 	     && EXITED(status) != EXEC_EXIT_FAULT
   1340 	)
   1341 		Done (EXEC_EXIT_EXIT, EXITED(status));
   1342 
   1343 	done (status, 0);	/* Don't use Done() */
   1344 	/*NOTREACHED*/
   1345 }
   1346 
   1347 
   1348 /**
   1349  ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT
   1350  ** ignore_fault_signals() - IGNORE SAME
   1351  **/
   1352 
   1353 static void
   1354 trap_fault_signals(void)
   1355 {
   1356 	signal (SIGHUP, sigtrap);
   1357 	signal (SIGINT, sigtrap);
   1358 	signal (SIGQUIT, sigtrap);
   1359 	signal (SIGPIPE, sigtrap);
   1360 	return;
   1361 }
   1362 
   1363 static void
   1364 ignore_fault_signals(void)
   1365 {
   1366 	signal (SIGHUP, SIG_IGN);
   1367 	signal (SIGINT, SIG_IGN);
   1368 	signal (SIGQUIT, SIG_IGN);
   1369 	signal (SIGPIPE, SIG_IGN);
   1370 	return;
   1371 }
   1372 
   1373 /**
   1374  ** sigtrap() - TRAP VARIOUS SIGNALS
   1375  **/
   1376 
   1377 static void
   1378 sigtrap(int sig)
   1379 {
   1380 	signal (sig, SIG_IGN);
   1381 	switch (sig) {
   1382 
   1383 	case SIGHUP:
   1384 		Done (EXEC_EXIT_HUP, 0);
   1385 		/*NOTREACHED*/
   1386 
   1387 	case SIGQUIT:
   1388 	case SIGINT:
   1389 		Done (EXEC_EXIT_INTR, 0);
   1390 		/*NOTREACHED*/
   1391 
   1392 	case SIGPIPE:
   1393 		Done (EXEC_EXIT_PIPE, 0);
   1394 		/*NOTREACHED*/
   1395 
   1396 	case SIGTERM:
   1397 		/*
   1398 		 * If we were killed with SIGTERM, it should have been
   1399 		 * via the Spooler who should have killed the entire
   1400 		 * process group. We have to wait for the children,
   1401 		 * since we're their parent, but WE MAY HAVE WAITED
   1402 		 * FOR THEM ALREADY (in cool_heels()).
   1403 		 */
   1404 		if (ChildPid != WaitedChildPid) {
   1405 			register int		cpid;
   1406 
   1407 			while (
   1408 				(cpid = wait((int *)0)) != ChildPid
   1409 			     && (cpid != -1 || errno != ECHILD)
   1410 			)
   1411 				;
   1412 		}
   1413 
   1414 		/*
   1415 		 * We can't rely on getting SIGTERM back in the wait()
   1416 		 * above, because, for instance, some shells trap SIGTERM
   1417 		 * and exit instead. Thus we force it.
   1418 		 */
   1419 		done (SIGTERM, 0);	/* Don't use Done() */
   1420 		/*NOTREACHED*/
   1421 	}
   1422 }
   1423 
   1424 /**
   1425  ** done() - TELL SPOOLER THIS CHILD IS DONE
   1426  **/
   1427 
   1428 static void
   1429 done(int status, int err)
   1430 {
   1431 	if (do_undial)
   1432 		undial (1);
   1433 
   1434 	mputm (ChildMd, S_CHILD_DONE, key, status, err);
   1435 	mdisconnect (ChildMd);
   1436 
   1437 	exit (0);
   1438 	/*NOTREACHED*/
   1439 }
   1440 
   1441 /**
   1442  ** child_mallocfail()
   1443  **/
   1444 
   1445 static void
   1446 child_mallocfail(void)
   1447 {
   1448 	Done (EXEC_EXIT_NOMEM, ENOMEM);
   1449 }
   1450