xref: /csrg-svn/usr.sbin/lpr/lpd/printjob.c (revision 69008)
122437Sdist /*
261845Sbostic  * Copyright (c) 1983, 1993
361845Sbostic  *	The Regents of the University of California.  All rights reserved.
434203Sbostic  *
556123Selan  *
656251Selan  * %sccs.include.redist.c%
722437Sdist  */
822437Sdist 
913954Ssam #ifndef lint
1061845Sbostic static char copyright[] =
1161845Sbostic "@(#) Copyright (c) 1983, 1993\n\
1261845Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1334203Sbostic #endif /* not lint */
1413954Ssam 
1556251Selan #ifndef lint
16*69008Stef static char sccsid[] = "@(#)printjob.c	8.5 (Berkeley) 04/28/95";
1756251Selan #endif /* not lint */
1856251Selan 
1956251Selan 
2012111Sralph /*
2112111Sralph  * printjob -- print jobs in the queue.
2212111Sralph  *
2312111Sralph  *	NOTE: the lock file is used to pass information to lpq and lprm.
2412111Sralph  *	it does not need to be removed because file locks are dynamic.
2512111Sralph  */
2612111Sralph 
2755474Sbostic #include <sys/param.h>
2855474Sbostic #include <sys/wait.h>
2955474Sbostic #include <sys/stat.h>
3056123Selan #include <sys/types.h>
3155474Sbostic 
3256123Selan #include <pwd.h>
3356123Selan #include <unistd.h>
3455474Sbostic #include <signal.h>
3555474Sbostic #include <sgtty.h>
3655474Sbostic #include <syslog.h>
3755474Sbostic #include <fcntl.h>
3855474Sbostic #include <dirent.h>
3955474Sbostic #include <errno.h>
4055474Sbostic #include <stdio.h>
4155474Sbostic #include <string.h>
4256123Selan #include <stdlib.h>
4312111Sralph #include "lp.h"
4455474Sbostic #include "lp.local.h"
4537968Sbostic #include "pathnames.h"
4655474Sbostic #include "extern.h"
4712111Sralph 
4816762Sralph #define DORETURN	0	/* absorb fork error */
4916762Sralph #define DOABORT		1	/* abort if dofork fails */
5012111Sralph 
5117463Sralph /*
5217463Sralph  * Error tokens
5317463Sralph  */
5417463Sralph #define REPRINT		-2
5517463Sralph #define ERROR		-1
5617463Sralph #define	OK		0
5717463Sralph #define	FATALERR	1
5817463Sralph #define	NOACCT		2
5917463Sralph #define	FILTERERR	3
6017463Sralph #define	ACCESS		4
6117463Sralph 
6256123Selan static dev_t	 fdev;		/* device of file pointed to by symlink */
6356123Selan static ino_t	 fino;		/* inode of file pointed to by symlink */
6456123Selan static FILE	*cfp;		/* control file */
6556123Selan static int	 child;		/* id of any filters */
6656123Selan static int	 lfd;		/* lock file descriptor */
6756123Selan static int	 ofd;		/* output filter file descriptor */
6856123Selan static int	 ofilter;	/* id of output filter, if any */
6956123Selan static int	 pfd;		/* prstatic inter file descriptor */
7056123Selan static int	 pid;		/* pid of lpd process */
7156123Selan static int	 prchild;	/* id of pr process */
7256123Selan static char	 title[80];	/* ``pr'' title */
7356123Selan static int	 tof;		/* true if at top of form */
7412111Sralph 
7556123Selan static char	class[32];		/* classification field */
7656123Selan static char	fromhost[32];		/* user's host machine */
7756123Selan 				/* indentation size in static characters */
7856123Selan static char	indent[10] = "-i0";
7956123Selan static char	jobname[100];		/* job or file name */
8056123Selan static char	length[10] = "-l";	/* page length in lines */
8156123Selan static char	logname[32];		/* user's login name */
8256123Selan static char	pxlength[10] = "-y";	/* page length in pixels */
8356123Selan static char	pxwidth[10] = "-x";	/* page width in pixels */
8456123Selan static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
8556123Selan static char	width[10] = "-w";	/* page width in static characters */
8612111Sralph 
8755474Sbostic static void       abortpr __P((int));
8855474Sbostic static void       banner __P((char *, char *));
8955474Sbostic static int        dofork __P((int));
9055474Sbostic static int        dropit __P((int));
9155474Sbostic static void       init __P((void));
9255474Sbostic static void       openpr __P((void));
93*69008Stef static void       opennet __P((char *));
94*69008Stef static void       opentty __P((void));
95*69008Stef static void       openrem __P((void));
9655474Sbostic static int        print __P((int, char *));
9755474Sbostic static int        printit __P((char *));
9855474Sbostic static void       pstatus __P((const char *, ...));
9955474Sbostic static char       response __P((void));
10055474Sbostic static void       scan_out __P((int, char *, int));
10155474Sbostic static char      *scnline __P((int, char *, int));
10255474Sbostic static int        sendfile __P((int, char *));
10355474Sbostic static int        sendit __P((char *));
10455474Sbostic static void       sendmail __P((char *, int));
10555474Sbostic static void       setty __P((void));
10655474Sbostic 
10755474Sbostic void
10812111Sralph printjob()
10912111Sralph {
11012111Sralph 	struct stat stb;
11112111Sralph 	register struct queue *q, **qp;
11212111Sralph 	struct queue **queue;
11312111Sralph 	register int i, nitems;
11468972Stef 	off_t pidoff;
11569007Stef 	int errcnt, count = 0;
11612111Sralph 
11712111Sralph 	init();					/* set up capabilities */
11813442Sralph 	(void) write(1, "", 1);			/* ack that daemon is started */
11925496Seric 	(void) close(2);			/* set up log file */
12025496Seric 	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
12125496Seric 		syslog(LOG_ERR, "%s: %m", LF);
12237968Sbostic 		(void) open(_PATH_DEVNULL, O_WRONLY);
12325496Seric 	}
12416762Sralph 	setgid(getegid());
12512463Sralph 	pid = getpid();				/* for use with lprm */
12612111Sralph 	setpgrp(0, pid);
12716762Sralph 	signal(SIGHUP, abortpr);
12816762Sralph 	signal(SIGINT, abortpr);
12916762Sralph 	signal(SIGQUIT, abortpr);
13016762Sralph 	signal(SIGTERM, abortpr);
13112111Sralph 
13239954Smckusick 	(void) mktemp(tempfile);
13315811Sralph 
13412111Sralph 	/*
13512111Sralph 	 * uses short form file names
13612111Sralph 	 */
13712111Sralph 	if (chdir(SD) < 0) {
13816762Sralph 		syslog(LOG_ERR, "%s: %m", SD);
13912111Sralph 		exit(1);
14012111Sralph 	}
14112463Sralph 	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
14212463Sralph 		exit(0);		/* printing disabled */
14314150Sralph 	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
14413169Sralph 	if (lfd < 0) {
14516762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
14613169Sralph 		exit(1);
14713169Sralph 	}
14813169Sralph 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
14912111Sralph 		if (errno == EWOULDBLOCK)	/* active deamon present */
15012111Sralph 			exit(0);
15116762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
15212111Sralph 		exit(1);
15312111Sralph 	}
15413148Ssam 	ftruncate(lfd, 0);
15512111Sralph 	/*
15612111Sralph 	 * write process id for others to know
15712111Sralph 	 */
15812111Sralph 	sprintf(line, "%u\n", pid);
15912111Sralph 	pidoff = i = strlen(line);
16012463Sralph 	if (write(lfd, line, i) != i) {
16116762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
16212111Sralph 		exit(1);
16312111Sralph 	}
16412111Sralph 	/*
16512111Sralph 	 * search the spool directory for work and sort by queue order.
16612111Sralph 	 */
16712111Sralph 	if ((nitems = getq(&queue)) < 0) {
16816762Sralph 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
16912111Sralph 		exit(1);
17012111Sralph 	}
17112463Sralph 	if (nitems == 0)		/* no work to do */
17212111Sralph 		exit(0);
17313169Sralph 	if (stb.st_mode & 01) {		/* reset queue flag */
17413169Sralph 		if (fchmod(lfd, stb.st_mode & 0776) < 0)
17516762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
17613169Sralph 	}
17712463Sralph 	openpr();			/* open printer or remote */
17812463Sralph again:
17912111Sralph 	/*
18012111Sralph 	 * we found something to do now do it --
18112111Sralph 	 *    write the name of the current control file into the lock file
18212111Sralph 	 *    so the spool queue program can tell what we're working on
18312111Sralph 	 */
18412111Sralph 	for (qp = queue; nitems--; free((char *) q)) {
18512111Sralph 		q = *qp++;
18612111Sralph 		if (stat(q->q_name, &stb) < 0)
18712111Sralph 			continue;
18869007Stef 		errcnt = 0;
18912463Sralph 	restart:
19068972Stef 		(void) lseek(lfd, pidoff, 0);
19112111Sralph 		(void) sprintf(line, "%s\n", q->q_name);
19212111Sralph 		i = strlen(line);
19312111Sralph 		if (write(lfd, line, i) != i)
19416762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
19512111Sralph 		if (!remote)
19612111Sralph 			i = printit(q->q_name);
19712111Sralph 		else
19812111Sralph 			i = sendit(q->q_name);
19912463Sralph 		/*
20013169Sralph 		 * Check to see if we are supposed to stop printing or
20113169Sralph 		 * if we are to rebuild the queue.
20212463Sralph 		 */
20313169Sralph 		if (fstat(lfd, &stb) == 0) {
20416762Sralph 			/* stop printing before starting next job? */
20513169Sralph 			if (stb.st_mode & 0100)
20613169Sralph 				goto done;
20716762Sralph 			/* rebuild queue (after lpc topq) */
20813169Sralph 			if (stb.st_mode & 01) {
20913169Sralph 				for (free((char *) q); nitems--; free((char *) q))
21013169Sralph 					q = *qp++;
21113169Sralph 				if (fchmod(lfd, stb.st_mode & 0776) < 0)
21216762Sralph 					syslog(LOG_WARNING, "%s: %s: %m",
21316762Sralph 						printer, LO);
21413169Sralph 				break;
21513169Sralph 			}
21613169Sralph 		}
21717463Sralph 		if (i == OK)		/* file ok and printed */
21814150Sralph 			count++;
21969007Stef 		else if (i == REPRINT && ++errcnt < 5) {
22069007Stef 			/* try reprinting the job */
22116762Sralph 			syslog(LOG_INFO, "restarting %s", printer);
22212111Sralph 			if (ofilter > 0) {
22312111Sralph 				kill(ofilter, SIGCONT);	/* to be sure */
22412111Sralph 				(void) close(ofd);
22512111Sralph 				while ((i = wait(0)) > 0 && i != ofilter)
22612111Sralph 					;
22712111Sralph 				ofilter = 0;
22812111Sralph 			}
22912463Sralph 			(void) close(pfd);	/* close printer */
23015811Sralph 			if (ftruncate(lfd, pidoff) < 0)
23116762Sralph 				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
23212463Sralph 			openpr();		/* try to reopen printer */
23312111Sralph 			goto restart;
23469007Stef 		} else {
23569007Stef 			syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
23669007Stef 				remote ? "sent to remote host" : "printed", q->q_name);
23769007Stef 			if (i == REPRINT) {
23869007Stef 				/* insure we don't attempt this job again */
23969007Stef 				(void) unlink(q->q_name);
24069007Stef 				q->q_name[0] = 'd';
24169007Stef 				(void) unlink(q->q_name);
24269007Stef 				if (logname[0])
24369007Stef 					sendmail(logname, FATALERR);
24469007Stef 			}
24512111Sralph 		}
24612111Sralph 	}
24712111Sralph 	free((char *) queue);
24812463Sralph 	/*
24912463Sralph 	 * search the spool directory for more work.
25012463Sralph 	 */
25112463Sralph 	if ((nitems = getq(&queue)) < 0) {
25216762Sralph 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
25312463Sralph 		exit(1);
25412463Sralph 	}
25512463Sralph 	if (nitems == 0) {		/* no more work to do */
25612463Sralph 	done:
25714150Sralph 		if (count > 0) {	/* Files actually printed */
25814150Sralph 			if (!SF && !tof)
25914150Sralph 				(void) write(ofd, FF, strlen(FF));
26014150Sralph 			if (TR != NULL)		/* output trailer */
26114150Sralph 				(void) write(ofd, TR, strlen(TR));
26214150Sralph 		}
26339954Smckusick 		(void) unlink(tempfile);
26412463Sralph 		exit(0);
26512463Sralph 	}
26612111Sralph 	goto again;
26712111Sralph }
26812111Sralph 
26912111Sralph char	fonts[4][50];	/* fonts for troff */
27012111Sralph 
27137968Sbostic char ifonts[4][40] = {
27237968Sbostic 	_PATH_VFONTR,
27337968Sbostic 	_PATH_VFONTI,
27437968Sbostic 	_PATH_VFONTB,
27537968Sbostic 	_PATH_VFONTS,
27612111Sralph };
27712111Sralph 
27812111Sralph /*
27912111Sralph  * The remaining part is the reading of the control file (cf)
28012111Sralph  * and performing the various actions.
28112111Sralph  */
28255474Sbostic static int
28312111Sralph printit(file)
28412111Sralph 	char *file;
28512111Sralph {
28612111Sralph 	register int i;
28717463Sralph 	char *cp;
28817463Sralph 	int bombed = OK;
28912111Sralph 
29012111Sralph 	/*
29117463Sralph 	 * open control file; ignore if no longer there.
29212111Sralph 	 */
29312111Sralph 	if ((cfp = fopen(file, "r")) == NULL) {
29416762Sralph 		syslog(LOG_INFO, "%s: %s: %m", printer, file);
29517463Sralph 		return(OK);
29612111Sralph 	}
29712111Sralph 	/*
29812111Sralph 	 * Reset troff fonts.
29912111Sralph 	 */
30012111Sralph 	for (i = 0; i < 4; i++)
30112111Sralph 		strcpy(fonts[i], ifonts[i]);
30266833Sbostic 	sprintf(&width[2], "%d", PW);
30317302Sralph 	strcpy(indent+2, "0");
30412111Sralph 
30512111Sralph 	/*
30612111Sralph 	 *      read the control file for work to do
30712111Sralph 	 *
30812111Sralph 	 *      file format -- first character in the line is a command
30912111Sralph 	 *      rest of the line is the argument.
31012111Sralph 	 *      valid commands are:
31112111Sralph 	 *
31217463Sralph 	 *		S -- "stat info" for symbolic link protection
31312111Sralph 	 *		J -- "job name" on banner page
31412111Sralph 	 *		C -- "class name" on banner page
31512111Sralph 	 *              L -- "literal" user's name to print on banner
31612111Sralph 	 *		T -- "title" for pr
31712111Sralph 	 *		H -- "host name" of machine where lpr was done
31812111Sralph 	 *              P -- "person" user's login name
31912581Sralph 	 *              I -- "indent" amount to indent output
32069007Stef 	 *		R -- laser dpi "resolution"
32112111Sralph 	 *              f -- "file name" name of text file to print
32212111Sralph 	 *		l -- "file name" text file with control chars
32312111Sralph 	 *		p -- "file name" text file to print with pr(1)
32412111Sralph 	 *		t -- "file name" troff(1) file to print
32513233Sralph 	 *		n -- "file name" ditroff(1) file to print
32612111Sralph 	 *		d -- "file name" dvi file to print
32712111Sralph 	 *		g -- "file name" plot(1G) file to print
32812111Sralph 	 *		v -- "file name" plain raster file to print
32912111Sralph 	 *		c -- "file name" cifplot file to print
33012111Sralph 	 *		1 -- "R font file" for troff
33112111Sralph 	 *		2 -- "I font file" for troff
33212111Sralph 	 *		3 -- "B font file" for troff
33312111Sralph 	 *		4 -- "S font file" for troff
33412111Sralph 	 *		N -- "name" of file (used by lpq)
33512111Sralph 	 *              U -- "unlink" name of file to remove
33612111Sralph 	 *                    (after we print it. (Pass 2 only)).
33712111Sralph 	 *		M -- "mail" to user when done printing
33812111Sralph 	 *
33912111Sralph 	 *      getline reads a line and expands tabs to blanks
34012111Sralph 	 */
34112111Sralph 
34212111Sralph 	/* pass 1 */
34312111Sralph 
34412111Sralph 	while (getline(cfp))
34512111Sralph 		switch (line[0]) {
34612111Sralph 		case 'H':
34714150Sralph 			strcpy(fromhost, line+1);
34812111Sralph 			if (class[0] == '\0')
34915552Sralph 				strncpy(class, line+1, sizeof(class)-1);
35012111Sralph 			continue;
35112111Sralph 
35212111Sralph 		case 'P':
35315552Sralph 			strncpy(logname, line+1, sizeof(logname)-1);
35412463Sralph 			if (RS) {			/* restricted */
35555474Sbostic 				if (getpwnam(logname) == NULL) {
35617463Sralph 					bombed = NOACCT;
35715811Sralph 					sendmail(line+1, bombed);
35812463Sralph 					goto pass2;
35912463Sralph 				}
36012463Sralph 			}
36112111Sralph 			continue;
36212111Sralph 
36317463Sralph 		case 'S':
36417463Sralph 			cp = line+1;
36517463Sralph 			i = 0;
36617463Sralph 			while (*cp >= '0' && *cp <= '9')
36717463Sralph 				i = i * 10 + (*cp++ - '0');
36817463Sralph 			fdev = i;
36917463Sralph 			cp++;
37017463Sralph 			i = 0;
37117463Sralph 			while (*cp >= '0' && *cp <= '9')
37217463Sralph 				i = i * 10 + (*cp++ - '0');
37317463Sralph 			fino = i;
37417463Sralph 			continue;
37517463Sralph 
37612111Sralph 		case 'J':
37712111Sralph 			if (line[1] != '\0')
37815552Sralph 				strncpy(jobname, line+1, sizeof(jobname)-1);
37912111Sralph 			else
38012111Sralph 				strcpy(jobname, " ");
38112111Sralph 			continue;
38212111Sralph 
38312111Sralph 		case 'C':
38412111Sralph 			if (line[1] != '\0')
38515552Sralph 				strncpy(class, line+1, sizeof(class)-1);
38612111Sralph 			else if (class[0] == '\0')
38715811Sralph 				gethostname(class, sizeof(class));
38812111Sralph 			continue;
38912111Sralph 
39012111Sralph 		case 'T':	/* header title for pr */
39115552Sralph 			strncpy(title, line+1, sizeof(title)-1);
39212111Sralph 			continue;
39312111Sralph 
39412111Sralph 		case 'L':	/* identification line */
39518127Sralph 			if (!SH && !HL)
39612111Sralph 				banner(line+1, jobname);
39712111Sralph 			continue;
39812111Sralph 
39912111Sralph 		case '1':	/* troff fonts */
40012111Sralph 		case '2':
40112111Sralph 		case '3':
40212111Sralph 		case '4':
40312111Sralph 			if (line[1] != '\0')
40412111Sralph 				strcpy(fonts[line[0]-'1'], line+1);
40512111Sralph 			continue;
40612111Sralph 
40712111Sralph 		case 'W':	/* page width */
40815552Sralph 			strncpy(width+2, line+1, sizeof(width)-3);
40912111Sralph 			continue;
41012111Sralph 
41112581Sralph 		case 'I':	/* indent amount */
41215552Sralph 			strncpy(indent+2, line+1, sizeof(indent)-3);
41312581Sralph 			continue;
41412581Sralph 
41512111Sralph 		default:	/* some file to print */
41615811Sralph 			switch (i = print(line[0], line+1)) {
41717463Sralph 			case ERROR:
41817463Sralph 				if (bombed == OK)
41917463Sralph 					bombed = FATALERR;
42015811Sralph 				break;
42117463Sralph 			case REPRINT:
42212111Sralph 				(void) fclose(cfp);
42317463Sralph 				return(REPRINT);
42417463Sralph 			case FILTERERR:
42517463Sralph 			case ACCESS:
42617463Sralph 				bombed = i;
42715811Sralph 				sendmail(logname, bombed);
42815811Sralph 			}
42912111Sralph 			title[0] = '\0';
43012111Sralph 			continue;
43112111Sralph 
43212111Sralph 		case 'N':
43312111Sralph 		case 'U':
43412111Sralph 		case 'M':
43569007Stef 		case 'R':
43612111Sralph 			continue;
43712111Sralph 		}
43812111Sralph 
43912111Sralph 	/* pass 2 */
44012111Sralph 
44112463Sralph pass2:
44212111Sralph 	fseek(cfp, 0L, 0);
44312111Sralph 	while (getline(cfp))
44412111Sralph 		switch (line[0]) {
44518127Sralph 		case 'L':	/* identification line */
44618127Sralph 			if (!SH && HL)
44718127Sralph 				banner(line+1, jobname);
44818127Sralph 			continue;
44918127Sralph 
45012111Sralph 		case 'M':
45117463Sralph 			if (bombed < NOACCT)	/* already sent if >= NOACCT */
45215811Sralph 				sendmail(line+1, bombed);
45312111Sralph 			continue;
45412111Sralph 
45512111Sralph 		case 'U':
45612111Sralph 			(void) unlink(line+1);
45712111Sralph 		}
45812111Sralph 	/*
45915811Sralph 	 * clean-up in case another control file exists
46012111Sralph 	 */
46112111Sralph 	(void) fclose(cfp);
46212111Sralph 	(void) unlink(file);
46317463Sralph 	return(bombed == OK ? OK : ERROR);
46412111Sralph }
46512111Sralph 
46612111Sralph /*
46712111Sralph  * Print a file.
46813233Sralph  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
46915811Sralph  * Return -1 if a non-recoverable error occured,
47015811Sralph  * 2 if the filter detected some errors (but printed the job anyway),
47115811Sralph  * 1 if we should try to reprint this job and
47212111Sralph  * 0 if all is well.
47312111Sralph  * Note: all filters take stdin as the file, stdout as the printer,
47412111Sralph  * stderr as the log file, and must not ignore SIGINT.
47512111Sralph  */
47655474Sbostic static int
47712111Sralph print(format, file)
47812111Sralph 	int format;
47912111Sralph 	char *file;
48012111Sralph {
48115811Sralph 	register int n;
48212111Sralph 	register char *prog;
48315811Sralph 	int fi, fo;
48439954Smckusick 	FILE *fp;
48512111Sralph 	char *av[15], buf[BUFSIZ];
48612111Sralph 	int pid, p[2], stopped = 0;
48712111Sralph 	union wait status;
48817463Sralph 	struct stat stb;
48912111Sralph 
49017463Sralph 	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
49117463Sralph 		return(ERROR);
49217463Sralph 	/*
49317463Sralph 	 * Check to see if data file is a symbolic link. If so, it should
49417463Sralph 	 * still point to the same file or someone is trying to print
49517463Sralph 	 * something he shouldn't.
49617463Sralph 	 */
49717463Sralph 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
49817463Sralph 	    (stb.st_dev != fdev || stb.st_ino != fino))
49917463Sralph 		return(ACCESS);
50012111Sralph 	if (!SF && !tof) {		/* start on a fresh page */
50112111Sralph 		(void) write(ofd, FF, strlen(FF));
50212111Sralph 		tof = 1;
50312111Sralph 	}
50412111Sralph 	if (IF == NULL && (format == 'f' || format == 'l')) {
50512111Sralph 		tof = 0;
50612111Sralph 		while ((n = read(fi, buf, BUFSIZ)) > 0)
50712111Sralph 			if (write(ofd, buf, n) != n) {
50812111Sralph 				(void) close(fi);
50917463Sralph 				return(REPRINT);
51012111Sralph 			}
51112111Sralph 		(void) close(fi);
51217463Sralph 		return(OK);
51312111Sralph 	}
51412111Sralph 	switch (format) {
51512111Sralph 	case 'p':	/* print file using 'pr' */
51612111Sralph 		if (IF == NULL) {	/* use output filter */
51737968Sbostic 			prog = _PATH_PR;
51812111Sralph 			av[0] = "pr";
51912111Sralph 			av[1] = width;
52012111Sralph 			av[2] = length;
52112111Sralph 			av[3] = "-h";
52212111Sralph 			av[4] = *title ? title : " ";
52312111Sralph 			av[5] = 0;
52412111Sralph 			fo = ofd;
52512111Sralph 			goto start;
52612111Sralph 		}
52712111Sralph 		pipe(p);
52812111Sralph 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
52912111Sralph 			dup2(fi, 0);		/* file is stdin */
53012111Sralph 			dup2(p[1], 1);		/* pipe is stdout */
53168972Stef 			closelog();
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 	}
61469007Stef 	if (prog == NULL) {
61569007Stef 		(void) close(fi);
61669007Stef 		syslog(LOG_ERR,
61769007Stef 		   "%s: no filter found in printcap for format character '%c'",
61869007Stef 		   printer, format);
61969007Stef 		return(ERROR);
62069007Stef 	}
62112111Sralph 	if ((av[0] = rindex(prog, '/')) != NULL)
62212111Sralph 		av[0]++;
62312111Sralph 	else
62412111Sralph 		av[0] = prog;
62512111Sralph 	av[n++] = "-n";
62612111Sralph 	av[n++] = logname;
62712111Sralph 	av[n++] = "-h";
62814150Sralph 	av[n++] = fromhost;
62912111Sralph 	av[n++] = AF;
63012111Sralph 	av[n] = 0;
63112111Sralph 	fo = pfd;
63212111Sralph 	if (ofilter > 0) {		/* stop output filter */
63312111Sralph 		write(ofd, "\031\1", 2);
63446912Sbostic 		while ((pid =
63546912Sbostic 		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
63612111Sralph 			;
63712111Sralph 		if (status.w_stopval != WSTOPPED) {
63812111Sralph 			(void) close(fi);
63969007Stef 			syslog(LOG_WARNING,
64069007Stef 				"%s: output filter died (retcode=%d termsig=%d)",
64169007Stef 				printer, status.w_retcode, status.w_termsig);
64217463Sralph 			return(REPRINT);
64312111Sralph 		}
64412111Sralph 		stopped++;
64512111Sralph 	}
64612111Sralph start:
64712111Sralph 	if ((child = dofork(DORETURN)) == 0) {	/* child */
64812111Sralph 		dup2(fi, 0);
64912111Sralph 		dup2(fo, 1);
65039954Smckusick 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
65115811Sralph 		if (n >= 0)
65215811Sralph 			dup2(n, 2);
65368972Stef 		closelog();
65412111Sralph 		for (n = 3; n < NOFILE; n++)
65512111Sralph 			(void) close(n);
65612111Sralph 		execv(prog, av);
65716762Sralph 		syslog(LOG_ERR, "cannot execv %s", prog);
65812111Sralph 		exit(2);
65912111Sralph 	}
66012111Sralph 	(void) close(fi);
66112111Sralph 	if (child < 0)
66212111Sralph 		status.w_retcode = 100;
66312111Sralph 	else
66446912Sbostic 		while ((pid = wait((int *)&status)) > 0 && pid != child)
66512111Sralph 			;
66612111Sralph 	child = 0;
66712111Sralph 	prchild = 0;
66812111Sralph 	if (stopped) {		/* restart output filter */
66912111Sralph 		if (kill(ofilter, SIGCONT) < 0) {
67016762Sralph 			syslog(LOG_ERR, "cannot restart output filter");
67112111Sralph 			exit(1);
67212111Sralph 		}
67312111Sralph 	}
67412111Sralph 	tof = 0;
67539954Smckusick 
67639954Smckusick 	/* Copy filter output to "lf" logfile */
67739954Smckusick 	if (fp = fopen(tempfile, "r")) {
67839954Smckusick 		while (fgets(buf, sizeof(buf), fp))
67939954Smckusick 			fputs(buf, stderr);
68056123Selan 		fclose(fp);
68139954Smckusick 	}
68239954Smckusick 
68315811Sralph 	if (!WIFEXITED(status)) {
68469007Stef 		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
68516762Sralph 			printer, format, status.w_termsig);
68617463Sralph 		return(ERROR);
68717463Sralph 	}
68817463Sralph 	switch (status.w_retcode) {
68917463Sralph 	case 0:
69017463Sralph 		tof = 1;
69117463Sralph 		return(OK);
69217463Sralph 	case 1:
69317463Sralph 		return(REPRINT);
69469007Stef 	case 2:
69569007Stef 		return(ERROR);
69617463Sralph 	default:
69769007Stef 		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
69816762Sralph 			printer, format, status.w_retcode);
69969007Stef 		return(FILTERERR);
70017463Sralph 	}
70112111Sralph }
70212111Sralph 
70312111Sralph /*
70412111Sralph  * Send the daemon control file (cf) and any data files.
70512111Sralph  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
70612111Sralph  * 0 if all is well.
70712111Sralph  */
70855474Sbostic static int
70912111Sralph sendit(file)
71012111Sralph 	char *file;
71112111Sralph {
71217463Sralph 	register int i, err = OK;
71317463Sralph 	char *cp, last[BUFSIZ];
71412111Sralph 
71512111Sralph 	/*
71612111Sralph 	 * open control file
71712111Sralph 	 */
71816762Sralph 	if ((cfp = fopen(file, "r")) == NULL)
71917463Sralph 		return(OK);
72012111Sralph 	/*
72112111Sralph 	 *      read the control file for work to do
72212111Sralph 	 *
72312111Sralph 	 *      file format -- first character in the line is a command
72412111Sralph 	 *      rest of the line is the argument.
72512111Sralph 	 *      commands of interest are:
72612111Sralph 	 *
72712111Sralph 	 *            a-z -- "file name" name of file to print
72812111Sralph 	 *              U -- "unlink" name of file to remove
72912111Sralph 	 *                    (after we print it. (Pass 2 only)).
73012111Sralph 	 */
73112111Sralph 
73212111Sralph 	/*
73312111Sralph 	 * pass 1
73412111Sralph 	 */
73512111Sralph 	while (getline(cfp)) {
73612111Sralph 	again:
73717463Sralph 		if (line[0] == 'S') {
73817463Sralph 			cp = line+1;
73917463Sralph 			i = 0;
74017463Sralph 			while (*cp >= '0' && *cp <= '9')
74117463Sralph 				i = i * 10 + (*cp++ - '0');
74217463Sralph 			fdev = i;
74317463Sralph 			cp++;
74417463Sralph 			i = 0;
74517463Sralph 			while (*cp >= '0' && *cp <= '9')
74617463Sralph 				i = i * 10 + (*cp++ - '0');
74717463Sralph 			fino = i;
74817463Sralph 			continue;
74917463Sralph 		}
75012111Sralph 		if (line[0] >= 'a' && line[0] <= 'z') {
75112111Sralph 			strcpy(last, line);
75217463Sralph 			while (i = getline(cfp))
75312111Sralph 				if (strcmp(last, line))
75412111Sralph 					break;
75517463Sralph 			switch (sendfile('\3', last+1)) {
75617463Sralph 			case OK:
75717463Sralph 				if (i)
75817463Sralph 					goto again;
75917463Sralph 				break;
76017463Sralph 			case REPRINT:
76112111Sralph 				(void) fclose(cfp);
76217463Sralph 				return(REPRINT);
76317463Sralph 			case ACCESS:
76417463Sralph 				sendmail(logname, ACCESS);
76517463Sralph 			case ERROR:
76617463Sralph 				err = ERROR;
76717463Sralph 			}
76812111Sralph 			break;
76912111Sralph 		}
77012111Sralph 	}
77117463Sralph 	if (err == OK && sendfile('\2', file) > 0) {
77212111Sralph 		(void) fclose(cfp);
77317463Sralph 		return(REPRINT);
77412111Sralph 	}
77512111Sralph 	/*
77612111Sralph 	 * pass 2
77712111Sralph 	 */
77812111Sralph 	fseek(cfp, 0L, 0);
77912111Sralph 	while (getline(cfp))
78012111Sralph 		if (line[0] == 'U')
78112111Sralph 			(void) unlink(line+1);
78212111Sralph 	/*
78317463Sralph 	 * clean-up in case another control file exists
78412111Sralph 	 */
78512111Sralph 	(void) fclose(cfp);
78612111Sralph 	(void) unlink(file);
78717463Sralph 	return(err);
78812111Sralph }
78912111Sralph 
79012111Sralph /*
79112111Sralph  * Send a data file to the remote machine and spool it.
79212111Sralph  * Return positive if we should try resending.
79312111Sralph  */
79455474Sbostic static int
79512111Sralph sendfile(type, file)
79655474Sbostic 	int type;
79755474Sbostic 	char *file;
79812111Sralph {
79912111Sralph 	register int f, i, amt;
80012111Sralph 	struct stat stb;
80112111Sralph 	char buf[BUFSIZ];
80216762Sralph 	int sizerr, resp;
80312111Sralph 
80417463Sralph 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
80517463Sralph 		return(ERROR);
80617463Sralph 	/*
80717463Sralph 	 * Check to see if data file is a symbolic link. If so, it should
80817463Sralph 	 * still point to the same file or someone is trying to print something
80917463Sralph 	 * he shouldn't.
81017463Sralph 	 */
81117463Sralph 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
81217463Sralph 	    (stb.st_dev != fdev || stb.st_ino != fino))
81317463Sralph 		return(ACCESS);
81468972Stef 	(void) sprintf(buf, "%c%ld %s\n", type, (long)stb.st_size, file);
81512111Sralph 	amt = strlen(buf);
81616762Sralph 	for (i = 0;  ; i++) {
81716762Sralph 		if (write(pfd, buf, amt) != amt ||
81816762Sralph 		    (resp = response()) < 0 || resp == '\1') {
81916762Sralph 			(void) close(f);
82017463Sralph 			return(REPRINT);
82116762Sralph 		} else if (resp == '\0')
82216762Sralph 			break;
82316762Sralph 		if (i == 0)
82455474Sbostic 			pstatus("no space on remote; waiting for queue to drain");
82516762Sralph 		if (i == 10)
82624861Seric 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
82716762Sralph 				printer, RM);
82816762Sralph 		sleep(5 * 60);
82912692Sralph 	}
83016762Sralph 	if (i)
83155474Sbostic 		pstatus("sending to %s", RM);
83212111Sralph 	sizerr = 0;
83312111Sralph 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
83412111Sralph 		amt = BUFSIZ;
83512111Sralph 		if (i + amt > stb.st_size)
83612111Sralph 			amt = stb.st_size - i;
83712111Sralph 		if (sizerr == 0 && read(f, buf, amt) != amt)
83812111Sralph 			sizerr = 1;
83912692Sralph 		if (write(pfd, buf, amt) != amt) {
84012692Sralph 			(void) close(f);
84117463Sralph 			return(REPRINT);
84212692Sralph 		}
84312111Sralph 	}
84455474Sbostic 
84555474Sbostic 
84655474Sbostic 
84755474Sbostic 
84812111Sralph 	(void) close(f);
84912111Sralph 	if (sizerr) {
85016762Sralph 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
85117463Sralph 		/* tell recvjob to ignore this file */
85217463Sralph 		(void) write(pfd, "\1", 1);
85317463Sralph 		return(ERROR);
85417463Sralph 	}
85517463Sralph 	if (write(pfd, "", 1) != 1 || response())
85617463Sralph 		return(REPRINT);
85717463Sralph 	return(OK);
85812111Sralph }
85912111Sralph 
86012111Sralph /*
86112111Sralph  * Check to make sure there have been no errors and that both programs
86212111Sralph  * are in sync with eachother.
86312111Sralph  * Return non-zero if the connection was lost.
86412111Sralph  */
86555474Sbostic static char
86616762Sralph response()
86712111Sralph {
86812111Sralph 	char resp;
86912111Sralph 
87016762Sralph 	if (read(pfd, &resp, 1) != 1) {
87116762Sralph 		syslog(LOG_INFO, "%s: lost connection", printer);
87216762Sralph 		return(-1);
87312111Sralph 	}
87416762Sralph 	return(resp);
87512111Sralph }
87612111Sralph 
87712111Sralph /*
87812111Sralph  * Banner printing stuff
87912111Sralph  */
88055474Sbostic static void
88112111Sralph banner(name1, name2)
88212111Sralph 	char *name1, *name2;
88312111Sralph {
88412111Sralph 	time_t tvec;
88512111Sralph 	extern char *ctime();
88612111Sralph 
88712111Sralph 	time(&tvec);
88812111Sralph 	if (!SF && !tof)
88912111Sralph 		(void) write(ofd, FF, strlen(FF));
89012111Sralph 	if (SB) {	/* short banner only */
89112111Sralph 		if (class[0]) {
89212111Sralph 			(void) write(ofd, class, strlen(class));
89312111Sralph 			(void) write(ofd, ":", 1);
89412111Sralph 		}
89512111Sralph 		(void) write(ofd, name1, strlen(name1));
89612111Sralph 		(void) write(ofd, "  Job: ", 7);
89712111Sralph 		(void) write(ofd, name2, strlen(name2));
89812111Sralph 		(void) write(ofd, "  Date: ", 8);
89912111Sralph 		(void) write(ofd, ctime(&tvec), 24);
90012111Sralph 		(void) write(ofd, "\n", 1);
90112111Sralph 	} else {	/* normal banner */
90212111Sralph 		(void) write(ofd, "\n\n\n", 3);
90312111Sralph 		scan_out(ofd, name1, '\0');
90412111Sralph 		(void) write(ofd, "\n\n", 2);
90512111Sralph 		scan_out(ofd, name2, '\0');
90612111Sralph 		if (class[0]) {
90712111Sralph 			(void) write(ofd,"\n\n\n",3);
90812111Sralph 			scan_out(ofd, class, '\0');
90912111Sralph 		}
91012111Sralph 		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
91112111Sralph 		(void) write(ofd, name2, strlen(name2));
91212111Sralph 		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
91312111Sralph 		(void) write(ofd, ctime(&tvec), 24);
91412111Sralph 		(void) write(ofd, "\n", 1);
91512111Sralph 	}
91612111Sralph 	if (!SF)
91712111Sralph 		(void) write(ofd, FF, strlen(FF));
91812111Sralph 	tof = 1;
91912111Sralph }
92012111Sralph 
92155474Sbostic static char *
92212111Sralph scnline(key, p, c)
92355474Sbostic 	register int key;
92455474Sbostic 	register char *p;
92555474Sbostic 	int c;
92612111Sralph {
92712111Sralph 	register scnwidth;
92812111Sralph 
92912111Sralph 	for (scnwidth = WIDTH; --scnwidth;) {
93012111Sralph 		key <<= 1;
93112111Sralph 		*p++ = key & 0200 ? c : BACKGND;
93212111Sralph 	}
93312111Sralph 	return (p);
93412111Sralph }
93512111Sralph 
93612111Sralph #define TRC(q)	(((q)-' ')&0177)
93712111Sralph 
93855474Sbostic static void
93912111Sralph scan_out(scfd, scsp, dlm)
94055474Sbostic 	int scfd, dlm;
94155474Sbostic 	char *scsp;
94212111Sralph {
94312111Sralph 	register char *strp;
94412111Sralph 	register nchrs, j;
94512111Sralph 	char outbuf[LINELEN+1], *sp, c, cc;
94612111Sralph 	int d, scnhgt;
94712111Sralph 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
94812111Sralph 
94912111Sralph 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
95012111Sralph 		strp = &outbuf[0];
95112111Sralph 		sp = scsp;
95212111Sralph 		for (nchrs = 0; ; ) {
95312111Sralph 			d = dropit(c = TRC(cc = *sp++));
95412111Sralph 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
95512111Sralph 				for (j = WIDTH; --j;)
95612111Sralph 					*strp++ = BACKGND;
95712111Sralph 			else
95812111Sralph 				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
95912111Sralph 			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
96012111Sralph 				break;
96112111Sralph 			*strp++ = BACKGND;
96212111Sralph 			*strp++ = BACKGND;
96312111Sralph 		}
96412111Sralph 		while (*--strp == BACKGND && strp >= outbuf)
96512111Sralph 			;
96612111Sralph 		strp++;
96712111Sralph 		*strp++ = '\n';
96812111Sralph 		(void) write(scfd, outbuf, strp-outbuf);
96912111Sralph 	}
97012111Sralph }
97112111Sralph 
97255474Sbostic static int
97312111Sralph dropit(c)
97455474Sbostic 	int c;
97512111Sralph {
97612111Sralph 	switch(c) {
97712111Sralph 
97812111Sralph 	case TRC('_'):
97912111Sralph 	case TRC(';'):
98012111Sralph 	case TRC(','):
98112111Sralph 	case TRC('g'):
98212111Sralph 	case TRC('j'):
98312111Sralph 	case TRC('p'):
98412111Sralph 	case TRC('q'):
98512111Sralph 	case TRC('y'):
98612111Sralph 		return (DROP);
98712111Sralph 
98812111Sralph 	default:
98912111Sralph 		return (0);
99012111Sralph 	}
99112111Sralph }
99212111Sralph 
99312111Sralph /*
99412111Sralph  * sendmail ---
99512111Sralph  *   tell people about job completion
99612111Sralph  */
99755474Sbostic static void
99815811Sralph sendmail(user, bombed)
99915811Sralph 	char *user;
100012111Sralph 	int bombed;
100112111Sralph {
100212111Sralph 	register int i;
100315811Sralph 	int p[2], s;
100412111Sralph 	register char *cp;
100512111Sralph 	char buf[100];
100615811Sralph 	struct stat stb;
100715811Sralph 	FILE *fp;
100812111Sralph 
100912111Sralph 	pipe(p);
101015811Sralph 	if ((s = dofork(DORETURN)) == 0) {		/* child */
101112111Sralph 		dup2(p[0], 0);
101268972Stef 		closelog();
101312111Sralph 		for (i = 3; i < NOFILE; i++)
101412111Sralph 			(void) close(i);
101537968Sbostic 		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
101612111Sralph 			cp++;
101755474Sbostic 	else
101837968Sbostic 			cp = _PATH_SENDMAIL;
101915811Sralph 		sprintf(buf, "%s@%s", user, fromhost);
102037968Sbostic 		execl(_PATH_SENDMAIL, cp, buf, 0);
102112111Sralph 		exit(0);
102215811Sralph 	} else if (s > 0) {				/* parent */
102312111Sralph 		dup2(p[1], 1);
102415811Sralph 		printf("To: %s@%s\n", user, fromhost);
102569007Stef 		printf("Subject: %s printer job \"%s\"\n", printer,
102669007Stef 			*jobname ? jobname : "<unknown>");
102769007Stef 		printf("Reply-To: root@%s\n\n", host);
102812111Sralph 		printf("Your printer job ");
102912111Sralph 		if (*jobname)
103012111Sralph 			printf("(%s) ", jobname);
103112463Sralph 		switch (bombed) {
103217463Sralph 		case OK:
103312463Sralph 			printf("\ncompleted successfully\n");
1034*69008Stef 			cp = "OK";
103512463Sralph 			break;
103612463Sralph 		default:
103717463Sralph 		case FATALERR:
103812463Sralph 			printf("\ncould not be printed\n");
1039*69008Stef 			cp = "FATALERR";
104012463Sralph 			break;
104117463Sralph 		case NOACCT:
104212463Sralph 			printf("\ncould not be printed without an account on %s\n", host);
1043*69008Stef 			cp = "NOACCT";
104412463Sralph 			break;
104517463Sralph 		case FILTERERR:
104639954Smckusick 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
104739954Smckusick 			    (fp = fopen(tempfile, "r")) == NULL) {
104869007Stef 				printf("\nhad some errors and may not have printed\n");
104915811Sralph 				break;
105015811Sralph 			}
105169007Stef 			printf("\nhad the following errors and may not have printed:\n");
105215811Sralph 			while ((i = getc(fp)) != EOF)
105315811Sralph 				putchar(i);
105415811Sralph 			(void) fclose(fp);
1055*69008Stef 			cp = "FILTERERR";
105617463Sralph 			break;
105717463Sralph 		case ACCESS:
105817463Sralph 			printf("\nwas not printed because it was not linked to the original file\n");
1059*69008Stef 			cp = "ACCESS";
106012463Sralph 		}
106112111Sralph 		fflush(stdout);
106212111Sralph 		(void) close(1);
106312111Sralph 	}
106412111Sralph 	(void) close(p[0]);
106512111Sralph 	(void) close(p[1]);
106615811Sralph 	wait(&s);
1067*69008Stef 	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
1068*69008Stef 		user, *jobname ? jobname : "<unknown>", printer, cp);
106912111Sralph }
107012111Sralph 
107112111Sralph /*
107212111Sralph  * dofork - fork with retries on failure
107312111Sralph  */
107455474Sbostic static int
107512111Sralph dofork(action)
107612111Sralph 	int action;
107712111Sralph {
107812111Sralph 	register int i, pid;
107912111Sralph 
108012111Sralph 	for (i = 0; i < 20; i++) {
108112463Sralph 		if ((pid = fork()) < 0) {
108212111Sralph 			sleep((unsigned)(i*i));
108312463Sralph 			continue;
108412463Sralph 		}
108512463Sralph 		/*
108612463Sralph 		 * Child should run as daemon instead of root
108712463Sralph 		 */
108812463Sralph 		if (pid == 0)
108912463Sralph 			setuid(DU);
109012463Sralph 		return(pid);
109112111Sralph 	}
109216762Sralph 	syslog(LOG_ERR, "can't fork");
109312111Sralph 
109412111Sralph 	switch (action) {
109512111Sralph 	case DORETURN:
109612111Sralph 		return (-1);
109712111Sralph 	default:
109816762Sralph 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
109912111Sralph 		/*FALL THRU*/
110012111Sralph 	case DOABORT:
110112111Sralph 		exit(1);
110212111Sralph 	}
110312111Sralph 	/*NOTREACHED*/
110412111Sralph }
110512111Sralph 
110612111Sralph /*
110716762Sralph  * Kill child processes to abort current job.
110812111Sralph  */
110955474Sbostic static void
111055474Sbostic abortpr(signo)
111155474Sbostic 	int signo;
111212111Sralph {
111339954Smckusick 	(void) unlink(tempfile);
111412111Sralph 	kill(0, SIGINT);
111512111Sralph 	if (ofilter > 0)
111612111Sralph 		kill(ofilter, SIGCONT);
111746912Sbostic 	while (wait(NULL) > 0)
111812111Sralph 		;
111912111Sralph 	exit(0);
112012111Sralph }
112112111Sralph 
112255474Sbostic static void
112312111Sralph init()
112412111Sralph {
112512111Sralph 	int status;
112638736Stef 	char *s;
112712111Sralph 
112856123Selan 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
112925468Stef 		syslog(LOG_ERR, "can't open printer description file");
113025468Stef 		exit(1);
113156123Selan 	} else if (status == -1) {
113225468Stef 		syslog(LOG_ERR, "unknown printer: %s", printer);
113325468Stef 		exit(1);
113456123Selan 	} else if (status == -3)
113556123Selan 		fatal("potential reference loop detected in printcap file");
113656123Selan 
113756123Selan 	if (cgetstr(bp, "lp", &LP) == -1)
113837968Sbostic 		LP = _PATH_DEFDEVLP;
113956123Selan 	if (cgetstr(bp, "rp", &RP) == -1)
114012463Sralph 		RP = DEFLP;
114156123Selan 	if (cgetstr(bp, "lo", &LO) == -1)
114212111Sralph 		LO = DEFLOCK;
114356123Selan 	if (cgetstr(bp, "st", &ST) == -1)
114412111Sralph 		ST = DEFSTAT;
114556123Selan 	if (cgetstr(bp, "lf", &LF) == -1)
114637968Sbostic 		LF = _PATH_CONSOLE;
114756123Selan 	if (cgetstr(bp, "sd", &SD) == -1)
114837968Sbostic 		SD = _PATH_DEFSPOOL;
114956123Selan 	if (cgetnum(bp, "du", &DU) < 0)
115012111Sralph 		DU = DEFUID;
115156123Selan 	if (cgetstr(bp,"ff", &FF) == -1)
115212111Sralph 		FF = DEFFF;
115356123Selan 	if (cgetnum(bp, "pw", &PW) < 0)
115412111Sralph 		PW = DEFWIDTH;
115512111Sralph 	sprintf(&width[2], "%d", PW);
115656123Selan 	if (cgetnum(bp, "pl", &PL) < 0)
115712111Sralph 		PL = DEFLENGTH;
115812111Sralph 	sprintf(&length[2], "%d", PL);
115956123Selan 	if (cgetnum(bp,"px", &PX) < 0)
116012463Sralph 		PX = 0;
116112463Sralph 	sprintf(&pxwidth[2], "%d", PX);
116256123Selan 	if (cgetnum(bp, "py", &PY) < 0)
116312463Sralph 		PY = 0;
116412463Sralph 	sprintf(&pxlength[2], "%d", PY);
116556123Selan 	cgetstr(bp, "rm", &RM);
116638736Stef 	if (s = checkremote())
116738736Stef 		syslog(LOG_WARNING, s);
116825468Stef 
116956123Selan 	cgetstr(bp, "af", &AF);
117056123Selan 	cgetstr(bp, "of", &OF);
117156123Selan 	cgetstr(bp, "if", &IF);
117256123Selan 	cgetstr(bp, "rf", &RF);
117356123Selan 	cgetstr(bp, "tf", &TF);
117456123Selan 	cgetstr(bp, "nf", &NF);
117556123Selan 	cgetstr(bp, "df", &DF);
117656123Selan 	cgetstr(bp, "gf", &GF);
117756123Selan 	cgetstr(bp, "vf", &VF);
117856123Selan 	cgetstr(bp, "cf", &CF);
117956123Selan 	cgetstr(bp, "tr", &TR);
118056123Selan 
118156123Selan 	RS = (cgetcap(bp, "rs", ':') != NULL);
118256123Selan 	SF = (cgetcap(bp, "sf", ':') != NULL);
118356123Selan 	SH = (cgetcap(bp, "sh", ':') != NULL);
118456123Selan 	SB = (cgetcap(bp, "sb", ':') != NULL);
118556123Selan 	HL = (cgetcap(bp, "hl", ':') != NULL);
118656123Selan 	RW = (cgetcap(bp, "rw", ':') != NULL);
118756123Selan 
118856123Selan 	cgetnum(bp, "br", &BR);
118956123Selan 	if (cgetnum(bp, "fc", &FC) < 0)
119012111Sralph 		FC = 0;
119156123Selan 	if (cgetnum(bp, "fs", &FS) < 0)
119212111Sralph 		FS = 0;
119356123Selan 	if (cgetnum(bp, "xc", &XC) < 0)
119412111Sralph 		XC = 0;
119556123Selan 	if (cgetnum(bp, "xs", &XS) < 0)
119612111Sralph 		XS = 0;
119756123Selan 
119856123Selan 	tof = (cgetcap(bp, "fo", ':') == NULL);
119912111Sralph }
120012111Sralph 
120112463Sralph /*
120212463Sralph  * Acquire line printer or remote connection.
120312463Sralph  */
120455474Sbostic static void
120512463Sralph openpr()
120612463Sralph {
1207*69008Stef 	register int i;
1208*69008Stef 	char *cp;
120912463Sralph 
1210*69008Stef 	if (!remote && *LP) {
1211*69008Stef 		if (cp = index(LP, '@'))
1212*69008Stef 			opennet(cp);
1213*69008Stef 		else
1214*69008Stef 			opentty();
1215*69008Stef 	} else if (remote) {
1216*69008Stef 		openrem();
121712463Sralph 	} else {
121816762Sralph 		syslog(LOG_ERR, "%s: no line printer device or host name",
121916762Sralph 			printer);
122012463Sralph 		exit(1);
122112463Sralph 	}
1222*69008Stef 
122312463Sralph 	/*
122412463Sralph 	 * Start up an output filter, if needed.
122512463Sralph 	 */
122640049Stef 	if (!remote && OF) {
122712463Sralph 		int p[2];
122812463Sralph 
122912463Sralph 		pipe(p);
123012463Sralph 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
123112463Sralph 			dup2(p[0], 0);		/* pipe is std in */
123212463Sralph 			dup2(pfd, 1);		/* printer is std out */
123368972Stef 			closelog();
123412463Sralph 			for (i = 3; i < NOFILE; i++)
123512463Sralph 				(void) close(i);
123612463Sralph 			if ((cp = rindex(OF, '/')) == NULL)
123712463Sralph 				cp = OF;
123812463Sralph 			else
123912463Sralph 				cp++;
124012463Sralph 			execl(OF, cp, width, length, 0);
124116762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
124212463Sralph 			exit(1);
124312463Sralph 		}
124412463Sralph 		(void) close(p[0]);		/* close input side */
124512463Sralph 		ofd = p[1];			/* use pipe for output */
124612463Sralph 	} else {
124712463Sralph 		ofd = pfd;
124812463Sralph 		ofilter = 0;
124912463Sralph 	}
125012463Sralph }
125112463Sralph 
1252*69008Stef /*
1253*69008Stef  * Printer connected directly to the network
1254*69008Stef  * or to a terminal server on the net
1255*69008Stef  */
1256*69008Stef static void
1257*69008Stef opennet(cp)
1258*69008Stef 	char *cp;
1259*69008Stef {
1260*69008Stef 	register int i;
1261*69008Stef 	int resp, port;
1262*69008Stef 
1263*69008Stef 	*cp++ = '\0';
1264*69008Stef 	port = atoi(cp);
1265*69008Stef 	if (port <= 0) {
1266*69008Stef 		syslog(LOG_ERR, "%s: bad port number: %s", printer, cp);
1267*69008Stef 		exit(1);
1268*69008Stef 	}
1269*69008Stef 
1270*69008Stef 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1271*69008Stef 		resp = -1;
1272*69008Stef 		pfd = getport(LP, port);
1273*69008Stef 		if (pfd < 0 && errno == ECONNREFUSED)
1274*69008Stef 			resp = 1;
1275*69008Stef 		else if (pfd >= 0) {
1276*69008Stef 			/*
1277*69008Stef 			 * need to delay a bit for rs232 lines
1278*69008Stef 			 * to stabilize in case printer is
1279*69008Stef 			 * connected via a terminal server
1280*69008Stef 			 */
1281*69008Stef 			delay(500);
1282*69008Stef 			break;
1283*69008Stef 		}
1284*69008Stef 		if (i == 1) {
1285*69008Stef 		   if (resp < 0)
1286*69008Stef 			pstatus("waiting for %s to come up", LP);
1287*69008Stef 		   else
1288*69008Stef 			pstatus("waiting for access to printer on %s", LP);
1289*69008Stef 		}
1290*69008Stef 		sleep(i);
1291*69008Stef 	}
1292*69008Stef 	pstatus("sending to %s port %d", LP, port);
1293*69008Stef 	*(--cp) = '@';	/* restore LP parameter in case we are called again */
1294*69008Stef }
1295*69008Stef 
1296*69008Stef /*
1297*69008Stef  * Printer is connected to an RS232 port on this host
1298*69008Stef  */
1299*69008Stef static void
1300*69008Stef opentty()
1301*69008Stef {
1302*69008Stef 	register int i;
1303*69008Stef 	int resp, port;
1304*69008Stef 
1305*69008Stef 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
1306*69008Stef 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1307*69008Stef 		if (pfd >= 0) {
1308*69008Stef 			delay(500);
1309*69008Stef 			break;
1310*69008Stef 		}
1311*69008Stef 		if (errno == ENOENT) {
1312*69008Stef 			syslog(LOG_ERR, "%s: %m", LP);
1313*69008Stef 			exit(1);
1314*69008Stef 		}
1315*69008Stef 		if (i == 1)
1316*69008Stef 			pstatus("waiting for %s to become ready (offline ?)",
1317*69008Stef 				printer);
1318*69008Stef 		sleep(i);
1319*69008Stef 	}
1320*69008Stef 	if (isatty(pfd))
1321*69008Stef 		setty();
1322*69008Stef 	pstatus("%s is ready and printing", printer);
1323*69008Stef }
1324*69008Stef 
1325*69008Stef /*
1326*69008Stef  * Printer is on a remote host
1327*69008Stef  */
1328*69008Stef static void
1329*69008Stef openrem()
1330*69008Stef {
1331*69008Stef 	register int i, n;
1332*69008Stef 	int resp, port;
1333*69008Stef 
1334*69008Stef 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1335*69008Stef 		resp = -1;
1336*69008Stef 		pfd = getport(RM, 0);
1337*69008Stef 		if (pfd >= 0) {
1338*69008Stef 			(void) sprintf(line, "\2%s\n", RP);
1339*69008Stef 			n = strlen(line);
1340*69008Stef 			if (write(pfd, line, n) == n &&
1341*69008Stef 			    (resp = response()) == '\0')
1342*69008Stef 				break;
1343*69008Stef 			(void) close(pfd);
1344*69008Stef 		}
1345*69008Stef 		if (i == 1) {
1346*69008Stef 			if (resp < 0)
1347*69008Stef 				pstatus("waiting for %s to come up", RM);
1348*69008Stef 			else {
1349*69008Stef 				pstatus("waiting for queue to be enabled on %s",
1350*69008Stef 					RM);
1351*69008Stef 				i = 256;
1352*69008Stef 			}
1353*69008Stef 		}
1354*69008Stef 		sleep(i);
1355*69008Stef 	}
1356*69008Stef 	pstatus("sending to %s", RM);
1357*69008Stef }
1358*69008Stef 
135912111Sralph struct bauds {
136012111Sralph 	int	baud;
136112111Sralph 	int	speed;
136212111Sralph } bauds[] = {
136312111Sralph 	50,	B50,
136412111Sralph 	75,	B75,
136512111Sralph 	110,	B110,
136612111Sralph 	134,	B134,
136712111Sralph 	150,	B150,
136812111Sralph 	200,	B200,
136912111Sralph 	300,	B300,
137012111Sralph 	600,	B600,
137112111Sralph 	1200,	B1200,
137212111Sralph 	1800,	B1800,
137312111Sralph 	2400,	B2400,
137412111Sralph 	4800,	B4800,
137512111Sralph 	9600,	B9600,
137612111Sralph 	19200,	EXTA,
137712111Sralph 	38400,	EXTB,
137812111Sralph 	0,	0
137912111Sralph };
138012111Sralph 
138112111Sralph /*
138212111Sralph  * setup tty lines.
138312111Sralph  */
138455474Sbostic static void
138512111Sralph setty()
138612111Sralph {
138712111Sralph 	struct sgttyb ttybuf;
138812111Sralph 	register struct bauds *bp;
138912111Sralph 
139012111Sralph 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
139116762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
139212111Sralph 		exit(1);
139312111Sralph 	}
139412111Sralph 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
139516762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
139612111Sralph 		exit(1);
139712111Sralph 	}
139812111Sralph 	if (BR > 0) {
139912111Sralph 		for (bp = bauds; bp->baud; bp++)
140012111Sralph 			if (BR == bp->baud)
140112111Sralph 				break;
140212111Sralph 		if (!bp->baud) {
140316762Sralph 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
140412111Sralph 			exit(1);
140512111Sralph 		}
140612111Sralph 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
140712111Sralph 	}
140813169Sralph 	ttybuf.sg_flags &= ~FC;
140913169Sralph 	ttybuf.sg_flags |= FS;
141012111Sralph 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
141116762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
141212111Sralph 		exit(1);
141312111Sralph 	}
141412111Sralph 	if (XC) {
141512111Sralph 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
141616762Sralph 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
141712111Sralph 			exit(1);
141812111Sralph 		}
141912111Sralph 	}
142012111Sralph 	if (XS) {
142112111Sralph 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
142216762Sralph 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
142312111Sralph 			exit(1);
142412111Sralph 		}
142512111Sralph 	}
142612111Sralph }
142712463Sralph 
142855474Sbostic #if __STDC__
142955474Sbostic #include <stdarg.h>
143055474Sbostic #else
143155474Sbostic #include <varargs.h>
143255474Sbostic #endif
143355474Sbostic 
143455474Sbostic void
143555474Sbostic #if __STDC__
143655474Sbostic pstatus(const char *msg, ...)
143755474Sbostic #else
143855474Sbostic pstatus(msg, va_alist)
143912463Sralph 	char *msg;
144055474Sbostic         va_dcl
144155474Sbostic #endif
144212463Sralph {
144312463Sralph 	register int fd;
144412463Sralph 	char buf[BUFSIZ];
144555474Sbostic 	va_list ap;
144655474Sbostic #if __STDC__
144755474Sbostic 	va_start(ap, msg);
144855474Sbostic #else
144955474Sbostic 	va_start(ap);
145055474Sbostic #endif
145112463Sralph 
145212463Sralph 	umask(0);
145313148Ssam 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
145416762Sralph 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
145516762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
145616762Sralph 		exit(1);
145716762Sralph 	}
145813148Ssam 	ftruncate(fd, 0);
145955474Sbostic 	(void)vsnprintf(buf, sizeof(buf), msg, ap);
146055474Sbostic 	va_end(ap);
146112463Sralph 	strcat(buf, "\n");
146212463Sralph 	(void) write(fd, buf, strlen(buf));
146312463Sralph 	(void) close(fd);
146412463Sralph }
1465