xref: /csrg-svn/usr.sbin/lpr/lpd/printjob.c (revision 56123)
122437Sdist /*
222437Sdist  * Copyright (c) 1983 Regents of the University of California.
334203Sbostic  * All rights reserved.
434203Sbostic  *
5*56123Selan  * Redistribution and use in source and binary forms, with or without
6*56123Selan  * modification, are permitted provided that the following conditions
7*56123Selan  * are met:
8*56123Selan  * 1. Redistributions of source code must retain the above copyright
9*56123Selan  *    notice, this list of conditions and the following disclaimer.
10*56123Selan  * 2. Redistributions in binary form must reproduce the above copyright
11*56123Selan  *    notice, this list of conditions and the following disclaimer in the
12*56123Selan  *    documentation and/or other materials provided with the distribution.
13*56123Selan  * 3. All advertising materials mentioning features or use of this software
14*56123Selan  *    must display the following acknowledgement:
15*56123Selan  *	This product includes software developed by the University of
16*56123Selan  *	California, Berkeley and its contributors.
17*56123Selan  * 4. Neither the name of the University nor the names of its contributors
18*56123Selan  *    may be used to endorse or promote products derived from this software
19*56123Selan  *    without specific prior written permission.
20*56123Selan  *
21*56123Selan  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22*56123Selan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23*56123Selan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24*56123Selan  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25*56123Selan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26*56123Selan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27*56123Selan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28*56123Selan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29*56123Selan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30*56123Selan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*56123Selan  * SUCH DAMAGE.
3222437Sdist  */
3322437Sdist 
3413954Ssam #ifndef lint
35*56123Selan static char sccsid[] = "@(#)printjob.c	5.16 (Berkeley) 8/6/92";
3634203Sbostic #endif /* not lint */
3713954Ssam 
3812111Sralph /*
3912111Sralph  * printjob -- print jobs in the queue.
4012111Sralph  *
4112111Sralph  *	NOTE: the lock file is used to pass information to lpq and lprm.
4212111Sralph  *	it does not need to be removed because file locks are dynamic.
4312111Sralph  */
4412111Sralph 
4555474Sbostic #include <sys/param.h>
4655474Sbostic #include <sys/wait.h>
4755474Sbostic #include <sys/stat.h>
48*56123Selan #include <sys/types.h>
4955474Sbostic 
50*56123Selan #include <pwd.h>
51*56123Selan #include <unistd.h>
5255474Sbostic #include <signal.h>
5355474Sbostic #include <sgtty.h>
5455474Sbostic #include <syslog.h>
5555474Sbostic #include <fcntl.h>
5655474Sbostic #include <dirent.h>
5755474Sbostic #include <errno.h>
5855474Sbostic #include <stdio.h>
5955474Sbostic #include <string.h>
60*56123Selan #include <stdlib.h>
6112111Sralph #include "lp.h"
6255474Sbostic #include "lp.local.h"
6337968Sbostic #include "pathnames.h"
6455474Sbostic #include "extern.h"
6512111Sralph 
6616762Sralph #define DORETURN	0	/* absorb fork error */
6716762Sralph #define DOABORT		1	/* abort if dofork fails */
6812111Sralph 
6917463Sralph /*
7017463Sralph  * Error tokens
7117463Sralph  */
7217463Sralph #define REPRINT		-2
7317463Sralph #define ERROR		-1
7417463Sralph #define	OK		0
7517463Sralph #define	FATALERR	1
7617463Sralph #define	NOACCT		2
7717463Sralph #define	FILTERERR	3
7817463Sralph #define	ACCESS		4
7917463Sralph 
80*56123Selan static dev_t	 fdev;		/* device of file pointed to by symlink */
81*56123Selan static ino_t	 fino;		/* inode of file pointed to by symlink */
82*56123Selan static FILE	*cfp;		/* control file */
83*56123Selan static int	 child;		/* id of any filters */
84*56123Selan static int	 lfd;		/* lock file descriptor */
85*56123Selan static int	 ofd;		/* output filter file descriptor */
86*56123Selan static int	 ofilter;	/* id of output filter, if any */
87*56123Selan static int	 pfd;		/* prstatic inter file descriptor */
88*56123Selan static int	 pid;		/* pid of lpd process */
89*56123Selan static int	 prchild;	/* id of pr process */
90*56123Selan static int	 remote;	/* true if sending files to remote */
91*56123Selan static char	 title[80];	/* ``pr'' title */
92*56123Selan static int	 tof;		/* true if at top of form */
9312111Sralph 
94*56123Selan static char	class[32];		/* classification field */
95*56123Selan static char	fromhost[32];		/* user's host machine */
96*56123Selan 				/* indentation size in static characters */
97*56123Selan static char	indent[10] = "-i0";
98*56123Selan static char	jobname[100];		/* job or file name */
99*56123Selan static char	length[10] = "-l";	/* page length in lines */
100*56123Selan static char	logname[32];		/* user's login name */
101*56123Selan static char	pxlength[10] = "-y";	/* page length in pixels */
102*56123Selan static char	pxwidth[10] = "-x";	/* page width in pixels */
103*56123Selan static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
104*56123Selan static char	width[10] = "-w";	/* page width in static characters */
10512111Sralph 
10655474Sbostic static void       abortpr __P((int));
10755474Sbostic static void       banner __P((char *, char *));
10855474Sbostic static int        dofork __P((int));
10955474Sbostic static int        dropit __P((int));
11055474Sbostic static void       init __P((void));
11155474Sbostic static void       openpr __P((void));
11255474Sbostic static int        print __P((int, char *));
11355474Sbostic static int        printit __P((char *));
11455474Sbostic static void       pstatus __P((const char *, ...));
11555474Sbostic static char       response __P((void));
11655474Sbostic static void       scan_out __P((int, char *, int));
11755474Sbostic static char      *scnline __P((int, char *, int));
11855474Sbostic static int        sendfile __P((int, char *));
11955474Sbostic static int        sendit __P((char *));
12055474Sbostic static void       sendmail __P((char *, int));
12155474Sbostic static void       setty __P((void));
12255474Sbostic 
12355474Sbostic void
12412111Sralph printjob()
12512111Sralph {
12612111Sralph 	struct stat stb;
12712111Sralph 	register struct queue *q, **qp;
12812111Sralph 	struct queue **queue;
12912111Sralph 	register int i, nitems;
13012111Sralph 	long pidoff;
13116762Sralph 	int count = 0;
13212111Sralph 
13312111Sralph 	init();					/* set up capabilities */
13413442Sralph 	(void) write(1, "", 1);			/* ack that daemon is started */
13525496Seric 	(void) close(2);			/* set up log file */
13625496Seric 	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
13725496Seric 		syslog(LOG_ERR, "%s: %m", LF);
13837968Sbostic 		(void) open(_PATH_DEVNULL, O_WRONLY);
13925496Seric 	}
14016762Sralph 	setgid(getegid());
14112463Sralph 	pid = getpid();				/* for use with lprm */
14212111Sralph 	setpgrp(0, pid);
14316762Sralph 	signal(SIGHUP, abortpr);
14416762Sralph 	signal(SIGINT, abortpr);
14516762Sralph 	signal(SIGQUIT, abortpr);
14616762Sralph 	signal(SIGTERM, abortpr);
14712111Sralph 
14839954Smckusick 	(void) mktemp(tempfile);
14915811Sralph 
15012111Sralph 	/*
15112111Sralph 	 * uses short form file names
15212111Sralph 	 */
15312111Sralph 	if (chdir(SD) < 0) {
15416762Sralph 		syslog(LOG_ERR, "%s: %m", SD);
15512111Sralph 		exit(1);
15612111Sralph 	}
15712463Sralph 	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
15812463Sralph 		exit(0);		/* printing disabled */
15914150Sralph 	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
16013169Sralph 	if (lfd < 0) {
16116762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
16213169Sralph 		exit(1);
16313169Sralph 	}
16413169Sralph 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
16512111Sralph 		if (errno == EWOULDBLOCK)	/* active deamon present */
16612111Sralph 			exit(0);
16716762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
16812111Sralph 		exit(1);
16912111Sralph 	}
17013148Ssam 	ftruncate(lfd, 0);
17112111Sralph 	/*
17212111Sralph 	 * write process id for others to know
17312111Sralph 	 */
17412111Sralph 	sprintf(line, "%u\n", pid);
17512111Sralph 	pidoff = i = strlen(line);
17612463Sralph 	if (write(lfd, line, i) != i) {
17716762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
17812111Sralph 		exit(1);
17912111Sralph 	}
18012111Sralph 	/*
18112111Sralph 	 * search the spool directory for work and sort by queue order.
18212111Sralph 	 */
18312111Sralph 	if ((nitems = getq(&queue)) < 0) {
18416762Sralph 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
18512111Sralph 		exit(1);
18612111Sralph 	}
18712463Sralph 	if (nitems == 0)		/* no work to do */
18812111Sralph 		exit(0);
18913169Sralph 	if (stb.st_mode & 01) {		/* reset queue flag */
19013169Sralph 		if (fchmod(lfd, stb.st_mode & 0776) < 0)
19116762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
19213169Sralph 	}
19312463Sralph 	openpr();			/* open printer or remote */
19412463Sralph again:
19512111Sralph 	/*
19612111Sralph 	 * we found something to do now do it --
19712111Sralph 	 *    write the name of the current control file into the lock file
19812111Sralph 	 *    so the spool queue program can tell what we're working on
19912111Sralph 	 */
20012111Sralph 	for (qp = queue; nitems--; free((char *) q)) {
20112111Sralph 		q = *qp++;
20212111Sralph 		if (stat(q->q_name, &stb) < 0)
20312111Sralph 			continue;
20412463Sralph 	restart:
20555474Sbostic 		(void) lseek(lfd, (off_t)pidoff, 0);
20612111Sralph 		(void) sprintf(line, "%s\n", q->q_name);
20712111Sralph 		i = strlen(line);
20812111Sralph 		if (write(lfd, line, i) != i)
20916762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
21012111Sralph 		if (!remote)
21112111Sralph 			i = printit(q->q_name);
21212111Sralph 		else
21312111Sralph 			i = sendit(q->q_name);
21412463Sralph 		/*
21513169Sralph 		 * Check to see if we are supposed to stop printing or
21613169Sralph 		 * if we are to rebuild the queue.
21712463Sralph 		 */
21813169Sralph 		if (fstat(lfd, &stb) == 0) {
21916762Sralph 			/* stop printing before starting next job? */
22013169Sralph 			if (stb.st_mode & 0100)
22113169Sralph 				goto done;
22216762Sralph 			/* rebuild queue (after lpc topq) */
22313169Sralph 			if (stb.st_mode & 01) {
22413169Sralph 				for (free((char *) q); nitems--; free((char *) q))
22513169Sralph 					q = *qp++;
22613169Sralph 				if (fchmod(lfd, stb.st_mode & 0776) < 0)
22716762Sralph 					syslog(LOG_WARNING, "%s: %s: %m",
22816762Sralph 						printer, LO);
22913169Sralph 				break;
23013169Sralph 			}
23113169Sralph 		}
23217463Sralph 		if (i == OK)		/* file ok and printed */
23314150Sralph 			count++;
23417463Sralph 		else if (i == REPRINT) { /* try reprinting the job */
23516762Sralph 			syslog(LOG_INFO, "restarting %s", printer);
23612111Sralph 			if (ofilter > 0) {
23712111Sralph 				kill(ofilter, SIGCONT);	/* to be sure */
23812111Sralph 				(void) close(ofd);
23912111Sralph 				while ((i = wait(0)) > 0 && i != ofilter)
24012111Sralph 					;
24112111Sralph 				ofilter = 0;
24212111Sralph 			}
24312463Sralph 			(void) close(pfd);	/* close printer */
24415811Sralph 			if (ftruncate(lfd, pidoff) < 0)
24516762Sralph 				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
24612463Sralph 			openpr();		/* try to reopen printer */
24712111Sralph 			goto restart;
24812111Sralph 		}
24912111Sralph 	}
25012111Sralph 	free((char *) queue);
25112463Sralph 	/*
25212463Sralph 	 * search the spool directory for more work.
25312463Sralph 	 */
25412463Sralph 	if ((nitems = getq(&queue)) < 0) {
25516762Sralph 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
25612463Sralph 		exit(1);
25712463Sralph 	}
25812463Sralph 	if (nitems == 0) {		/* no more work to do */
25912463Sralph 	done:
26014150Sralph 		if (count > 0) {	/* Files actually printed */
26114150Sralph 			if (!SF && !tof)
26214150Sralph 				(void) write(ofd, FF, strlen(FF));
26314150Sralph 			if (TR != NULL)		/* output trailer */
26414150Sralph 				(void) write(ofd, TR, strlen(TR));
26514150Sralph 		}
26639954Smckusick 		(void) unlink(tempfile);
26712463Sralph 		exit(0);
26812463Sralph 	}
26912111Sralph 	goto again;
27012111Sralph }
27112111Sralph 
27212111Sralph char	fonts[4][50];	/* fonts for troff */
27312111Sralph 
27437968Sbostic char ifonts[4][40] = {
27537968Sbostic 	_PATH_VFONTR,
27637968Sbostic 	_PATH_VFONTI,
27737968Sbostic 	_PATH_VFONTB,
27837968Sbostic 	_PATH_VFONTS,
27912111Sralph };
28012111Sralph 
28112111Sralph /*
28212111Sralph  * The remaining part is the reading of the control file (cf)
28312111Sralph  * and performing the various actions.
28412111Sralph  */
28555474Sbostic static int
28612111Sralph printit(file)
28712111Sralph 	char *file;
28812111Sralph {
28912111Sralph 	register int i;
29017463Sralph 	char *cp;
29117463Sralph 	int bombed = OK;
29212111Sralph 
29312111Sralph 	/*
29417463Sralph 	 * open control file; ignore if no longer there.
29512111Sralph 	 */
29612111Sralph 	if ((cfp = fopen(file, "r")) == NULL) {
29716762Sralph 		syslog(LOG_INFO, "%s: %s: %m", printer, file);
29817463Sralph 		return(OK);
29912111Sralph 	}
30012111Sralph 	/*
30112111Sralph 	 * Reset troff fonts.
30212111Sralph 	 */
30312111Sralph 	for (i = 0; i < 4; i++)
30412111Sralph 		strcpy(fonts[i], ifonts[i]);
30517339Sralph 	strcpy(width+2, "0");
30617302Sralph 	strcpy(indent+2, "0");
30712111Sralph 
30812111Sralph 	/*
30912111Sralph 	 *      read the control file for work to do
31012111Sralph 	 *
31112111Sralph 	 *      file format -- first character in the line is a command
31212111Sralph 	 *      rest of the line is the argument.
31312111Sralph 	 *      valid commands are:
31412111Sralph 	 *
31517463Sralph 	 *		S -- "stat info" for symbolic link protection
31612111Sralph 	 *		J -- "job name" on banner page
31712111Sralph 	 *		C -- "class name" on banner page
31812111Sralph 	 *              L -- "literal" user's name to print on banner
31912111Sralph 	 *		T -- "title" for pr
32012111Sralph 	 *		H -- "host name" of machine where lpr was done
32112111Sralph 	 *              P -- "person" user's login name
32212581Sralph 	 *              I -- "indent" amount to indent output
32312111Sralph 	 *              f -- "file name" name of text file to print
32412111Sralph 	 *		l -- "file name" text file with control chars
32512111Sralph 	 *		p -- "file name" text file to print with pr(1)
32612111Sralph 	 *		t -- "file name" troff(1) file to print
32713233Sralph 	 *		n -- "file name" ditroff(1) file to print
32812111Sralph 	 *		d -- "file name" dvi file to print
32912111Sralph 	 *		g -- "file name" plot(1G) file to print
33012111Sralph 	 *		v -- "file name" plain raster file to print
33112111Sralph 	 *		c -- "file name" cifplot file to print
33212111Sralph 	 *		1 -- "R font file" for troff
33312111Sralph 	 *		2 -- "I font file" for troff
33412111Sralph 	 *		3 -- "B font file" for troff
33512111Sralph 	 *		4 -- "S font file" for troff
33612111Sralph 	 *		N -- "name" of file (used by lpq)
33712111Sralph 	 *              U -- "unlink" name of file to remove
33812111Sralph 	 *                    (after we print it. (Pass 2 only)).
33912111Sralph 	 *		M -- "mail" to user when done printing
34012111Sralph 	 *
34112111Sralph 	 *      getline reads a line and expands tabs to blanks
34212111Sralph 	 */
34312111Sralph 
34412111Sralph 	/* pass 1 */
34512111Sralph 
34612111Sralph 	while (getline(cfp))
34712111Sralph 		switch (line[0]) {
34812111Sralph 		case 'H':
34914150Sralph 			strcpy(fromhost, line+1);
35012111Sralph 			if (class[0] == '\0')
35115552Sralph 				strncpy(class, line+1, sizeof(class)-1);
35212111Sralph 			continue;
35312111Sralph 
35412111Sralph 		case 'P':
35515552Sralph 			strncpy(logname, line+1, sizeof(logname)-1);
35612463Sralph 			if (RS) {			/* restricted */
35755474Sbostic 				if (getpwnam(logname) == NULL) {
35817463Sralph 					bombed = NOACCT;
35915811Sralph 					sendmail(line+1, bombed);
36012463Sralph 					goto pass2;
36112463Sralph 				}
36212463Sralph 			}
36312111Sralph 			continue;
36412111Sralph 
36517463Sralph 		case 'S':
36617463Sralph 			cp = line+1;
36717463Sralph 			i = 0;
36817463Sralph 			while (*cp >= '0' && *cp <= '9')
36917463Sralph 				i = i * 10 + (*cp++ - '0');
37017463Sralph 			fdev = i;
37117463Sralph 			cp++;
37217463Sralph 			i = 0;
37317463Sralph 			while (*cp >= '0' && *cp <= '9')
37417463Sralph 				i = i * 10 + (*cp++ - '0');
37517463Sralph 			fino = i;
37617463Sralph 			continue;
37717463Sralph 
37812111Sralph 		case 'J':
37912111Sralph 			if (line[1] != '\0')
38015552Sralph 				strncpy(jobname, line+1, sizeof(jobname)-1);
38112111Sralph 			else
38212111Sralph 				strcpy(jobname, " ");
38312111Sralph 			continue;
38412111Sralph 
38512111Sralph 		case 'C':
38612111Sralph 			if (line[1] != '\0')
38715552Sralph 				strncpy(class, line+1, sizeof(class)-1);
38812111Sralph 			else if (class[0] == '\0')
38915811Sralph 				gethostname(class, sizeof(class));
39012111Sralph 			continue;
39112111Sralph 
39212111Sralph 		case 'T':	/* header title for pr */
39315552Sralph 			strncpy(title, line+1, sizeof(title)-1);
39412111Sralph 			continue;
39512111Sralph 
39612111Sralph 		case 'L':	/* identification line */
39718127Sralph 			if (!SH && !HL)
39812111Sralph 				banner(line+1, jobname);
39912111Sralph 			continue;
40012111Sralph 
40112111Sralph 		case '1':	/* troff fonts */
40212111Sralph 		case '2':
40312111Sralph 		case '3':
40412111Sralph 		case '4':
40512111Sralph 			if (line[1] != '\0')
40612111Sralph 				strcpy(fonts[line[0]-'1'], line+1);
40712111Sralph 			continue;
40812111Sralph 
40912111Sralph 		case 'W':	/* page width */
41015552Sralph 			strncpy(width+2, line+1, sizeof(width)-3);
41112111Sralph 			continue;
41212111Sralph 
41312581Sralph 		case 'I':	/* indent amount */
41415552Sralph 			strncpy(indent+2, line+1, sizeof(indent)-3);
41512581Sralph 			continue;
41612581Sralph 
41712111Sralph 		default:	/* some file to print */
41815811Sralph 			switch (i = print(line[0], line+1)) {
41917463Sralph 			case ERROR:
42017463Sralph 				if (bombed == OK)
42117463Sralph 					bombed = FATALERR;
42215811Sralph 				break;
42317463Sralph 			case REPRINT:
42412111Sralph 				(void) fclose(cfp);
42517463Sralph 				return(REPRINT);
42617463Sralph 			case FILTERERR:
42717463Sralph 			case ACCESS:
42817463Sralph 				bombed = i;
42915811Sralph 				sendmail(logname, bombed);
43015811Sralph 			}
43112111Sralph 			title[0] = '\0';
43212111Sralph 			continue;
43312111Sralph 
43412111Sralph 		case 'N':
43512111Sralph 		case 'U':
43612111Sralph 		case 'M':
43712111Sralph 			continue;
43812111Sralph 		}
43912111Sralph 
44012111Sralph 	/* pass 2 */
44112111Sralph 
44212463Sralph pass2:
44312111Sralph 	fseek(cfp, 0L, 0);
44412111Sralph 	while (getline(cfp))
44512111Sralph 		switch (line[0]) {
44618127Sralph 		case 'L':	/* identification line */
44718127Sralph 			if (!SH && HL)
44818127Sralph 				banner(line+1, jobname);
44918127Sralph 			continue;
45018127Sralph 
45112111Sralph 		case 'M':
45217463Sralph 			if (bombed < NOACCT)	/* already sent if >= NOACCT */
45315811Sralph 				sendmail(line+1, bombed);
45412111Sralph 			continue;
45512111Sralph 
45612111Sralph 		case 'U':
45712111Sralph 			(void) unlink(line+1);
45812111Sralph 		}
45912111Sralph 	/*
46015811Sralph 	 * clean-up in case another control file exists
46112111Sralph 	 */
46212111Sralph 	(void) fclose(cfp);
46312111Sralph 	(void) unlink(file);
46417463Sralph 	return(bombed == OK ? OK : ERROR);
46512111Sralph }
46612111Sralph 
46712111Sralph /*
46812111Sralph  * Print a file.
46913233Sralph  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
47015811Sralph  * Return -1 if a non-recoverable error occured,
47115811Sralph  * 2 if the filter detected some errors (but printed the job anyway),
47215811Sralph  * 1 if we should try to reprint this job and
47312111Sralph  * 0 if all is well.
47412111Sralph  * Note: all filters take stdin as the file, stdout as the printer,
47512111Sralph  * stderr as the log file, and must not ignore SIGINT.
47612111Sralph  */
47755474Sbostic static int
47812111Sralph print(format, file)
47912111Sralph 	int format;
48012111Sralph 	char *file;
48112111Sralph {
48215811Sralph 	register int n;
48312111Sralph 	register char *prog;
48415811Sralph 	int fi, fo;
48539954Smckusick 	FILE *fp;
48612111Sralph 	char *av[15], buf[BUFSIZ];
48712111Sralph 	int pid, p[2], stopped = 0;
48812111Sralph 	union wait status;
48917463Sralph 	struct stat stb;
49012111Sralph 
49117463Sralph 	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
49217463Sralph 		return(ERROR);
49317463Sralph 	/*
49417463Sralph 	 * Check to see if data file is a symbolic link. If so, it should
49517463Sralph 	 * still point to the same file or someone is trying to print
49617463Sralph 	 * something he shouldn't.
49717463Sralph 	 */
49817463Sralph 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
49917463Sralph 	    (stb.st_dev != fdev || stb.st_ino != fino))
50017463Sralph 		return(ACCESS);
50112111Sralph 	if (!SF && !tof) {		/* start on a fresh page */
50212111Sralph 		(void) write(ofd, FF, strlen(FF));
50312111Sralph 		tof = 1;
50412111Sralph 	}
50512111Sralph 	if (IF == NULL && (format == 'f' || format == 'l')) {
50612111Sralph 		tof = 0;
50712111Sralph 		while ((n = read(fi, buf, BUFSIZ)) > 0)
50812111Sralph 			if (write(ofd, buf, n) != n) {
50912111Sralph 				(void) close(fi);
51017463Sralph 				return(REPRINT);
51112111Sralph 			}
51212111Sralph 		(void) close(fi);
51317463Sralph 		return(OK);
51412111Sralph 	}
51512111Sralph 	switch (format) {
51612111Sralph 	case 'p':	/* print file using 'pr' */
51712111Sralph 		if (IF == NULL) {	/* use output filter */
51837968Sbostic 			prog = _PATH_PR;
51912111Sralph 			av[0] = "pr";
52012111Sralph 			av[1] = width;
52112111Sralph 			av[2] = length;
52212111Sralph 			av[3] = "-h";
52312111Sralph 			av[4] = *title ? title : " ";
52412111Sralph 			av[5] = 0;
52512111Sralph 			fo = ofd;
52612111Sralph 			goto start;
52712111Sralph 		}
52812111Sralph 		pipe(p);
52912111Sralph 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
53012111Sralph 			dup2(fi, 0);		/* file is stdin */
53112111Sralph 			dup2(p[1], 1);		/* pipe is stdout */
53212111Sralph 			for (n = 3; n < NOFILE; n++)
53312111Sralph 				(void) close(n);
53437968Sbostic 			execl(_PATH_PR, "pr", width, length,
53537968Sbostic 			    "-h", *title ? title : " ", 0);
53637968Sbostic 			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
53712111Sralph 			exit(2);
53812111Sralph 		}
53912111Sralph 		(void) close(p[1]);		/* close output side */
54012111Sralph 		(void) close(fi);
54112111Sralph 		if (prchild < 0) {
54212111Sralph 			prchild = 0;
54312111Sralph 			(void) close(p[0]);
54417463Sralph 			return(ERROR);
54512111Sralph 		}
54612111Sralph 		fi = p[0];			/* use pipe for input */
54712111Sralph 	case 'f':	/* print plain text file */
54812111Sralph 		prog = IF;
54912111Sralph 		av[1] = width;
55012111Sralph 		av[2] = length;
55112581Sralph 		av[3] = indent;
55212581Sralph 		n = 4;
55312111Sralph 		break;
55412111Sralph 	case 'l':	/* like 'f' but pass control characters */
55512111Sralph 		prog = IF;
55614325Sralph 		av[1] = "-c";
55712111Sralph 		av[2] = width;
55812111Sralph 		av[3] = length;
55912581Sralph 		av[4] = indent;
56012581Sralph 		n = 5;
56112111Sralph 		break;
56212463Sralph 	case 'r':	/* print a fortran text file */
56312463Sralph 		prog = RF;
56412463Sralph 		av[1] = width;
56512463Sralph 		av[2] = length;
56612463Sralph 		n = 3;
56712463Sralph 		break;
56812111Sralph 	case 't':	/* print troff output */
56913233Sralph 	case 'n':	/* print ditroff output */
57012463Sralph 	case 'd':	/* print tex output */
57112111Sralph 		(void) unlink(".railmag");
57212463Sralph 		if ((fo = creat(".railmag", FILMOD)) < 0) {
57316762Sralph 			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
57412111Sralph 			(void) unlink(".railmag");
57512111Sralph 		} else {
57612111Sralph 			for (n = 0; n < 4; n++) {
57712111Sralph 				if (fonts[n][0] != '/')
57854520Sbostic 					(void) write(fo, _PATH_VFONT,
57954520Sbostic 					    sizeof(_PATH_VFONT) - 1);
58012111Sralph 				(void) write(fo, fonts[n], strlen(fonts[n]));
58112111Sralph 				(void) write(fo, "\n", 1);
58212111Sralph 			}
58312111Sralph 			(void) close(fo);
58412111Sralph 		}
58513233Sralph 		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
58612463Sralph 		av[1] = pxwidth;
58712463Sralph 		av[2] = pxlength;
58812463Sralph 		n = 3;
58912111Sralph 		break;
59012111Sralph 	case 'c':	/* print cifplot output */
59112111Sralph 		prog = CF;
59212463Sralph 		av[1] = pxwidth;
59312463Sralph 		av[2] = pxlength;
59412463Sralph 		n = 3;
59512111Sralph 		break;
59612111Sralph 	case 'g':	/* print plot(1G) output */
59712111Sralph 		prog = GF;
59812463Sralph 		av[1] = pxwidth;
59912463Sralph 		av[2] = pxlength;
60012463Sralph 		n = 3;
60112111Sralph 		break;
60212111Sralph 	case 'v':	/* print raster output */
60312111Sralph 		prog = VF;
60412463Sralph 		av[1] = pxwidth;
60512463Sralph 		av[2] = pxlength;
60612463Sralph 		n = 3;
60712111Sralph 		break;
60812111Sralph 	default:
60912111Sralph 		(void) close(fi);
61016762Sralph 		syslog(LOG_ERR, "%s: illegal format character '%c'",
61116762Sralph 			printer, format);
61217463Sralph 		return(ERROR);
61312111Sralph 	}
61412111Sralph 	if ((av[0] = rindex(prog, '/')) != NULL)
61512111Sralph 		av[0]++;
61612111Sralph 	else
61712111Sralph 		av[0] = prog;
61812111Sralph 	av[n++] = "-n";
61912111Sralph 	av[n++] = logname;
62012111Sralph 	av[n++] = "-h";
62114150Sralph 	av[n++] = fromhost;
62212111Sralph 	av[n++] = AF;
62312111Sralph 	av[n] = 0;
62412111Sralph 	fo = pfd;
62512111Sralph 	if (ofilter > 0) {		/* stop output filter */
62612111Sralph 		write(ofd, "\031\1", 2);
62746912Sbostic 		while ((pid =
62846912Sbostic 		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
62912111Sralph 			;
63012111Sralph 		if (status.w_stopval != WSTOPPED) {
63112111Sralph 			(void) close(fi);
63216762Sralph 			syslog(LOG_WARNING, "%s: output filter died (%d)",
63316762Sralph 				printer, status.w_retcode);
63417463Sralph 			return(REPRINT);
63512111Sralph 		}
63612111Sralph 		stopped++;
63712111Sralph 	}
63812111Sralph start:
63912111Sralph 	if ((child = dofork(DORETURN)) == 0) {	/* child */
64012111Sralph 		dup2(fi, 0);
64112111Sralph 		dup2(fo, 1);
64239954Smckusick 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
64315811Sralph 		if (n >= 0)
64415811Sralph 			dup2(n, 2);
64512111Sralph 		for (n = 3; n < NOFILE; n++)
64612111Sralph 			(void) close(n);
64712111Sralph 		execv(prog, av);
64816762Sralph 		syslog(LOG_ERR, "cannot execv %s", prog);
64912111Sralph 		exit(2);
65012111Sralph 	}
65112111Sralph 	(void) close(fi);
65212111Sralph 	if (child < 0)
65312111Sralph 		status.w_retcode = 100;
65412111Sralph 	else
65546912Sbostic 		while ((pid = wait((int *)&status)) > 0 && pid != child)
65612111Sralph 			;
65712111Sralph 	child = 0;
65812111Sralph 	prchild = 0;
65912111Sralph 	if (stopped) {		/* restart output filter */
66012111Sralph 		if (kill(ofilter, SIGCONT) < 0) {
66116762Sralph 			syslog(LOG_ERR, "cannot restart output filter");
66212111Sralph 			exit(1);
66312111Sralph 		}
66412111Sralph 	}
66512111Sralph 	tof = 0;
66639954Smckusick 
66739954Smckusick 	/* Copy filter output to "lf" logfile */
66839954Smckusick 	if (fp = fopen(tempfile, "r")) {
66939954Smckusick 		while (fgets(buf, sizeof(buf), fp))
67039954Smckusick 			fputs(buf, stderr);
671*56123Selan 		fclose(fp);
67239954Smckusick 	}
67339954Smckusick 
67415811Sralph 	if (!WIFEXITED(status)) {
67516762Sralph 		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
67616762Sralph 			printer, format, status.w_termsig);
67717463Sralph 		return(ERROR);
67817463Sralph 	}
67917463Sralph 	switch (status.w_retcode) {
68017463Sralph 	case 0:
68117463Sralph 		tof = 1;
68217463Sralph 		return(OK);
68317463Sralph 	case 1:
68417463Sralph 		return(REPRINT);
68517463Sralph 	default:
68616762Sralph 		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
68716762Sralph 			printer, format, status.w_retcode);
68817463Sralph 	case 2:
68917463Sralph 		return(ERROR);
69017463Sralph 	}
69112111Sralph }
69212111Sralph 
69312111Sralph /*
69412111Sralph  * Send the daemon control file (cf) and any data files.
69512111Sralph  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
69612111Sralph  * 0 if all is well.
69712111Sralph  */
69855474Sbostic static int
69912111Sralph sendit(file)
70012111Sralph 	char *file;
70112111Sralph {
70217463Sralph 	register int i, err = OK;
70317463Sralph 	char *cp, last[BUFSIZ];
70412111Sralph 
70512111Sralph 	/*
70612111Sralph 	 * open control file
70712111Sralph 	 */
70816762Sralph 	if ((cfp = fopen(file, "r")) == NULL)
70917463Sralph 		return(OK);
71012111Sralph 	/*
71112111Sralph 	 *      read the control file for work to do
71212111Sralph 	 *
71312111Sralph 	 *      file format -- first character in the line is a command
71412111Sralph 	 *      rest of the line is the argument.
71512111Sralph 	 *      commands of interest are:
71612111Sralph 	 *
71712111Sralph 	 *            a-z -- "file name" name of file to print
71812111Sralph 	 *              U -- "unlink" name of file to remove
71912111Sralph 	 *                    (after we print it. (Pass 2 only)).
72012111Sralph 	 */
72112111Sralph 
72212111Sralph 	/*
72312111Sralph 	 * pass 1
72412111Sralph 	 */
72512111Sralph 	while (getline(cfp)) {
72612111Sralph 	again:
72717463Sralph 		if (line[0] == 'S') {
72817463Sralph 			cp = line+1;
72917463Sralph 			i = 0;
73017463Sralph 			while (*cp >= '0' && *cp <= '9')
73117463Sralph 				i = i * 10 + (*cp++ - '0');
73217463Sralph 			fdev = i;
73317463Sralph 			cp++;
73417463Sralph 			i = 0;
73517463Sralph 			while (*cp >= '0' && *cp <= '9')
73617463Sralph 				i = i * 10 + (*cp++ - '0');
73717463Sralph 			fino = i;
73817463Sralph 			continue;
73917463Sralph 		}
74012111Sralph 		if (line[0] >= 'a' && line[0] <= 'z') {
74112111Sralph 			strcpy(last, line);
74217463Sralph 			while (i = getline(cfp))
74312111Sralph 				if (strcmp(last, line))
74412111Sralph 					break;
74517463Sralph 			switch (sendfile('\3', last+1)) {
74617463Sralph 			case OK:
74717463Sralph 				if (i)
74817463Sralph 					goto again;
74917463Sralph 				break;
75017463Sralph 			case REPRINT:
75112111Sralph 				(void) fclose(cfp);
75217463Sralph 				return(REPRINT);
75317463Sralph 			case ACCESS:
75417463Sralph 				sendmail(logname, ACCESS);
75517463Sralph 			case ERROR:
75617463Sralph 				err = ERROR;
75717463Sralph 			}
75812111Sralph 			break;
75912111Sralph 		}
76012111Sralph 	}
76117463Sralph 	if (err == OK && sendfile('\2', file) > 0) {
76212111Sralph 		(void) fclose(cfp);
76317463Sralph 		return(REPRINT);
76412111Sralph 	}
76512111Sralph 	/*
76612111Sralph 	 * pass 2
76712111Sralph 	 */
76812111Sralph 	fseek(cfp, 0L, 0);
76912111Sralph 	while (getline(cfp))
77012111Sralph 		if (line[0] == 'U')
77112111Sralph 			(void) unlink(line+1);
77212111Sralph 	/*
77317463Sralph 	 * clean-up in case another control file exists
77412111Sralph 	 */
77512111Sralph 	(void) fclose(cfp);
77612111Sralph 	(void) unlink(file);
77717463Sralph 	return(err);
77812111Sralph }
77912111Sralph 
78012111Sralph /*
78112111Sralph  * Send a data file to the remote machine and spool it.
78212111Sralph  * Return positive if we should try resending.
78312111Sralph  */
78455474Sbostic static int
78512111Sralph sendfile(type, file)
78655474Sbostic 	int type;
78755474Sbostic 	char *file;
78812111Sralph {
78912111Sralph 	register int f, i, amt;
79012111Sralph 	struct stat stb;
79112111Sralph 	char buf[BUFSIZ];
79216762Sralph 	int sizerr, resp;
79312111Sralph 
79417463Sralph 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
79517463Sralph 		return(ERROR);
79617463Sralph 	/*
79717463Sralph 	 * Check to see if data file is a symbolic link. If so, it should
79817463Sralph 	 * still point to the same file or someone is trying to print something
79917463Sralph 	 * he shouldn't.
80017463Sralph 	 */
80117463Sralph 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
80217463Sralph 	    (stb.st_dev != fdev || stb.st_ino != fino))
80317463Sralph 		return(ACCESS);
80455474Sbostic 	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
80512111Sralph 	amt = strlen(buf);
80616762Sralph 	for (i = 0;  ; i++) {
80716762Sralph 		if (write(pfd, buf, amt) != amt ||
80816762Sralph 		    (resp = response()) < 0 || resp == '\1') {
80916762Sralph 			(void) close(f);
81017463Sralph 			return(REPRINT);
81116762Sralph 		} else if (resp == '\0')
81216762Sralph 			break;
81316762Sralph 		if (i == 0)
81455474Sbostic 			pstatus("no space on remote; waiting for queue to drain");
81516762Sralph 		if (i == 10)
81624861Seric 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
81716762Sralph 				printer, RM);
81816762Sralph 		sleep(5 * 60);
81912692Sralph 	}
82016762Sralph 	if (i)
82155474Sbostic 		pstatus("sending to %s", RM);
82212111Sralph 	sizerr = 0;
82312111Sralph 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
82412111Sralph 		amt = BUFSIZ;
82512111Sralph 		if (i + amt > stb.st_size)
82612111Sralph 			amt = stb.st_size - i;
82712111Sralph 		if (sizerr == 0 && read(f, buf, amt) != amt)
82812111Sralph 			sizerr = 1;
82912692Sralph 		if (write(pfd, buf, amt) != amt) {
83012692Sralph 			(void) close(f);
83117463Sralph 			return(REPRINT);
83212692Sralph 		}
83312111Sralph 	}
83455474Sbostic 
83555474Sbostic 
83655474Sbostic 
83755474Sbostic 
83812111Sralph 	(void) close(f);
83912111Sralph 	if (sizerr) {
84016762Sralph 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
84117463Sralph 		/* tell recvjob to ignore this file */
84217463Sralph 		(void) write(pfd, "\1", 1);
84317463Sralph 		return(ERROR);
84417463Sralph 	}
84517463Sralph 	if (write(pfd, "", 1) != 1 || response())
84617463Sralph 		return(REPRINT);
84717463Sralph 	return(OK);
84812111Sralph }
84912111Sralph 
85012111Sralph /*
85112111Sralph  * Check to make sure there have been no errors and that both programs
85212111Sralph  * are in sync with eachother.
85312111Sralph  * Return non-zero if the connection was lost.
85412111Sralph  */
85555474Sbostic static char
85616762Sralph response()
85712111Sralph {
85812111Sralph 	char resp;
85912111Sralph 
86016762Sralph 	if (read(pfd, &resp, 1) != 1) {
86116762Sralph 		syslog(LOG_INFO, "%s: lost connection", printer);
86216762Sralph 		return(-1);
86312111Sralph 	}
86416762Sralph 	return(resp);
86512111Sralph }
86612111Sralph 
86712111Sralph /*
86812111Sralph  * Banner printing stuff
86912111Sralph  */
87055474Sbostic static void
87112111Sralph banner(name1, name2)
87212111Sralph 	char *name1, *name2;
87312111Sralph {
87412111Sralph 	time_t tvec;
87512111Sralph 	extern char *ctime();
87612111Sralph 
87712111Sralph 	time(&tvec);
87812111Sralph 	if (!SF && !tof)
87912111Sralph 		(void) write(ofd, FF, strlen(FF));
88012111Sralph 	if (SB) {	/* short banner only */
88112111Sralph 		if (class[0]) {
88212111Sralph 			(void) write(ofd, class, strlen(class));
88312111Sralph 			(void) write(ofd, ":", 1);
88412111Sralph 		}
88512111Sralph 		(void) write(ofd, name1, strlen(name1));
88612111Sralph 		(void) write(ofd, "  Job: ", 7);
88712111Sralph 		(void) write(ofd, name2, strlen(name2));
88812111Sralph 		(void) write(ofd, "  Date: ", 8);
88912111Sralph 		(void) write(ofd, ctime(&tvec), 24);
89012111Sralph 		(void) write(ofd, "\n", 1);
89112111Sralph 	} else {	/* normal banner */
89212111Sralph 		(void) write(ofd, "\n\n\n", 3);
89312111Sralph 		scan_out(ofd, name1, '\0');
89412111Sralph 		(void) write(ofd, "\n\n", 2);
89512111Sralph 		scan_out(ofd, name2, '\0');
89612111Sralph 		if (class[0]) {
89712111Sralph 			(void) write(ofd,"\n\n\n",3);
89812111Sralph 			scan_out(ofd, class, '\0');
89912111Sralph 		}
90012111Sralph 		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
90112111Sralph 		(void) write(ofd, name2, strlen(name2));
90212111Sralph 		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
90312111Sralph 		(void) write(ofd, ctime(&tvec), 24);
90412111Sralph 		(void) write(ofd, "\n", 1);
90512111Sralph 	}
90612111Sralph 	if (!SF)
90712111Sralph 		(void) write(ofd, FF, strlen(FF));
90812111Sralph 	tof = 1;
90912111Sralph }
91012111Sralph 
91155474Sbostic static char *
91212111Sralph scnline(key, p, c)
91355474Sbostic 	register int key;
91455474Sbostic 	register char *p;
91555474Sbostic 	int c;
91612111Sralph {
91712111Sralph 	register scnwidth;
91812111Sralph 
91912111Sralph 	for (scnwidth = WIDTH; --scnwidth;) {
92012111Sralph 		key <<= 1;
92112111Sralph 		*p++ = key & 0200 ? c : BACKGND;
92212111Sralph 	}
92312111Sralph 	return (p);
92412111Sralph }
92512111Sralph 
92612111Sralph #define TRC(q)	(((q)-' ')&0177)
92712111Sralph 
92855474Sbostic static void
92912111Sralph scan_out(scfd, scsp, dlm)
93055474Sbostic 	int scfd, dlm;
93155474Sbostic 	char *scsp;
93212111Sralph {
93312111Sralph 	register char *strp;
93412111Sralph 	register nchrs, j;
93512111Sralph 	char outbuf[LINELEN+1], *sp, c, cc;
93612111Sralph 	int d, scnhgt;
93712111Sralph 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
93812111Sralph 
93912111Sralph 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
94012111Sralph 		strp = &outbuf[0];
94112111Sralph 		sp = scsp;
94212111Sralph 		for (nchrs = 0; ; ) {
94312111Sralph 			d = dropit(c = TRC(cc = *sp++));
94412111Sralph 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
94512111Sralph 				for (j = WIDTH; --j;)
94612111Sralph 					*strp++ = BACKGND;
94712111Sralph 			else
94812111Sralph 				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
94912111Sralph 			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
95012111Sralph 				break;
95112111Sralph 			*strp++ = BACKGND;
95212111Sralph 			*strp++ = BACKGND;
95312111Sralph 		}
95412111Sralph 		while (*--strp == BACKGND && strp >= outbuf)
95512111Sralph 			;
95612111Sralph 		strp++;
95712111Sralph 		*strp++ = '\n';
95812111Sralph 		(void) write(scfd, outbuf, strp-outbuf);
95912111Sralph 	}
96012111Sralph }
96112111Sralph 
96255474Sbostic static int
96312111Sralph dropit(c)
96455474Sbostic 	int c;
96512111Sralph {
96612111Sralph 	switch(c) {
96712111Sralph 
96812111Sralph 	case TRC('_'):
96912111Sralph 	case TRC(';'):
97012111Sralph 	case TRC(','):
97112111Sralph 	case TRC('g'):
97212111Sralph 	case TRC('j'):
97312111Sralph 	case TRC('p'):
97412111Sralph 	case TRC('q'):
97512111Sralph 	case TRC('y'):
97612111Sralph 		return (DROP);
97712111Sralph 
97812111Sralph 	default:
97912111Sralph 		return (0);
98012111Sralph 	}
98112111Sralph }
98212111Sralph 
98312111Sralph /*
98412111Sralph  * sendmail ---
98512111Sralph  *   tell people about job completion
98612111Sralph  */
98755474Sbostic static void
98815811Sralph sendmail(user, bombed)
98915811Sralph 	char *user;
99012111Sralph 	int bombed;
99112111Sralph {
99212111Sralph 	register int i;
99315811Sralph 	int p[2], s;
99412111Sralph 	register char *cp;
99512111Sralph 	char buf[100];
99615811Sralph 	struct stat stb;
99715811Sralph 	FILE *fp;
99812111Sralph 
99912111Sralph 	pipe(p);
100015811Sralph 	if ((s = dofork(DORETURN)) == 0) {		/* child */
100112111Sralph 		dup2(p[0], 0);
100212111Sralph 		for (i = 3; i < NOFILE; i++)
100312111Sralph 			(void) close(i);
100437968Sbostic 		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
100512111Sralph 			cp++;
100655474Sbostic 	else
100737968Sbostic 			cp = _PATH_SENDMAIL;
100815811Sralph 		sprintf(buf, "%s@%s", user, fromhost);
100937968Sbostic 		execl(_PATH_SENDMAIL, cp, buf, 0);
101012111Sralph 		exit(0);
101115811Sralph 	} else if (s > 0) {				/* parent */
101212111Sralph 		dup2(p[1], 1);
101315811Sralph 		printf("To: %s@%s\n", user, fromhost);
101412111Sralph 		printf("Subject: printer job\n\n");
101512111Sralph 		printf("Your printer job ");
101612111Sralph 		if (*jobname)
101712111Sralph 			printf("(%s) ", jobname);
101812463Sralph 		switch (bombed) {
101917463Sralph 		case OK:
102012463Sralph 			printf("\ncompleted successfully\n");
102112463Sralph 			break;
102212463Sralph 		default:
102317463Sralph 		case FATALERR:
102412463Sralph 			printf("\ncould not be printed\n");
102512463Sralph 			break;
102617463Sralph 		case NOACCT:
102712463Sralph 			printf("\ncould not be printed without an account on %s\n", host);
102812463Sralph 			break;
102917463Sralph 		case FILTERERR:
103039954Smckusick 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
103139954Smckusick 			    (fp = fopen(tempfile, "r")) == NULL) {
103215811Sralph 				printf("\nwas printed but had some errors\n");
103315811Sralph 				break;
103415811Sralph 			}
103515811Sralph 			printf("\nwas printed but had the following errors:\n");
103615811Sralph 			while ((i = getc(fp)) != EOF)
103715811Sralph 				putchar(i);
103815811Sralph 			(void) fclose(fp);
103917463Sralph 			break;
104017463Sralph 		case ACCESS:
104117463Sralph 			printf("\nwas not printed because it was not linked to the original file\n");
104212463Sralph 		}
104312111Sralph 		fflush(stdout);
104412111Sralph 		(void) close(1);
104512111Sralph 	}
104612111Sralph 	(void) close(p[0]);
104712111Sralph 	(void) close(p[1]);
104815811Sralph 	wait(&s);
104912111Sralph }
105012111Sralph 
105112111Sralph /*
105212111Sralph  * dofork - fork with retries on failure
105312111Sralph  */
105455474Sbostic static int
105512111Sralph dofork(action)
105612111Sralph 	int action;
105712111Sralph {
105812111Sralph 	register int i, pid;
105912111Sralph 
106012111Sralph 	for (i = 0; i < 20; i++) {
106112463Sralph 		if ((pid = fork()) < 0) {
106212111Sralph 			sleep((unsigned)(i*i));
106312463Sralph 			continue;
106412463Sralph 		}
106512463Sralph 		/*
106612463Sralph 		 * Child should run as daemon instead of root
106712463Sralph 		 */
106812463Sralph 		if (pid == 0)
106912463Sralph 			setuid(DU);
107012463Sralph 		return(pid);
107112111Sralph 	}
107216762Sralph 	syslog(LOG_ERR, "can't fork");
107312111Sralph 
107412111Sralph 	switch (action) {
107512111Sralph 	case DORETURN:
107612111Sralph 		return (-1);
107712111Sralph 	default:
107816762Sralph 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
107912111Sralph 		/*FALL THRU*/
108012111Sralph 	case DOABORT:
108112111Sralph 		exit(1);
108212111Sralph 	}
108312111Sralph 	/*NOTREACHED*/
108412111Sralph }
108512111Sralph 
108612111Sralph /*
108716762Sralph  * Kill child processes to abort current job.
108812111Sralph  */
108955474Sbostic static void
109055474Sbostic abortpr(signo)
109155474Sbostic 	int signo;
109212111Sralph {
109339954Smckusick 	(void) unlink(tempfile);
109412111Sralph 	kill(0, SIGINT);
109512111Sralph 	if (ofilter > 0)
109612111Sralph 		kill(ofilter, SIGCONT);
109746912Sbostic 	while (wait(NULL) > 0)
109812111Sralph 		;
109912111Sralph 	exit(0);
110012111Sralph }
110112111Sralph 
110255474Sbostic static void
110312111Sralph init()
110412111Sralph {
110512111Sralph 	int status;
110638736Stef 	char *s;
110712111Sralph 
1108*56123Selan 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
110925468Stef 		syslog(LOG_ERR, "can't open printer description file");
111025468Stef 		exit(1);
1111*56123Selan 	} else if (status == -1) {
111225468Stef 		syslog(LOG_ERR, "unknown printer: %s", printer);
111325468Stef 		exit(1);
1114*56123Selan 	} else if (status == -3)
1115*56123Selan 		fatal("potential reference loop detected in printcap file");
1116*56123Selan 
1117*56123Selan 	if (cgetstr(bp, "lp", &LP) == -1)
111837968Sbostic 		LP = _PATH_DEFDEVLP;
1119*56123Selan 	if (cgetstr(bp, "rp", &RP) == -1)
112012463Sralph 		RP = DEFLP;
1121*56123Selan 	if (cgetstr(bp, "lo", &LO) == -1)
112212111Sralph 		LO = DEFLOCK;
1123*56123Selan 	if (cgetstr(bp, "st", &ST) == -1)
112412111Sralph 		ST = DEFSTAT;
1125*56123Selan 	if (cgetstr(bp, "lf", &LF) == -1)
112637968Sbostic 		LF = _PATH_CONSOLE;
1127*56123Selan 	if (cgetstr(bp, "sd", &SD) == -1)
112837968Sbostic 		SD = _PATH_DEFSPOOL;
1129*56123Selan 	if (cgetnum(bp, "du", &DU) < 0)
113012111Sralph 		DU = DEFUID;
1131*56123Selan 	if (cgetstr(bp,"ff", &FF) == -1)
113212111Sralph 		FF = DEFFF;
1133*56123Selan 	if (cgetnum(bp, "pw", &PW) < 0)
113412111Sralph 		PW = DEFWIDTH;
113512111Sralph 	sprintf(&width[2], "%d", PW);
1136*56123Selan 	if (cgetnum(bp, "pl", &PL) < 0)
113712111Sralph 		PL = DEFLENGTH;
113812111Sralph 	sprintf(&length[2], "%d", PL);
1139*56123Selan 	if (cgetnum(bp,"px", &PX) < 0)
114012463Sralph 		PX = 0;
114112463Sralph 	sprintf(&pxwidth[2], "%d", PX);
1142*56123Selan 	if (cgetnum(bp, "py", &PY) < 0)
114312463Sralph 		PY = 0;
114412463Sralph 	sprintf(&pxlength[2], "%d", PY);
1145*56123Selan 	cgetstr(bp, "rm", &RM);
114638736Stef 	if (s = checkremote())
114738736Stef 		syslog(LOG_WARNING, s);
114825468Stef 
1149*56123Selan 	cgetstr(bp, "af", &AF);
1150*56123Selan 	cgetstr(bp, "of", &OF);
1151*56123Selan 	cgetstr(bp, "if", &IF);
1152*56123Selan 	cgetstr(bp, "rf", &RF);
1153*56123Selan 	cgetstr(bp, "tf", &TF);
1154*56123Selan 	cgetstr(bp, "nf", &NF);
1155*56123Selan 	cgetstr(bp, "df", &DF);
1156*56123Selan 	cgetstr(bp, "gf", &GF);
1157*56123Selan 	cgetstr(bp, "vf", &VF);
1158*56123Selan 	cgetstr(bp, "cf", &CF);
1159*56123Selan 	cgetstr(bp, "tr", &TR);
1160*56123Selan 
1161*56123Selan 	RS = (cgetcap(bp, "rs", ':') != NULL);
1162*56123Selan 	SF = (cgetcap(bp, "sf", ':') != NULL);
1163*56123Selan 	SH = (cgetcap(bp, "sh", ':') != NULL);
1164*56123Selan 	SB = (cgetcap(bp, "sb", ':') != NULL);
1165*56123Selan 	HL = (cgetcap(bp, "hl", ':') != NULL);
1166*56123Selan 	RW = (cgetcap(bp, "rw", ':') != NULL);
1167*56123Selan 
1168*56123Selan 	cgetnum(bp, "br", &BR);
1169*56123Selan 	if (cgetnum(bp, "fc", &FC) < 0)
117012111Sralph 		FC = 0;
1171*56123Selan 	if (cgetnum(bp, "fs", &FS) < 0)
117212111Sralph 		FS = 0;
1173*56123Selan 	if (cgetnum(bp, "xc", &XC) < 0)
117412111Sralph 		XC = 0;
1175*56123Selan 	if (cgetnum(bp, "xs", &XS) < 0)
117612111Sralph 		XS = 0;
1177*56123Selan 
1178*56123Selan 	tof = (cgetcap(bp, "fo", ':') == NULL);
117912111Sralph }
118012111Sralph 
118112463Sralph /*
118212463Sralph  * Acquire line printer or remote connection.
118312463Sralph  */
118455474Sbostic static void
118512463Sralph openpr()
118612463Sralph {
118712463Sralph 	register int i, n;
118816762Sralph 	int resp;
118912463Sralph 
119038736Stef 	if (!sendtorem && *LP) {
119112463Sralph 		for (i = 1; ; i = i < 32 ? i << 1 : i) {
119213148Ssam 			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
119312463Sralph 			if (pfd >= 0)
119412463Sralph 				break;
119512463Sralph 			if (errno == ENOENT) {
119616762Sralph 				syslog(LOG_ERR, "%s: %m", LP);
119712463Sralph 				exit(1);
119812463Sralph 			}
119912463Sralph 			if (i == 1)
120055474Sbostic 				pstatus("waiting for %s to become ready (offline ?)", printer);
120112463Sralph 			sleep(i);
120212463Sralph 		}
120312463Sralph 		if (isatty(pfd))
120412463Sralph 			setty();
120555474Sbostic 		pstatus("%s is ready and printing", printer);
120612463Sralph 	} else if (RM != NULL) {
120716762Sralph 		for (i = 1; ; i = i < 256 ? i << 1 : i) {
120816762Sralph 			resp = -1;
120912528Sralph 			pfd = getport(RM);
121012463Sralph 			if (pfd >= 0) {
121112463Sralph 				(void) sprintf(line, "\2%s\n", RP);
121212463Sralph 				n = strlen(line);
121316762Sralph 				if (write(pfd, line, n) == n &&
121416762Sralph 				    (resp = response()) == '\0')
121512463Sralph 					break;
121616031Sralph 				(void) close(pfd);
121712463Sralph 			}
121816031Sralph 			if (i == 1) {
121916762Sralph 				if (resp < 0)
122055474Sbostic 					pstatus("waiting for %s to come up", RM);
122116762Sralph 				else {
122255474Sbostic 					pstatus("waiting for queue to be enabled on %s", RM);
122316762Sralph 					i = 256;
122416762Sralph 				}
122516031Sralph 			}
122612463Sralph 			sleep(i);
122712463Sralph 		}
122855474Sbostic 		pstatus("sending to %s", RM);
122912463Sralph 		remote = 1;
123012463Sralph 	} else {
123116762Sralph 		syslog(LOG_ERR, "%s: no line printer device or host name",
123216762Sralph 			printer);
123312463Sralph 		exit(1);
123412463Sralph 	}
123512463Sralph 	/*
123612463Sralph 	 * Start up an output filter, if needed.
123712463Sralph 	 */
123840049Stef 	if (!remote && OF) {
123912463Sralph 		int p[2];
124012463Sralph 		char *cp;
124112463Sralph 
124212463Sralph 		pipe(p);
124312463Sralph 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
124412463Sralph 			dup2(p[0], 0);		/* pipe is std in */
124512463Sralph 			dup2(pfd, 1);		/* printer is std out */
124612463Sralph 			for (i = 3; i < NOFILE; i++)
124712463Sralph 				(void) close(i);
124812463Sralph 			if ((cp = rindex(OF, '/')) == NULL)
124912463Sralph 				cp = OF;
125012463Sralph 			else
125112463Sralph 				cp++;
125212463Sralph 			execl(OF, cp, width, length, 0);
125316762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
125412463Sralph 			exit(1);
125512463Sralph 		}
125612463Sralph 		(void) close(p[0]);		/* close input side */
125712463Sralph 		ofd = p[1];			/* use pipe for output */
125812463Sralph 	} else {
125912463Sralph 		ofd = pfd;
126012463Sralph 		ofilter = 0;
126112463Sralph 	}
126212463Sralph }
126312463Sralph 
126412111Sralph struct bauds {
126512111Sralph 	int	baud;
126612111Sralph 	int	speed;
126712111Sralph } bauds[] = {
126812111Sralph 	50,	B50,
126912111Sralph 	75,	B75,
127012111Sralph 	110,	B110,
127112111Sralph 	134,	B134,
127212111Sralph 	150,	B150,
127312111Sralph 	200,	B200,
127412111Sralph 	300,	B300,
127512111Sralph 	600,	B600,
127612111Sralph 	1200,	B1200,
127712111Sralph 	1800,	B1800,
127812111Sralph 	2400,	B2400,
127912111Sralph 	4800,	B4800,
128012111Sralph 	9600,	B9600,
128112111Sralph 	19200,	EXTA,
128212111Sralph 	38400,	EXTB,
128312111Sralph 	0,	0
128412111Sralph };
128512111Sralph 
128612111Sralph /*
128712111Sralph  * setup tty lines.
128812111Sralph  */
128955474Sbostic static void
129012111Sralph setty()
129112111Sralph {
129212111Sralph 	struct sgttyb ttybuf;
129312111Sralph 	register struct bauds *bp;
129412111Sralph 
129512111Sralph 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
129616762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
129712111Sralph 		exit(1);
129812111Sralph 	}
129912111Sralph 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
130016762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
130112111Sralph 		exit(1);
130212111Sralph 	}
130312111Sralph 	if (BR > 0) {
130412111Sralph 		for (bp = bauds; bp->baud; bp++)
130512111Sralph 			if (BR == bp->baud)
130612111Sralph 				break;
130712111Sralph 		if (!bp->baud) {
130816762Sralph 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
130912111Sralph 			exit(1);
131012111Sralph 		}
131112111Sralph 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
131212111Sralph 	}
131313169Sralph 	ttybuf.sg_flags &= ~FC;
131413169Sralph 	ttybuf.sg_flags |= FS;
131512111Sralph 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
131616762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
131712111Sralph 		exit(1);
131812111Sralph 	}
131912111Sralph 	if (XC) {
132012111Sralph 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
132116762Sralph 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
132212111Sralph 			exit(1);
132312111Sralph 		}
132412111Sralph 	}
132512111Sralph 	if (XS) {
132612111Sralph 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
132716762Sralph 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
132812111Sralph 			exit(1);
132912111Sralph 		}
133012111Sralph 	}
133112111Sralph }
133212463Sralph 
133355474Sbostic #if __STDC__
133455474Sbostic #include <stdarg.h>
133555474Sbostic #else
133655474Sbostic #include <varargs.h>
133755474Sbostic #endif
133855474Sbostic 
133955474Sbostic void
134055474Sbostic #if __STDC__
134155474Sbostic pstatus(const char *msg, ...)
134255474Sbostic #else
134355474Sbostic pstatus(msg, va_alist)
134412463Sralph 	char *msg;
134555474Sbostic         va_dcl
134655474Sbostic #endif
134712463Sralph {
134812463Sralph 	register int fd;
134912463Sralph 	char buf[BUFSIZ];
135055474Sbostic 	va_list ap;
135155474Sbostic #if __STDC__
135255474Sbostic 	va_start(ap, msg);
135355474Sbostic #else
135455474Sbostic 	va_start(ap);
135555474Sbostic #endif
135612463Sralph 
135712463Sralph 	umask(0);
135813148Ssam 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
135916762Sralph 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
136016762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
136116762Sralph 		exit(1);
136216762Sralph 	}
136313148Ssam 	ftruncate(fd, 0);
136455474Sbostic 	(void)vsnprintf(buf, sizeof(buf), msg, ap);
136555474Sbostic 	va_end(ap);
136612463Sralph 	strcat(buf, "\n");
136712463Sralph 	(void) write(fd, buf, strlen(buf));
136812463Sralph 	(void) close(fd);
136912463Sralph }
1370