view usr/src/cmd/lp/cmd/lpsched/exec.c @ 13:f60a82e85167 default tip

Revert NEC's changes to fix krb5 build
author Andrew Stormont <andyjstormont@gmail.com>
date Fri, 02 Mar 2012 22:25:26 +0000
parents 1a15d5aaf794
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <pwd.h>
#include <zone.h>
#if defined PS_FAULTED
#undef  PS_FAULTED
#endif /* PS_FAULTED */
#include <dial.h>

#include <stdlib.h>
#include "limits.h"
#include "stdarg.h"
#include "wait.h"
#include "dial.h"
#include "lpsched.h"
#include <syslog.h>
#include "tsol/label.h"

#define Done(EC,ERRNO)	done(((EC) << 8),ERRNO)

#define	STRLCAT(dst, src, size) \
	if (strlcat((dst), (src), (size)) >= (size)) { \
		errno = EINVAL; \
		return (-1); \
	}

static MESG *		ChildMd;

static int		ChildPid;
static int		WaitedChildPid;
static int		do_undial;

static char		argbuf[ARG_MAX];

static long		key;

static void		sigtrap ( int );
static void		done ( int , int );
static void		cool_heels ( void );
static void		addenv (char ***envp, char * , char * );
static void		trap_fault_signals ( void );
static void		ignore_fault_signals ( void );
static void		child_mallocfail ( void );
static void		Fork2 ( void );

static int		Fork1 ( EXEC * );

static void
relock(void)
{
	struct flock		l;

	l.l_type = F_WRLCK;
	l.l_whence = 1;
	l.l_start = 0;
	l.l_len = 0;
	(void)Fcntl (lock_fd, F_SETLK, &l);
	return;
}

static char *_exec_name(int type)
{
	static char *_names[] = {
	"", "EX_INTERF", "EX_SLOWF", "EX_ALERT", "EX_FALERT", "EX_PALERT",
	"EX_NOTIFY", "EX_FAULT_MESSAGE", "EX_FORM_MESSAGE", NULL };

	if ((type < 0) || (type > EX_FORM_MESSAGE))
		return ("BAD_EXEC_TYPE");
	else
		return (_names[type]);
}

/*
 * This function replaces characters in a string that might be used
 * to exploit a security hole.  Replace command seperators (`, &, ;, |, ^),
 * output redirection (>, |), variable expansion ($), and character
 * escape (\).
 *
 * Bugid 4141687
 * Add ( ) < * ? [
 * Bugid 4139071
 * Remove \
 */
void clean_string(char *ptr)
{
	char *cp;
	wchar_t wc;
	size_t len;

	for (cp = ptr; *cp != NULL; ) {
		if ((len = mbtowc(&wc, cp, MB_CUR_MAX)) == -1) {
			cp++;
			continue;
		}

		if (len == 1 &&
		    ((wc == L'`') || (wc == L'&') || (wc == L';') ||
		    (wc == L'|') || (wc == L'>') || (wc == L'^') ||
		    (wc == L'$') || (wc == L'(') || (wc == L')') ||
		    (wc == L'<') || (wc == L'*') || (wc == L'?') ||
		    (wc == L'[')))
			*cp = '_';
		cp += len;
	}
}

enum trust {TRUSTED, UNTRUSTED};

static char *arg_string(enum trust type, char *fmt, ...) __PRINTFLIKE(2);

/* PRINTFLIKE2 */
static char *
arg_string(enum trust type, char *fmt, ...)
{
	char buf[BUFSIZ];
	va_list	args;

	va_start(args, fmt);
	(void) vsnprintf(buf, sizeof(buf), fmt, args);
	va_end(args);

	/*
	 * If the string contains data from an untrusted origin (user supplied),
	 * clean it up in case one of our progeny is a shell script and isn't
	 * careful about checking its input.
	 */
	if (type == UNTRUSTED)
		clean_string(buf);

	return (strdup(buf));
}

/* stolen from libc/gen/port/gen/execvp.c */
static const char *
execat(const char *s1, const char *s2, char *si)
{
        char    *s;
        int cnt = PATH_MAX + 1; /* number of characters in s2 */

        s = si;
        while (*s1 && *s1 != ':') {
                if (cnt > 0) {
                        *s++ = *s1++;
                        cnt--;
                } else
                        s1++;
        }
        if (si != s && cnt > 0) {
                *s++ = '/';
                cnt--;
        }
        while (*s2 && cnt > 0) {
                *s++ = *s2++;
                cnt--;
        }
        *s = '\0';
        return (*s1 ? ++s1: 0);
}

/*
 * Similiar to execvp(), execpt you can supply an environment and we always
 * use /bin/sh for shell scripts.  The PATH searched is the PATH in the
 * current environment, not the environment in the argument list.
 * This was pretty much stolen from libc/gen/port/execvp.c
 */
static int
execvpe(char *name, char *const argv[], char *const envp[])
{
	char *path;
	char fname[PATH_MAX+2];
	char *newargs[256];
	int i;
	const char *cp;
	unsigned etxtbsy = 1;
        int eacces = 0;

	if (*name == '\0') {
		errno = ENOENT;
		return (-1);
	}

	if ((path = getenv("PATH")) == NULL)
		path = "/usr/bin:/bin";

        cp = strchr(name, '/')? (const char *)"": path;

        do {
                cp = execat(cp, name, fname);
        retry:
                /*
                 * 4025035 and 4038378
                 * if a filename begins with a "-" prepend "./" so that
                 * the shell can't interpret it as an option
                 */
                if (*fname == '-') {
                        size_t size = strlen(fname) + 1;
                        if ((size + 2) > sizeof (fname)) {
                                errno = E2BIG;
                                return (-1);
                        }
                        (void) memmove(fname + 2, fname, size);
                        fname[0] = '.';
                        fname[1] = '/';
                }
                (void) execve(fname, argv, envp);
                switch (errno) {
                case ENOEXEC:
                        newargs[0] = "sh";
                        newargs[1] = fname;
                        for (i = 1; (newargs[i + 1] = argv[i]) != NULL; ++i) {
                                if (i >= 254) {
                                        errno = E2BIG;
                                        return (-1);
                                }
                        }
                        (void) execve("/bin/sh", newargs, envp);
                        return (-1);
                case ETXTBSY:
                        if (++etxtbsy > 5)
                                return (-1);
                        (void) sleep(etxtbsy);
                        goto retry;
                case EACCES:
                        ++eacces;
                        break;
                case ENOMEM:
                case E2BIG:
                case EFAULT:
                        return (-1);
                }
        } while (cp);
        if (eacces)
                errno = EACCES;
        return (-1);
}

static char time_buf[50];

/**
 ** exec() - FORK AND EXEC CHILD PROCESS
 **/

/*VARARGS1*/
int
exec(int type, ...)
{
	va_list			args;

	int			i;
	int			procuid;
	int			procgid;
	int			ret;
	int			fr_flg;

	char			*cp;
	char			*infile;
	char			*outfile;
	char			*errfile;
	char			*sep;

	char			**listp;
	char			**file_list;
	char			*printerName;
	char			*printerNameToShow;
	static char		nameBuf[100];
	char			*clean_title;

	PSTATUS			*printer;

	RSTATUS			*request;

	FSTATUS			*form;

	EXEC			*ep;

	PWSTATUS		*pwheel;
	time_t			now;
	struct passwd		*pwp;
#ifdef LP_USE_PAPI_ATTR
	struct stat		tmpBuf;
	char 			tmpName[BUFSIZ];
	char			*path = NULL;
#endif
	char *av[ARG_MAX];
	char **envp = NULL;
	int ac = 0;
	char	*mail_zonename = NULL;
	char	*slabel = NULL;

	syslog(LOG_DEBUG, "exec(%s)", _exec_name(type));

	memset(av, 0, sizeof (*av));

	va_start (args, type);

	switch (type) {

	case EX_INTERF:
		printer = va_arg(args, PSTATUS *);
		request = printer->request;
		ep = printer->exec;
		break;
		
	case EX_FAULT_MESSAGE:
		printer = va_arg(args, PSTATUS *);
		request = va_arg(args, RSTATUS *);
		if (! ( printer->status & (PS_FORM_FAULT | PS_SHOW_FAULT))) {
			return(0);
		}
		ep = printer->fault_exec;
		printerName = (printer->printer && printer->printer->name 
				  ? printer->printer->name : "??");
			snprintf(nameBuf, sizeof (nameBuf),
				"%s (on %s)\n", printerName, Local_System);

		printerNameToShow = nameBuf;

		(void) time(&now);
		(void) strftime(time_buf, sizeof (time_buf),
			NULL, localtime(&now));
		break;

	case EX_SLOWF:
		request = va_arg(args, RSTATUS *);
		ep = request->exec;
		break;

	case EX_NOTIFY:
		request = va_arg(args, RSTATUS *);
		if (request->request->actions & ACT_NOTIFY) {
			errno = EINVAL;
			return (-1);
		}
		ep = request->exec;
		break;

	case EX_ALERT:
		printer = va_arg(args, PSTATUS *);
		if (!(printer->printer->fault_alert.shcmd)) {
			errno = EINVAL;
			return(-1);
		}
		ep = printer->alert->exec;
		break;

	case EX_PALERT:
		pwheel = va_arg(args, PWSTATUS *);
		ep = pwheel->alert->exec;
		break;

	case EX_FORM_MESSAGE:
		(void) time(&now);
		(void) strftime(time_buf, sizeof (time_buf),
			NULL, localtime(&now));

		/*FALLTHRU*/
	case EX_FALERT:
		form = va_arg(args, FSTATUS *);
		ep = form->alert->exec;
		break;

	default:
		errno = EINVAL;
		return(-1);

	}
	va_end (args);

	if (!ep || (ep->pid > 0)) {
		errno = EBUSY;
		return(-1);
	}

	ep->flags = 0;

	key = ep->key = getkey();

	switch ((ep->pid = Fork1(ep))) {

	case -1:
		relock ();
		return(-1);

	case 0:
		/*
		 * We want to be able to tell our parent how we died.
		 */
		lp_alloc_fail_handler = child_mallocfail;
		break;

	default:
		switch(type) {

		case EX_INTERF:
			request->request->outcome |= RS_PRINTING;
			break;

		case EX_NOTIFY:
			request->request->outcome |= RS_NOTIFYING;
			break;

		case EX_SLOWF:
			request->request->outcome |= RS_FILTERING;
			request->request->outcome &= ~RS_REFILTER;
			break;

		}
		return(0);

	}

	for (i = 0; i < NSIG; i++)
		(void)signal (i, SIG_DFL);
	(void)signal (SIGALRM, SIG_IGN);
	(void)signal (SIGTERM, sigtrap);

	closelog();
	for (i = 0; i < OpenMax; i++)
		if (i != ChildMd->writefd)
			Close (i);
	openlog("lpsched", LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR);

	setpgrp();

	/* Set a default path */
	addenv (&envp, "PATH", "/usr/lib/lp/bin:/usr/bin:/bin:/usr/sbin:/sbin");
	/* copy locale related variables */
	addenv (&envp, "TZ", getenv("TZ"));
	addenv (&envp, "LANG", getenv("LANG"));
	addenv (&envp, "LC_ALL", getenv("LC_ALL"));
	addenv (&envp, "LC_COLLATE", getenv("LC_COLLATE"));
	addenv (&envp, "LC_CTYPE", getenv("LC_CTYPE"));
	addenv (&envp, "LC_MESSAGES", getenv("LC_MESSAGES"));
	addenv (&envp, "LC_MONETARY", getenv("LC_MONETARY"));
	addenv (&envp, "LC_NUMERIC", getenv("LC_NUMERIC"));
	addenv (&envp, "LC_TIME", getenv("LC_TIME"));

	sprintf ((cp = BIGGEST_NUMBER_S), "%ld", key);
	addenv (&envp, "SPOOLER_KEY", cp);

#if	defined(DEBUG)
	addenv (&envp, "LPDEBUG", (debug? "1" : "0"));
#endif

	/*
	 * Open the standard input, standard output, and standard error.
	 */
	switch (type) {
		
	case EX_SLOWF:
	case EX_INTERF:
		/*
		 * stdin:  /dev/null
		 * stdout: /dev/null (EX_SLOWF), printer port (EX_INTERF)
		 * stderr: req#
		 */
		infile = 0;
		outfile = 0;
		errfile = makereqerr(request);
		break;

	case EX_NOTIFY:
		/*
		 * stdin:  req#
		 * stdout: /dev/null
		 * stderr: /dev/null
		 */
		infile = makereqerr(request);
		outfile = 0;
		errfile = 0;

		break;

	case EX_ALERT:
	case EX_FALERT:
	case EX_PALERT:
	case EX_FAULT_MESSAGE:
	case EX_FORM_MESSAGE:
		/*
		 * stdin:  /dev/null
		 * stdout: /dev/null
		 * stderr: /dev/null
		 */
		infile = 0;
		outfile = 0;
		errfile = 0;
		break;

	}

	if (infile) {
		if (Open(infile, O_RDONLY) == -1)
			Done (EXEC_EXIT_NOPEN, errno);
	} else {
		if (Open("/dev/null", O_RDONLY) == -1)
			Done (EXEC_EXIT_NOPEN, errno);
	}

	if (outfile) {
		if (Open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
			Done (EXEC_EXIT_NOPEN, errno);
	} else {
		/*
		 * If EX_INTERF, this is still needed to cause the
		 * standard error channel to be #2.
		 */
		if (Open("/dev/null", O_WRONLY) == -1)
			Done (EXEC_EXIT_NOPEN, errno);
	}

	if (errfile) {
		if (Open(errfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
			Done (EXEC_EXIT_NOPEN, errno);
	} else {
		if (Open("/dev/null", O_WRONLY) == -1)
			Done (EXEC_EXIT_NOPEN, errno);
	}

	switch (type) {

	case EX_INTERF:
		/*
		 * Opening a ``port'' can be dangerous to our health:
		 *
		 *	- Hangups can occur if the line is dropped.
		 *	- The printer may send an interrupt.
		 *	- A FIFO may be closed, generating SIGPIPE.
		 *
		 * We catch these so we can complain nicely.
		 */
		trap_fault_signals ();

		(void)Close (1);

		if (strchr (request->request->user, '@'))
		{
			procuid = Lp_Uid;
			procgid = Lp_Gid;
		}
		else
		{
			procuid = request->secure->uid;
			procgid = request->secure->gid;
		}
		if (printer->printer->dial_info)
		{
			ret = open_dialup(request->printer_type,
				printer->printer);
			if (ret == 0)
				do_undial = 1;
		}
		else
		{
			ret = open_direct(request->printer_type,
				printer->printer);
			do_undial = 0;
			/* this is a URI */
			if (is_printer_uri(printer->printer->device) == 0)
				addenv(&envp, "DEVICE_URI",
					 printer->printer->device);
		}
				addenv(&envp, "DEVICE_URI",
					 printer->printer->device);
		if (ret != 0)
			Done (ret, errno);
			
		if (!(request->request->outcome & RS_FILTERED))
			file_list = request->request->file_list;

		else {
			register int		count	= 0;
			register char *		num	= BIGGEST_REQID_S;
			register char *		prefix;

			prefix = makestr(
				Lp_Temp,
				"/F",
				getreqno(request->secure->req_id),
				"-",
				(char *)0
			);

			file_list = (char **)Malloc(
				(lenlist(request->request->file_list) + 1)
			      * sizeof(char *)
			);

			for (
				listp = request->request->file_list;
				*listp;
				listp++
			) {
				sprintf (num, "%d", count + 1);
				file_list[count] = makestr(
					prefix,
					num,
					(char *)0
				);
				count++;
			}
			file_list[count] = 0;
		}

#ifdef LP_USE_PAPI_ATTR
		/*
		 * Check if the PAPI job attribute file exists, if it does
		 * pass the file's pathname to the printer interface script
		 * in an environment variable. This file is created when
		 * print jobs are submitted via the PAPI interface.
		 */
		snprintf(tmpName, sizeof (tmpName), "%s-%s",
			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
		path = makepath(Lp_Temp, tmpName, (char *)0);
		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
		{
			/*
			 * IPP job attribute file exists for this job so
			 * set the environment variable
			 */
			addenv(&envp, "ATTRPATH", path);
		}
		Free(path);

		/*
		 * now set environment variable for the printer's PostScript
		 * Printer Description (PPD) file, this is used by the filter
		 * when forming the print data for this printer.
		 */
		if ((request->printer != NULL) &&
		    (request->printer->printer != NULL) &&
		    (request->printer->printer->name != NULL))
		{
			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
				request->printer->printer->name);
			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
			{
				addenv(&envp, "PPD", path);
			}
			Free(path);
		}
#endif

		if (request->printer_type)
			addenv(&envp, "TERM", request->printer_type);

		if (!(printer->printer->daisy)) {
			register char *	chset = 0;
			register char *	csp;

			if (
				request->form
			     && request->form->form->chset
			     && request->form->form->mandatory
			     && !STREQU(NAME_ANY, request->form->form->chset)
			)
				chset = request->form->form->chset;

			else if (
				request->request->charset
			     && !STREQU(NAME_ANY, request->request->charset)
			)
				chset = request->request->charset;

			if (chset) {
				csp = search_cslist(
					chset,
					printer->printer->char_sets
				);

				/*
				 * The "strtok()" below wrecks the string
				 * for future use, but this is a child
				 * process where it won't be needed again.
				 */
				addenv (&envp, "CHARSET",
					(csp? strtok(csp, "=") : chset)
				);
			}
		}

		if (request->fast)
			addenv(&envp, "FILTER", request->fast);

		/*
		 * Add the sensitivity label to the environment for
		 * banner page and header/footer processing
		 */

		if (is_system_labeled() && request->secure->slabel != NULL)
			addenv(&envp, "SLABEL", request->secure->slabel);

		/*
		 * Add the system name to the user name (ala system!user)
		 * unless it is already there. RFS users may have trouble
		 * here, sorry!
		 */
		cp = strchr(request->secure->user, '@');

		allTraysWithForm(printer, request->form); 

		/*
		 * Fix for 4137389
		 * Remove double quotes from title string.
		 */
		fr_flg = 1;
		clean_title = strdup(NB(request->request->title));
		if (clean_title == NULL) {
			/*
			 * strdup failed. We're probably hosed
			 * but try setting clean_title
			 * to original title and continuing.
			 */
			clean_title = NB(request->request->title);
			fr_flg = 0;
		} else if (strcmp(clean_title, "") != 0) {
			char *ct_p;

			for (ct_p = clean_title; *ct_p != NULL; ct_p++) {
				if (*ct_p == '"')
					*ct_p = ' ';
			}
		}

		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces,
					printer->printer->name);
		av[ac++] = arg_string(TRUSTED, "%s", request->secure->req_id);
		av[ac++] = arg_string(UNTRUSTED, "%s", request->request->user);
		av[ac++] = arg_string(TRUSTED, "%s", clean_title);
		av[ac++] = arg_string(TRUSTED, "%d", request->copies);

		if (fr_flg)
			free (clean_title);

		sep = "";

		/*
		 * Do the administrator defined key=value pair options
		 */

		argbuf[0] = '\0';
				
		if (printer->printer->options) {
			char **tmp = printer->printer->options;
			while(*tmp != NULL) {
				STRLCAT(argbuf, sep, sizeof (argbuf));
				sep = " ";
				STRLCAT(argbuf, *tmp++, sizeof (argbuf));
			}
		}

		/*
		 * Do the administrator defined ``stty'' stuff before
		 * the user's -o options, to allow the user to override.
		 */
		if (printer->printer->stty) {
			STRLCAT (argbuf, sep, sizeof (argbuf));
			sep = " ";
			STRLCAT (argbuf, "stty='", sizeof (argbuf));
			STRLCAT (argbuf, printer->printer->stty,
			    sizeof (argbuf));
			STRLCAT (argbuf, "'", sizeof (argbuf));
		}

		/*
		 * Do all of the user's options except the cpi/lpi/etc.
		 * stuff, which is done separately.
		 */
		if (request->request->options) {
			listp = dashos(request->request->options);
			while (*listp) {
				if (
					!STRNEQU(*listp, "cpi=", 4)
				     && !STRNEQU(*listp, "lpi=", 4)
				     && !STRNEQU(*listp, "width=", 6)
				     && !STRNEQU(*listp, "length=", 7)
				) {
					STRLCAT (argbuf, sep, sizeof (argbuf));
					sep = " ";
					STRLCAT (argbuf, *listp,
					    sizeof (argbuf));
				}
				listp++;
			}
		}

		/*
		 * The "pickfilter()" routine (from "validate()")
		 * stored the cpi/lpi/etc. stuff that should be
		 * used for this request. It chose form over user,
		 * and user over printer.
		 */
		if (request->cpi) {
			STRLCAT (argbuf, sep, sizeof (argbuf));
			sep = " ";
			STRLCAT (argbuf, "cpi=", sizeof (argbuf));
			STRLCAT (argbuf, request->cpi, sizeof (argbuf));
		}
		if (request->lpi) {
			STRLCAT (argbuf, sep, sizeof (argbuf));
			sep = " ";
			STRLCAT (argbuf, "lpi=", sizeof (argbuf));
			STRLCAT (argbuf, request->lpi, sizeof (argbuf));
		}
		if (request->pwid) {
			STRLCAT (argbuf, sep, sizeof (argbuf));
			sep = " ";
			STRLCAT (argbuf, "width=", sizeof (argbuf));
			STRLCAT (argbuf, request->pwid, sizeof (argbuf));
		}
		if (request->plen) {
			STRLCAT (argbuf, sep, sizeof (argbuf));
			sep = " ";
			STRLCAT (argbuf, "length=", sizeof (argbuf));
			STRLCAT (argbuf, request->plen, sizeof (argbuf));
		}

		/*
		 * Do the ``raw'' bit last, to ensure it gets
		 * done. If the user doesn't want this, then he or
		 * she can do the correct thing using -o stty=
		 * and leaving out the -r option.
		 */
		if (request->request->actions & ACT_RAW) {
			STRLCAT (argbuf, sep, sizeof (argbuf));
			sep = " ";
			STRLCAT (argbuf, "stty=-opost", sizeof (argbuf));
		}


		/* the "options" */
		av[ac++] = arg_string(UNTRUSTED, "%s", argbuf);

		for (listp = file_list; *listp; listp++)
			av[ac++] = arg_string(TRUSTED, "%s", *listp);

		(void)chfiles (file_list, procuid, procgid);

		break;


	case EX_SLOWF:
		if (request->slow)
			addenv(&envp, "FILTER", request->slow);

		if (strchr (request->request->user, '@'))
		{
			procuid = Lp_Uid;
			procgid = Lp_Gid;
		}
		else
		{
			procuid = request->secure->uid;
			procgid = request->secure->gid;
		}
		cp = _alloc_files(
			lenlist(request->request->file_list),
			getreqno(request->secure->req_id),
			procuid, procgid);

		av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter);
		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_Temp, cp);
		for (listp = request->request->file_list; *listp; listp++)
			av[ac++] = arg_string(TRUSTED, "%s", *listp);

		(void)chfiles (request->request->file_list, procuid, procgid);

#ifdef LP_USE_PAPI_ATTR
		/*
		 * Check if the PAPI job attribute file exists, if it does
		 * pass the file's pathname to the slow-filters in an
		 * environment variable. Note: this file is created when
		 * print jobs are submitted via the PAPI interface.
		 */
		snprintf(tmpName, sizeof (tmpName), "%s-%s",
			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
		path = makepath(Lp_Temp, tmpName, (char *)0);
		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
		{
			/*
			 * IPP job attribute file exists for this job so
			 * set the environment variable
			 */
			addenv(&envp, "ATTRPATH", path);
		}
		Free(path);


		/*
		 * now set environment variable for the printer's PostScript
		 * Printer Description (PPD) file, this is used by the filter
		 * when forming the print data for this printer.
		 */
		if ((request->printer != NULL) &&
		    (request->printer->printer != NULL) &&
		    (request->printer->printer->name != NULL))
		{
			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
				request->printer->printer->name);
			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
			{
				addenv(&envp, "PPD", path);
			}
			Free(path);
		}
#endif
		break;

	case EX_ALERT:
		procuid = Lp_Uid;
		procgid = Lp_Gid;
		(void)Chown (printer->alert->msgfile, procuid, procgid);

		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
				printer->printer->name, ALERTSHFILE);
		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);

		break;

	case EX_PALERT:
		procuid = Lp_Uid;
		procgid = Lp_Gid;
		(void)Chown (pwheel->alert->msgfile, procuid, procgid);

		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels,
				pwheel->pwheel->name, ALERTSHFILE);
		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);

		break;

	case EX_FALERT:
		procuid = Lp_Uid;
		procgid = Lp_Gid;
		(void)Chown (form->alert->msgfile, procuid, procgid);

		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
				form->form->name, ALERTSHFILE);
		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);

		break;

	case EX_FORM_MESSAGE:
		procuid = Lp_Uid;
		procgid = Lp_Gid;

		av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults);
		av[ac++] = arg_string(TRUSTED, "%s", form->form->name);
		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
				form->form->name, FORMMESSAGEFILE);

		break;

	case EX_FAULT_MESSAGE:
		procuid = Lp_Uid;
		procgid = Lp_Gid;

		av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults);
		av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow);
		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
				printerName, FAULTMESSAGEFILE);

		break;

	case EX_NOTIFY:
		if (request->request->alert) {
			if (strchr(request->request->user, '@')) {
				procuid = Lp_Uid;
				procgid = Lp_Gid;
			} else {
				procuid = request->secure->uid;
				procgid = request->secure->gid;
			}
			av[ac++] = arg_string(TRUSTED, "%s",
					request->request->alert);
		} else {
			char *user = strdup(request->request->user);
			clean_string(user);
			slabel = request->secure->slabel;

			if (request->request->actions & ACT_WRITE) {
				av[ac++] = arg_string(TRUSTED, "%s", BINWRITE);
				snprintf(argbuf, sizeof (argbuf),
					"%s %s || %s %s",
					BINWRITE, user,
					BINMAIL, user
				);
				av[ac++] = arg_string(TRUSTED, "/bin/sh");
				av[ac++] = arg_string(TRUSTED, "-c");
				av[ac++] = arg_string(TRUSTED, "%s", argbuf);
			} else if ((getzoneid() == GLOBAL_ZONEID) &&
				   is_system_labeled() && (slabel != NULL)) {
				/*
				 * If in the global zone and the system is
				 * labeled, mail is handled via a local
				 * labeled zone that is the same label as
				 * the request.
				 */
				if ((mail_zonename =
				    get_labeled_zonename(slabel)) ==
				    (char *)-1) {
					/*
					 * Cannot find labeled zone, just
					 * return 0.
					 */
					return(0);
				}
			}
			if (mail_zonename == NULL) {
				procuid = Lp_Uid;
				procgid = Lp_Gid;
				av[ac++] = arg_string(TRUSTED, "%s", BINMAIL);
				av[ac++] = arg_string(UNTRUSTED, "%s", user);
			} else {
				procuid = getuid();
				procgid = getgid();
				av[ac++] = arg_string(TRUSTED, "%s",
				    "/usr/sbin/zlogin");
				av[ac++] = arg_string(TRUSTED, "%s",
				    mail_zonename);
				av[ac++] = arg_string(TRUSTED, "%s",
				    BINMAIL);
				av[ac++] = arg_string(UNTRUSTED, "%s",
				    user);
				Free(mail_zonename);
			}

			free(user);
		}
		break;
	}

	av[ac++] = NULL;

	Fork2 ();
	/* only the child returns */

	/*
	 * Correctly set up the supplemental group list
	 * for proper file access (before execl the interface program)
	 */

	pwp = getpwuid(procuid);
	if (pwp == NULL) {
		note("getpwuid(%d) call failed\n", procuid);
	} else if (initgroups(pwp->pw_name, procgid) < 0) {
		note("initgroups() call failed %d\n", errno);
	}
	
	setgid (procgid);
	setuid (procuid);

	/*
	 * The shell doesn't allow the "trap" builtin to set a trap
	 * for a signal ignored when the shell is started. Thus, don't
	 * turn off signals in the last child!
	 */

#ifdef DEBUG
	for (i = 0; av[i] != NULL; i++)
		note("exec(%s): av[%d] = %s", _exec_name(type), i, av[i]);
	for (i = 0; envp[i] != NULL; i++)
		note("exec(%s): envp[%d] = %s", _exec_name(type), i, envp[i]);
#endif

	execvpe(av[0], av, envp);
	Done (EXEC_EXIT_NEXEC, errno);
	/*NOTREACHED*/
	return (0);
}

/**
 ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT
 **/

static void
addenv(char ***envp, char *name, char *value)
{
	register char *		cp;

	if ((name == NULL) || (value == NULL))
		return;

	if ((cp = makestr(name, "=", value, (char *)0)))
		addlist(envp, cp);
	return;
}

/**
 ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT
 **/

static int
Fork1(EXEC *ep)
{
	int			pid;
	int			fds[2];

	if (pipe(fds) == -1) {
		note("Failed to create pipe for child process (%s).\n", PERROR);
		errno = EAGAIN ;
		return(-1);
	}

	ep->md = mconnect((char *)0, fds[0], fds[1]);

	switch (pid = fork()) {

	case -1:
		mdisconnect(ep->md);
		close(fds[0]);
		close(fds[1]);
		ep->md = 0;
		return (-1);
	
	case 0:
		ChildMd = mconnect(NULL, fds[1], fds[1]);
		return (0);

	default:
		mlistenadd(ep->md, POLLIN);
		return (pid);
	}
}

/**
 ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT
 **/

static void
Fork2(void)
{
	switch ((ChildPid = fork())) {

	case -1:
		Done (EXEC_EXIT_NFORK, errno);
		/*NOTREACHED*/
					
	case 0:
		return;
					
	default:
		/*
		 * Delay calling "ignore_fault_signals()" as long
		 * as possible, to give the child a chance to exec
		 * the interface program and turn on traps.
		 */

		cool_heels ();
		/*NOTREACHED*/

	}
}


/**
 ** cool_heels() - WAIT FOR CHILD TO "DIE"
 **/

static void
cool_heels(void)
{
	int			status;

	/*
	 * At this point our only job is to wait for the child process.
	 * If we hang out for a bit longer, that's okay.
	 * By delaying before turning off the fault signals,
	 * we increase the chance that the child process has completed
	 * its exec and has turned on the fault traps. Nonetheless,
	 * we can't guarantee a zero chance of missing a fault.
	 * (We don't want to keep trapping the signals because the
	 * interface program is likely to have a better way to handle
	 * them; this process provides only rudimentary handling.)
	 *
	 * Note that on a very busy system, or with a very fast interface
	 * program, the tables could be turned: Our sleep below (coupled
	 * with a delay in the kernel scheduling us) may cause us to
	 * detect the fault instead of the interface program.
	 *
	 * What we need is a way to synchronize with the child process.
	 */
	sleep (1);
	ignore_fault_signals ();

	WaitedChildPid = 0;
	while ((WaitedChildPid = wait(&status)) != ChildPid)
		;
			
	if (
		EXITED(status) > EXEC_EXIT_USER
	     && EXITED(status) != EXEC_EXIT_FAULT
	)
		Done (EXEC_EXIT_EXIT, EXITED(status));

	done (status, 0);	/* Don't use Done() */
	/*NOTREACHED*/
}


/**
 ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT
 ** ignore_fault_signals() - IGNORE SAME
 **/

static void
trap_fault_signals(void)
{
	signal (SIGHUP, sigtrap);
	signal (SIGINT, sigtrap);
	signal (SIGQUIT, sigtrap);
	signal (SIGPIPE, sigtrap);
	return;
}

static void
ignore_fault_signals(void)
{
	signal (SIGHUP, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
	signal (SIGPIPE, SIG_IGN);
	return;
}

/**
 ** sigtrap() - TRAP VARIOUS SIGNALS
 **/

static void
sigtrap(int sig)
{
	signal (sig, SIG_IGN);
	switch (sig) {

	case SIGHUP:
		Done (EXEC_EXIT_HUP, 0);
		/*NOTREACHED*/

	case SIGQUIT:
	case SIGINT:
		Done (EXEC_EXIT_INTR, 0);
		/*NOTREACHED*/

	case SIGPIPE:
		Done (EXEC_EXIT_PIPE, 0);
		/*NOTREACHED*/

	case SIGTERM:
		/*
		 * If we were killed with SIGTERM, it should have been
		 * via the Spooler who should have killed the entire
		 * process group. We have to wait for the children,
		 * since we're their parent, but WE MAY HAVE WAITED
		 * FOR THEM ALREADY (in cool_heels()).
		 */
		if (ChildPid != WaitedChildPid) {
			register int		cpid;

			while (
				(cpid = wait((int *)0)) != ChildPid
			     && (cpid != -1 || errno != ECHILD)
			)
				;
		}

		/*
		 * We can't rely on getting SIGTERM back in the wait()
		 * above, because, for instance, some shells trap SIGTERM
		 * and exit instead. Thus we force it.
		 */
		done (SIGTERM, 0);	/* Don't use Done() */
		/*NOTREACHED*/
	}
}

/**
 ** done() - TELL SPOOLER THIS CHILD IS DONE
 **/

static void
done(int status, int err)
{
	if (do_undial)
		undial (1);

	mputm (ChildMd, S_CHILD_DONE, key, status, err);
	mdisconnect (ChildMd);

	exit (0);
	/*NOTREACHED*/
}

/**
 ** child_mallocfail()
 **/

static void
child_mallocfail(void)
{
	Done (EXEC_EXIT_NOMEM, ENOMEM);
}