xref: /csrg-svn/usr.sbin/lpr/lpd/printjob.c (revision 69331)
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*69331Sbostic static char sccsid[] = "@(#)printjob.c	8.7 (Berkeley) 05/10/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>
3169061Stef #include <sys/file.h>
3255474Sbostic 
3356123Selan #include <pwd.h>
3456123Selan #include <unistd.h>
3555474Sbostic #include <signal.h>
3655474Sbostic #include <sgtty.h>
3755474Sbostic #include <syslog.h>
3855474Sbostic #include <fcntl.h>
3955474Sbostic #include <dirent.h>
4055474Sbostic #include <errno.h>
4155474Sbostic #include <stdio.h>
4255474Sbostic #include <string.h>
4356123Selan #include <stdlib.h>
4412111Sralph #include "lp.h"
4555474Sbostic #include "lp.local.h"
4637968Sbostic #include "pathnames.h"
4755474Sbostic #include "extern.h"
4812111Sralph 
4916762Sralph #define DORETURN	0	/* absorb fork error */
5016762Sralph #define DOABORT		1	/* abort if dofork fails */
5112111Sralph 
5217463Sralph /*
5317463Sralph  * Error tokens
5417463Sralph  */
5517463Sralph #define REPRINT		-2
5617463Sralph #define ERROR		-1
5717463Sralph #define	OK		0
5817463Sralph #define	FATALERR	1
5917463Sralph #define	NOACCT		2
6017463Sralph #define	FILTERERR	3
6117463Sralph #define	ACCESS		4
6217463Sralph 
6356123Selan static dev_t	 fdev;		/* device of file pointed to by symlink */
6456123Selan static ino_t	 fino;		/* inode of file pointed to by symlink */
6556123Selan static FILE	*cfp;		/* control file */
6656123Selan static int	 child;		/* id of any filters */
6756123Selan static int	 lfd;		/* lock file descriptor */
6856123Selan static int	 ofd;		/* output filter file descriptor */
6956123Selan static int	 ofilter;	/* id of output filter, if any */
7056123Selan static int	 pfd;		/* prstatic inter file descriptor */
7156123Selan static int	 pid;		/* pid of lpd process */
7256123Selan static int	 prchild;	/* id of pr process */
7356123Selan static char	 title[80];	/* ``pr'' title */
7456123Selan static int	 tof;		/* true if at top of form */
7512111Sralph 
7656123Selan static char	class[32];		/* classification field */
7756123Selan static char	fromhost[32];		/* user's host machine */
7856123Selan 				/* indentation size in static characters */
7956123Selan static char	indent[10] = "-i0";
8056123Selan static char	jobname[100];		/* job or file name */
8156123Selan static char	length[10] = "-l";	/* page length in lines */
8256123Selan static char	logname[32];		/* user's login name */
8356123Selan static char	pxlength[10] = "-y";	/* page length in pixels */
8456123Selan static char	pxwidth[10] = "-x";	/* page width in pixels */
8556123Selan static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
8656123Selan static char	width[10] = "-w";	/* page width in static characters */
8712111Sralph 
8855474Sbostic static void       abortpr __P((int));
8955474Sbostic static void       banner __P((char *, char *));
9055474Sbostic static int        dofork __P((int));
9155474Sbostic static int        dropit __P((int));
9255474Sbostic static void       init __P((void));
9355474Sbostic static void       openpr __P((void));
9469008Stef static void       opennet __P((char *));
9569008Stef static void       opentty __P((void));
9669008Stef static void       openrem __P((void));
9755474Sbostic static int        print __P((int, char *));
9855474Sbostic static int        printit __P((char *));
9955474Sbostic static void       pstatus __P((const char *, ...));
10055474Sbostic static char       response __P((void));
10155474Sbostic static void       scan_out __P((int, char *, int));
10255474Sbostic static char      *scnline __P((int, char *, int));
10355474Sbostic static int        sendfile __P((int, char *));
10455474Sbostic static int        sendit __P((char *));
10555474Sbostic static void       sendmail __P((char *, int));
10655474Sbostic static void       setty __P((void));
10755474Sbostic 
10855474Sbostic void
printjob()10912111Sralph printjob()
11012111Sralph {
11112111Sralph 	struct stat stb;
11212111Sralph 	register struct queue *q, **qp;
11312111Sralph 	struct queue **queue;
11412111Sralph 	register int i, nitems;
11568972Stef 	off_t pidoff;
11669007Stef 	int errcnt, count = 0;
11712111Sralph 
11812111Sralph 	init();					/* set up capabilities */
11913442Sralph 	(void) write(1, "", 1);			/* ack that daemon is started */
12025496Seric 	(void) close(2);			/* set up log file */
12125496Seric 	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
12225496Seric 		syslog(LOG_ERR, "%s: %m", LF);
12337968Sbostic 		(void) open(_PATH_DEVNULL, O_WRONLY);
12425496Seric 	}
12516762Sralph 	setgid(getegid());
12612463Sralph 	pid = getpid();				/* for use with lprm */
12712111Sralph 	setpgrp(0, pid);
12816762Sralph 	signal(SIGHUP, abortpr);
12916762Sralph 	signal(SIGINT, abortpr);
13016762Sralph 	signal(SIGQUIT, abortpr);
13116762Sralph 	signal(SIGTERM, abortpr);
13212111Sralph 
13339954Smckusick 	(void) mktemp(tempfile);
13415811Sralph 
13512111Sralph 	/*
13612111Sralph 	 * uses short form file names
13712111Sralph 	 */
13812111Sralph 	if (chdir(SD) < 0) {
13916762Sralph 		syslog(LOG_ERR, "%s: %m", SD);
14012111Sralph 		exit(1);
14112111Sralph 	}
14212463Sralph 	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
14312463Sralph 		exit(0);		/* printing disabled */
14414150Sralph 	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
14513169Sralph 	if (lfd < 0) {
14616762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
14713169Sralph 		exit(1);
14813169Sralph 	}
14913169Sralph 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
15012111Sralph 		if (errno == EWOULDBLOCK)	/* active deamon present */
15112111Sralph 			exit(0);
15216762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
15312111Sralph 		exit(1);
15412111Sralph 	}
15513148Ssam 	ftruncate(lfd, 0);
15612111Sralph 	/*
15712111Sralph 	 * write process id for others to know
15812111Sralph 	 */
15912111Sralph 	sprintf(line, "%u\n", pid);
16012111Sralph 	pidoff = i = strlen(line);
16112463Sralph 	if (write(lfd, line, i) != i) {
16216762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
16312111Sralph 		exit(1);
16412111Sralph 	}
16512111Sralph 	/*
16612111Sralph 	 * search the spool directory for work and sort by queue order.
16712111Sralph 	 */
16812111Sralph 	if ((nitems = getq(&queue)) < 0) {
16916762Sralph 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
17012111Sralph 		exit(1);
17112111Sralph 	}
17212463Sralph 	if (nitems == 0)		/* no work to do */
17312111Sralph 		exit(0);
17413169Sralph 	if (stb.st_mode & 01) {		/* reset queue flag */
17513169Sralph 		if (fchmod(lfd, stb.st_mode & 0776) < 0)
17616762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
17713169Sralph 	}
17812463Sralph 	openpr();			/* open printer or remote */
17912463Sralph again:
18012111Sralph 	/*
18112111Sralph 	 * we found something to do now do it --
18212111Sralph 	 *    write the name of the current control file into the lock file
18312111Sralph 	 *    so the spool queue program can tell what we're working on
18412111Sralph 	 */
18512111Sralph 	for (qp = queue; nitems--; free((char *) q)) {
18612111Sralph 		q = *qp++;
18712111Sralph 		if (stat(q->q_name, &stb) < 0)
18812111Sralph 			continue;
18969007Stef 		errcnt = 0;
19012463Sralph 	restart:
19168972Stef 		(void) lseek(lfd, pidoff, 0);
19212111Sralph 		(void) sprintf(line, "%s\n", q->q_name);
19312111Sralph 		i = strlen(line);
19412111Sralph 		if (write(lfd, line, i) != i)
19516762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
19612111Sralph 		if (!remote)
19712111Sralph 			i = printit(q->q_name);
19812111Sralph 		else
19912111Sralph 			i = sendit(q->q_name);
20012463Sralph 		/*
20113169Sralph 		 * Check to see if we are supposed to stop printing or
20213169Sralph 		 * if we are to rebuild the queue.
20312463Sralph 		 */
20413169Sralph 		if (fstat(lfd, &stb) == 0) {
20516762Sralph 			/* stop printing before starting next job? */
20613169Sralph 			if (stb.st_mode & 0100)
20713169Sralph 				goto done;
20816762Sralph 			/* rebuild queue (after lpc topq) */
20913169Sralph 			if (stb.st_mode & 01) {
21013169Sralph 				for (free((char *) q); nitems--; free((char *) q))
21113169Sralph 					q = *qp++;
21213169Sralph 				if (fchmod(lfd, stb.st_mode & 0776) < 0)
21316762Sralph 					syslog(LOG_WARNING, "%s: %s: %m",
21416762Sralph 						printer, LO);
21513169Sralph 				break;
21613169Sralph 			}
21713169Sralph 		}
21817463Sralph 		if (i == OK)		/* file ok and printed */
21914150Sralph 			count++;
22069007Stef 		else if (i == REPRINT && ++errcnt < 5) {
22169007Stef 			/* try reprinting the job */
22216762Sralph 			syslog(LOG_INFO, "restarting %s", printer);
22312111Sralph 			if (ofilter > 0) {
22412111Sralph 				kill(ofilter, SIGCONT);	/* to be sure */
22512111Sralph 				(void) close(ofd);
22669061Stef 				while ((i = wait(NULL)) > 0 && i != ofilter)
22712111Sralph 					;
22812111Sralph 				ofilter = 0;
22912111Sralph 			}
23012463Sralph 			(void) close(pfd);	/* close printer */
23115811Sralph 			if (ftruncate(lfd, pidoff) < 0)
23216762Sralph 				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
23312463Sralph 			openpr();		/* try to reopen printer */
23412111Sralph 			goto restart;
23569007Stef 		} else {
23669007Stef 			syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
23769007Stef 				remote ? "sent to remote host" : "printed", q->q_name);
23869007Stef 			if (i == REPRINT) {
23969007Stef 				/* insure we don't attempt this job again */
24069007Stef 				(void) unlink(q->q_name);
24169007Stef 				q->q_name[0] = 'd';
24269007Stef 				(void) unlink(q->q_name);
24369007Stef 				if (logname[0])
24469007Stef 					sendmail(logname, FATALERR);
24569007Stef 			}
24612111Sralph 		}
24712111Sralph 	}
24812111Sralph 	free((char *) queue);
24912463Sralph 	/*
25012463Sralph 	 * search the spool directory for more work.
25112463Sralph 	 */
25212463Sralph 	if ((nitems = getq(&queue)) < 0) {
25316762Sralph 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
25412463Sralph 		exit(1);
25512463Sralph 	}
25612463Sralph 	if (nitems == 0) {		/* no more work to do */
25712463Sralph 	done:
25814150Sralph 		if (count > 0) {	/* Files actually printed */
25914150Sralph 			if (!SF && !tof)
26014150Sralph 				(void) write(ofd, FF, strlen(FF));
26114150Sralph 			if (TR != NULL)		/* output trailer */
26214150Sralph 				(void) write(ofd, TR, strlen(TR));
26314150Sralph 		}
26439954Smckusick 		(void) unlink(tempfile);
26512463Sralph 		exit(0);
26612463Sralph 	}
26712111Sralph 	goto again;
26812111Sralph }
26912111Sralph 
27012111Sralph char	fonts[4][50];	/* fonts for troff */
27112111Sralph 
27237968Sbostic char ifonts[4][40] = {
27337968Sbostic 	_PATH_VFONTR,
27437968Sbostic 	_PATH_VFONTI,
27537968Sbostic 	_PATH_VFONTB,
27637968Sbostic 	_PATH_VFONTS,
27712111Sralph };
27812111Sralph 
27912111Sralph /*
28012111Sralph  * The remaining part is the reading of the control file (cf)
28112111Sralph  * and performing the various actions.
28212111Sralph  */
28355474Sbostic static int
printit(file)28412111Sralph printit(file)
28512111Sralph 	char *file;
28612111Sralph {
28712111Sralph 	register int i;
28817463Sralph 	char *cp;
28917463Sralph 	int bombed = OK;
29012111Sralph 
29112111Sralph 	/*
29217463Sralph 	 * open control file; ignore if no longer there.
29312111Sralph 	 */
29412111Sralph 	if ((cfp = fopen(file, "r")) == NULL) {
29516762Sralph 		syslog(LOG_INFO, "%s: %s: %m", printer, file);
29617463Sralph 		return(OK);
29712111Sralph 	}
29812111Sralph 	/*
29912111Sralph 	 * Reset troff fonts.
30012111Sralph 	 */
30112111Sralph 	for (i = 0; i < 4; i++)
30212111Sralph 		strcpy(fonts[i], ifonts[i]);
30366833Sbostic 	sprintf(&width[2], "%d", PW);
30417302Sralph 	strcpy(indent+2, "0");
30512111Sralph 
30612111Sralph 	/*
30712111Sralph 	 *      read the control file for work to do
30812111Sralph 	 *
30912111Sralph 	 *      file format -- first character in the line is a command
31012111Sralph 	 *      rest of the line is the argument.
31112111Sralph 	 *      valid commands are:
31212111Sralph 	 *
31317463Sralph 	 *		S -- "stat info" for symbolic link protection
31412111Sralph 	 *		J -- "job name" on banner page
31512111Sralph 	 *		C -- "class name" on banner page
31612111Sralph 	 *              L -- "literal" user's name to print on banner
31712111Sralph 	 *		T -- "title" for pr
31812111Sralph 	 *		H -- "host name" of machine where lpr was done
31912111Sralph 	 *              P -- "person" user's login name
32012581Sralph 	 *              I -- "indent" amount to indent output
32169007Stef 	 *		R -- laser dpi "resolution"
32212111Sralph 	 *              f -- "file name" name of text file to print
32312111Sralph 	 *		l -- "file name" text file with control chars
32412111Sralph 	 *		p -- "file name" text file to print with pr(1)
32512111Sralph 	 *		t -- "file name" troff(1) file to print
32613233Sralph 	 *		n -- "file name" ditroff(1) file to print
32712111Sralph 	 *		d -- "file name" dvi file to print
32812111Sralph 	 *		g -- "file name" plot(1G) file to print
32912111Sralph 	 *		v -- "file name" plain raster file to print
33012111Sralph 	 *		c -- "file name" cifplot file to print
33112111Sralph 	 *		1 -- "R font file" for troff
33212111Sralph 	 *		2 -- "I font file" for troff
33312111Sralph 	 *		3 -- "B font file" for troff
33412111Sralph 	 *		4 -- "S font file" for troff
33512111Sralph 	 *		N -- "name" of file (used by lpq)
33612111Sralph 	 *              U -- "unlink" name of file to remove
33712111Sralph 	 *                    (after we print it. (Pass 2 only)).
33812111Sralph 	 *		M -- "mail" to user when done printing
33912111Sralph 	 *
34012111Sralph 	 *      getline reads a line and expands tabs to blanks
34112111Sralph 	 */
34212111Sralph 
34312111Sralph 	/* pass 1 */
34412111Sralph 
34512111Sralph 	while (getline(cfp))
34612111Sralph 		switch (line[0]) {
34712111Sralph 		case 'H':
34814150Sralph 			strcpy(fromhost, line+1);
34912111Sralph 			if (class[0] == '\0')
35015552Sralph 				strncpy(class, line+1, sizeof(class)-1);
35112111Sralph 			continue;
35212111Sralph 
35312111Sralph 		case 'P':
35415552Sralph 			strncpy(logname, line+1, sizeof(logname)-1);
35512463Sralph 			if (RS) {			/* restricted */
35655474Sbostic 				if (getpwnam(logname) == NULL) {
35717463Sralph 					bombed = NOACCT;
35815811Sralph 					sendmail(line+1, bombed);
35912463Sralph 					goto pass2;
36012463Sralph 				}
36112463Sralph 			}
36212111Sralph 			continue;
36312111Sralph 
36417463Sralph 		case 'S':
36517463Sralph 			cp = line+1;
36617463Sralph 			i = 0;
36717463Sralph 			while (*cp >= '0' && *cp <= '9')
36817463Sralph 				i = i * 10 + (*cp++ - '0');
36917463Sralph 			fdev = i;
37017463Sralph 			cp++;
37117463Sralph 			i = 0;
37217463Sralph 			while (*cp >= '0' && *cp <= '9')
37317463Sralph 				i = i * 10 + (*cp++ - '0');
37417463Sralph 			fino = i;
37517463Sralph 			continue;
37617463Sralph 
37712111Sralph 		case 'J':
37812111Sralph 			if (line[1] != '\0')
37915552Sralph 				strncpy(jobname, line+1, sizeof(jobname)-1);
38012111Sralph 			else
38112111Sralph 				strcpy(jobname, " ");
38212111Sralph 			continue;
38312111Sralph 
38412111Sralph 		case 'C':
38512111Sralph 			if (line[1] != '\0')
38615552Sralph 				strncpy(class, line+1, sizeof(class)-1);
38712111Sralph 			else if (class[0] == '\0')
38815811Sralph 				gethostname(class, sizeof(class));
38912111Sralph 			continue;
39012111Sralph 
39112111Sralph 		case 'T':	/* header title for pr */
39215552Sralph 			strncpy(title, line+1, sizeof(title)-1);
39312111Sralph 			continue;
39412111Sralph 
39512111Sralph 		case 'L':	/* identification line */
39618127Sralph 			if (!SH && !HL)
39712111Sralph 				banner(line+1, jobname);
39812111Sralph 			continue;
39912111Sralph 
40012111Sralph 		case '1':	/* troff fonts */
40112111Sralph 		case '2':
40212111Sralph 		case '3':
40312111Sralph 		case '4':
40412111Sralph 			if (line[1] != '\0')
40512111Sralph 				strcpy(fonts[line[0]-'1'], line+1);
40612111Sralph 			continue;
40712111Sralph 
40812111Sralph 		case 'W':	/* page width */
40915552Sralph 			strncpy(width+2, line+1, sizeof(width)-3);
41012111Sralph 			continue;
41112111Sralph 
41212581Sralph 		case 'I':	/* indent amount */
41315552Sralph 			strncpy(indent+2, line+1, sizeof(indent)-3);
41412581Sralph 			continue;
41512581Sralph 
41612111Sralph 		default:	/* some file to print */
41715811Sralph 			switch (i = print(line[0], line+1)) {
41817463Sralph 			case ERROR:
41917463Sralph 				if (bombed == OK)
42017463Sralph 					bombed = FATALERR;
42115811Sralph 				break;
42217463Sralph 			case REPRINT:
42312111Sralph 				(void) fclose(cfp);
42417463Sralph 				return(REPRINT);
42517463Sralph 			case FILTERERR:
42617463Sralph 			case ACCESS:
42717463Sralph 				bombed = i;
42815811Sralph 				sendmail(logname, bombed);
42915811Sralph 			}
43012111Sralph 			title[0] = '\0';
43112111Sralph 			continue;
43212111Sralph 
43312111Sralph 		case 'N':
43412111Sralph 		case 'U':
43512111Sralph 		case 'M':
43669007Stef 		case 'R':
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
print(format,file)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 */
53268972Stef 			closelog();
53312111Sralph 			for (n = 3; n < NOFILE; n++)
53412111Sralph 				(void) close(n);
53537968Sbostic 			execl(_PATH_PR, "pr", width, length,
53637968Sbostic 			    "-h", *title ? title : " ", 0);
53737968Sbostic 			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
53812111Sralph 			exit(2);
53912111Sralph 		}
54012111Sralph 		(void) close(p[1]);		/* close output side */
54112111Sralph 		(void) close(fi);
54212111Sralph 		if (prchild < 0) {
54312111Sralph 			prchild = 0;
54412111Sralph 			(void) close(p[0]);
54517463Sralph 			return(ERROR);
54612111Sralph 		}
54712111Sralph 		fi = p[0];			/* use pipe for input */
54812111Sralph 	case 'f':	/* print plain text file */
54912111Sralph 		prog = IF;
55012111Sralph 		av[1] = width;
55112111Sralph 		av[2] = length;
55212581Sralph 		av[3] = indent;
55312581Sralph 		n = 4;
55412111Sralph 		break;
55512111Sralph 	case 'l':	/* like 'f' but pass control characters */
55612111Sralph 		prog = IF;
55714325Sralph 		av[1] = "-c";
55812111Sralph 		av[2] = width;
55912111Sralph 		av[3] = length;
56012581Sralph 		av[4] = indent;
56112581Sralph 		n = 5;
56212111Sralph 		break;
56312463Sralph 	case 'r':	/* print a fortran text file */
56412463Sralph 		prog = RF;
56512463Sralph 		av[1] = width;
56612463Sralph 		av[2] = length;
56712463Sralph 		n = 3;
56812463Sralph 		break;
56912111Sralph 	case 't':	/* print troff output */
57013233Sralph 	case 'n':	/* print ditroff output */
57112463Sralph 	case 'd':	/* print tex output */
57212111Sralph 		(void) unlink(".railmag");
57312463Sralph 		if ((fo = creat(".railmag", FILMOD)) < 0) {
57416762Sralph 			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
57512111Sralph 			(void) unlink(".railmag");
57612111Sralph 		} else {
57712111Sralph 			for (n = 0; n < 4; n++) {
57812111Sralph 				if (fonts[n][0] != '/')
57954520Sbostic 					(void) write(fo, _PATH_VFONT,
58054520Sbostic 					    sizeof(_PATH_VFONT) - 1);
58112111Sralph 				(void) write(fo, fonts[n], strlen(fonts[n]));
58212111Sralph 				(void) write(fo, "\n", 1);
58312111Sralph 			}
58412111Sralph 			(void) close(fo);
58512111Sralph 		}
58613233Sralph 		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
58712463Sralph 		av[1] = pxwidth;
58812463Sralph 		av[2] = pxlength;
58912463Sralph 		n = 3;
59012111Sralph 		break;
59112111Sralph 	case 'c':	/* print cifplot output */
59212111Sralph 		prog = CF;
59312463Sralph 		av[1] = pxwidth;
59412463Sralph 		av[2] = pxlength;
59512463Sralph 		n = 3;
59612111Sralph 		break;
59712111Sralph 	case 'g':	/* print plot(1G) output */
59812111Sralph 		prog = GF;
59912463Sralph 		av[1] = pxwidth;
60012463Sralph 		av[2] = pxlength;
60112463Sralph 		n = 3;
60212111Sralph 		break;
60312111Sralph 	case 'v':	/* print raster output */
60412111Sralph 		prog = VF;
60512463Sralph 		av[1] = pxwidth;
60612463Sralph 		av[2] = pxlength;
60712463Sralph 		n = 3;
60812111Sralph 		break;
60912111Sralph 	default:
61012111Sralph 		(void) close(fi);
61116762Sralph 		syslog(LOG_ERR, "%s: illegal format character '%c'",
61216762Sralph 			printer, format);
61317463Sralph 		return(ERROR);
61412111Sralph 	}
61569007Stef 	if (prog == NULL) {
61669007Stef 		(void) close(fi);
61769007Stef 		syslog(LOG_ERR,
61869007Stef 		   "%s: no filter found in printcap for format character '%c'",
61969007Stef 		   printer, format);
62069007Stef 		return(ERROR);
62169007Stef 	}
62212111Sralph 	if ((av[0] = rindex(prog, '/')) != NULL)
62312111Sralph 		av[0]++;
62412111Sralph 	else
62512111Sralph 		av[0] = prog;
62612111Sralph 	av[n++] = "-n";
62712111Sralph 	av[n++] = logname;
62812111Sralph 	av[n++] = "-h";
62914150Sralph 	av[n++] = fromhost;
63012111Sralph 	av[n++] = AF;
63112111Sralph 	av[n] = 0;
63212111Sralph 	fo = pfd;
63312111Sralph 	if (ofilter > 0) {		/* stop output filter */
63412111Sralph 		write(ofd, "\031\1", 2);
63546912Sbostic 		while ((pid =
63646912Sbostic 		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
63712111Sralph 			;
63812111Sralph 		if (status.w_stopval != WSTOPPED) {
63912111Sralph 			(void) close(fi);
64069007Stef 			syslog(LOG_WARNING,
64169007Stef 				"%s: output filter died (retcode=%d termsig=%d)",
64269007Stef 				printer, status.w_retcode, status.w_termsig);
64317463Sralph 			return(REPRINT);
64412111Sralph 		}
64512111Sralph 		stopped++;
64612111Sralph 	}
64712111Sralph start:
64812111Sralph 	if ((child = dofork(DORETURN)) == 0) {	/* child */
64912111Sralph 		dup2(fi, 0);
65012111Sralph 		dup2(fo, 1);
65139954Smckusick 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
65215811Sralph 		if (n >= 0)
65315811Sralph 			dup2(n, 2);
65468972Stef 		closelog();
65512111Sralph 		for (n = 3; n < NOFILE; n++)
65612111Sralph 			(void) close(n);
65712111Sralph 		execv(prog, av);
65816762Sralph 		syslog(LOG_ERR, "cannot execv %s", prog);
65912111Sralph 		exit(2);
66012111Sralph 	}
66112111Sralph 	(void) close(fi);
66212111Sralph 	if (child < 0)
66312111Sralph 		status.w_retcode = 100;
66412111Sralph 	else
66546912Sbostic 		while ((pid = wait((int *)&status)) > 0 && pid != child)
66612111Sralph 			;
66712111Sralph 	child = 0;
66812111Sralph 	prchild = 0;
66912111Sralph 	if (stopped) {		/* restart output filter */
67012111Sralph 		if (kill(ofilter, SIGCONT) < 0) {
67116762Sralph 			syslog(LOG_ERR, "cannot restart output filter");
67212111Sralph 			exit(1);
67312111Sralph 		}
67412111Sralph 	}
67512111Sralph 	tof = 0;
67639954Smckusick 
67739954Smckusick 	/* Copy filter output to "lf" logfile */
67839954Smckusick 	if (fp = fopen(tempfile, "r")) {
67939954Smckusick 		while (fgets(buf, sizeof(buf), fp))
68039954Smckusick 			fputs(buf, stderr);
68156123Selan 		fclose(fp);
68239954Smckusick 	}
68339954Smckusick 
68415811Sralph 	if (!WIFEXITED(status)) {
68569007Stef 		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
68616762Sralph 			printer, format, status.w_termsig);
68717463Sralph 		return(ERROR);
68817463Sralph 	}
68917463Sralph 	switch (status.w_retcode) {
69017463Sralph 	case 0:
69117463Sralph 		tof = 1;
69217463Sralph 		return(OK);
69317463Sralph 	case 1:
69417463Sralph 		return(REPRINT);
69569007Stef 	case 2:
69669007Stef 		return(ERROR);
69717463Sralph 	default:
69869007Stef 		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
69916762Sralph 			printer, format, status.w_retcode);
70069007Stef 		return(FILTERERR);
70117463Sralph 	}
70212111Sralph }
70312111Sralph 
70412111Sralph /*
70512111Sralph  * Send the daemon control file (cf) and any data files.
70612111Sralph  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
70712111Sralph  * 0 if all is well.
70812111Sralph  */
70955474Sbostic static int
sendit(file)71012111Sralph sendit(file)
71112111Sralph 	char *file;
71212111Sralph {
71317463Sralph 	register int i, err = OK;
71417463Sralph 	char *cp, last[BUFSIZ];
71512111Sralph 
71612111Sralph 	/*
71712111Sralph 	 * open control file
71812111Sralph 	 */
71916762Sralph 	if ((cfp = fopen(file, "r")) == NULL)
72017463Sralph 		return(OK);
72112111Sralph 	/*
72212111Sralph 	 *      read the control file for work to do
72312111Sralph 	 *
72412111Sralph 	 *      file format -- first character in the line is a command
72512111Sralph 	 *      rest of the line is the argument.
72612111Sralph 	 *      commands of interest are:
72712111Sralph 	 *
72812111Sralph 	 *            a-z -- "file name" name of file to print
72912111Sralph 	 *              U -- "unlink" name of file to remove
73012111Sralph 	 *                    (after we print it. (Pass 2 only)).
73112111Sralph 	 */
73212111Sralph 
73312111Sralph 	/*
73412111Sralph 	 * pass 1
73512111Sralph 	 */
73612111Sralph 	while (getline(cfp)) {
73712111Sralph 	again:
73817463Sralph 		if (line[0] == 'S') {
73917463Sralph 			cp = line+1;
74017463Sralph 			i = 0;
74117463Sralph 			while (*cp >= '0' && *cp <= '9')
74217463Sralph 				i = i * 10 + (*cp++ - '0');
74317463Sralph 			fdev = i;
74417463Sralph 			cp++;
74517463Sralph 			i = 0;
74617463Sralph 			while (*cp >= '0' && *cp <= '9')
74717463Sralph 				i = i * 10 + (*cp++ - '0');
74817463Sralph 			fino = i;
74917463Sralph 			continue;
75017463Sralph 		}
75112111Sralph 		if (line[0] >= 'a' && line[0] <= 'z') {
75212111Sralph 			strcpy(last, line);
75317463Sralph 			while (i = getline(cfp))
75412111Sralph 				if (strcmp(last, line))
75512111Sralph 					break;
75617463Sralph 			switch (sendfile('\3', last+1)) {
75717463Sralph 			case OK:
75817463Sralph 				if (i)
75917463Sralph 					goto again;
76017463Sralph 				break;
76117463Sralph 			case REPRINT:
76212111Sralph 				(void) fclose(cfp);
76317463Sralph 				return(REPRINT);
76417463Sralph 			case ACCESS:
76517463Sralph 				sendmail(logname, ACCESS);
76617463Sralph 			case ERROR:
76717463Sralph 				err = ERROR;
76817463Sralph 			}
76912111Sralph 			break;
77012111Sralph 		}
77112111Sralph 	}
77217463Sralph 	if (err == OK && sendfile('\2', file) > 0) {
77312111Sralph 		(void) fclose(cfp);
77417463Sralph 		return(REPRINT);
77512111Sralph 	}
77612111Sralph 	/*
77712111Sralph 	 * pass 2
77812111Sralph 	 */
77912111Sralph 	fseek(cfp, 0L, 0);
78012111Sralph 	while (getline(cfp))
78112111Sralph 		if (line[0] == 'U')
78212111Sralph 			(void) unlink(line+1);
78312111Sralph 	/*
78417463Sralph 	 * clean-up in case another control file exists
78512111Sralph 	 */
78612111Sralph 	(void) fclose(cfp);
78712111Sralph 	(void) unlink(file);
78817463Sralph 	return(err);
78912111Sralph }
79012111Sralph 
79112111Sralph /*
79212111Sralph  * Send a data file to the remote machine and spool it.
79312111Sralph  * Return positive if we should try resending.
79412111Sralph  */
79555474Sbostic static int
sendfile(type,file)79612111Sralph sendfile(type, file)
79755474Sbostic 	int type;
79855474Sbostic 	char *file;
79912111Sralph {
80012111Sralph 	register int f, i, amt;
80112111Sralph 	struct stat stb;
80212111Sralph 	char buf[BUFSIZ];
80316762Sralph 	int sizerr, resp;
80412111Sralph 
80517463Sralph 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
80617463Sralph 		return(ERROR);
80717463Sralph 	/*
80817463Sralph 	 * Check to see if data file is a symbolic link. If so, it should
80917463Sralph 	 * still point to the same file or someone is trying to print something
81017463Sralph 	 * he shouldn't.
81117463Sralph 	 */
81217463Sralph 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
81317463Sralph 	    (stb.st_dev != fdev || stb.st_ino != fino))
81417463Sralph 		return(ACCESS);
81568972Stef 	(void) sprintf(buf, "%c%ld %s\n", type, (long)stb.st_size, file);
81612111Sralph 	amt = strlen(buf);
81716762Sralph 	for (i = 0;  ; i++) {
81816762Sralph 		if (write(pfd, buf, amt) != amt ||
81916762Sralph 		    (resp = response()) < 0 || resp == '\1') {
82016762Sralph 			(void) close(f);
82117463Sralph 			return(REPRINT);
82216762Sralph 		} else if (resp == '\0')
82316762Sralph 			break;
82416762Sralph 		if (i == 0)
82555474Sbostic 			pstatus("no space on remote; waiting for queue to drain");
82616762Sralph 		if (i == 10)
82724861Seric 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
82816762Sralph 				printer, RM);
82916762Sralph 		sleep(5 * 60);
83012692Sralph 	}
83116762Sralph 	if (i)
83255474Sbostic 		pstatus("sending to %s", RM);
83312111Sralph 	sizerr = 0;
83412111Sralph 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
83512111Sralph 		amt = BUFSIZ;
83612111Sralph 		if (i + amt > stb.st_size)
83712111Sralph 			amt = stb.st_size - i;
83812111Sralph 		if (sizerr == 0 && read(f, buf, amt) != amt)
83912111Sralph 			sizerr = 1;
84012692Sralph 		if (write(pfd, buf, amt) != amt) {
84112692Sralph 			(void) close(f);
84217463Sralph 			return(REPRINT);
84312692Sralph 		}
84412111Sralph 	}
84555474Sbostic 
84655474Sbostic 
84755474Sbostic 
84855474Sbostic 
84912111Sralph 	(void) close(f);
85012111Sralph 	if (sizerr) {
85116762Sralph 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
85217463Sralph 		/* tell recvjob to ignore this file */
85317463Sralph 		(void) write(pfd, "\1", 1);
85417463Sralph 		return(ERROR);
85517463Sralph 	}
85617463Sralph 	if (write(pfd, "", 1) != 1 || response())
85717463Sralph 		return(REPRINT);
85817463Sralph 	return(OK);
85912111Sralph }
86012111Sralph 
86112111Sralph /*
86212111Sralph  * Check to make sure there have been no errors and that both programs
86312111Sralph  * are in sync with eachother.
86412111Sralph  * Return non-zero if the connection was lost.
86512111Sralph  */
86655474Sbostic static char
response()86716762Sralph response()
86812111Sralph {
86912111Sralph 	char resp;
87012111Sralph 
87116762Sralph 	if (read(pfd, &resp, 1) != 1) {
87216762Sralph 		syslog(LOG_INFO, "%s: lost connection", printer);
87316762Sralph 		return(-1);
87412111Sralph 	}
87516762Sralph 	return(resp);
87612111Sralph }
87712111Sralph 
87812111Sralph /*
87912111Sralph  * Banner printing stuff
88012111Sralph  */
88155474Sbostic static void
banner(name1,name2)88212111Sralph banner(name1, name2)
88312111Sralph 	char *name1, *name2;
88412111Sralph {
88512111Sralph 	time_t tvec;
88612111Sralph 	extern char *ctime();
88712111Sralph 
88812111Sralph 	time(&tvec);
88912111Sralph 	if (!SF && !tof)
89012111Sralph 		(void) write(ofd, FF, strlen(FF));
89112111Sralph 	if (SB) {	/* short banner only */
89212111Sralph 		if (class[0]) {
89312111Sralph 			(void) write(ofd, class, strlen(class));
89412111Sralph 			(void) write(ofd, ":", 1);
89512111Sralph 		}
89612111Sralph 		(void) write(ofd, name1, strlen(name1));
89712111Sralph 		(void) write(ofd, "  Job: ", 7);
89812111Sralph 		(void) write(ofd, name2, strlen(name2));
89912111Sralph 		(void) write(ofd, "  Date: ", 8);
90012111Sralph 		(void) write(ofd, ctime(&tvec), 24);
90112111Sralph 		(void) write(ofd, "\n", 1);
90212111Sralph 	} else {	/* normal banner */
90312111Sralph 		(void) write(ofd, "\n\n\n", 3);
90412111Sralph 		scan_out(ofd, name1, '\0');
90512111Sralph 		(void) write(ofd, "\n\n", 2);
90612111Sralph 		scan_out(ofd, name2, '\0');
90712111Sralph 		if (class[0]) {
90812111Sralph 			(void) write(ofd,"\n\n\n",3);
90912111Sralph 			scan_out(ofd, class, '\0');
91012111Sralph 		}
91112111Sralph 		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
91212111Sralph 		(void) write(ofd, name2, strlen(name2));
91312111Sralph 		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
91412111Sralph 		(void) write(ofd, ctime(&tvec), 24);
91512111Sralph 		(void) write(ofd, "\n", 1);
91612111Sralph 	}
91712111Sralph 	if (!SF)
91812111Sralph 		(void) write(ofd, FF, strlen(FF));
91912111Sralph 	tof = 1;
92012111Sralph }
92112111Sralph 
92255474Sbostic static char *
scnline(key,p,c)92312111Sralph scnline(key, p, c)
92455474Sbostic 	register int key;
92555474Sbostic 	register char *p;
92655474Sbostic 	int c;
92712111Sralph {
92812111Sralph 	register scnwidth;
92912111Sralph 
93012111Sralph 	for (scnwidth = WIDTH; --scnwidth;) {
93112111Sralph 		key <<= 1;
93212111Sralph 		*p++ = key & 0200 ? c : BACKGND;
93312111Sralph 	}
93412111Sralph 	return (p);
93512111Sralph }
93612111Sralph 
93712111Sralph #define TRC(q)	(((q)-' ')&0177)
93812111Sralph 
93955474Sbostic static void
scan_out(scfd,scsp,dlm)94012111Sralph scan_out(scfd, scsp, dlm)
94155474Sbostic 	int scfd, dlm;
94255474Sbostic 	char *scsp;
94312111Sralph {
94412111Sralph 	register char *strp;
94512111Sralph 	register nchrs, j;
94612111Sralph 	char outbuf[LINELEN+1], *sp, c, cc;
94712111Sralph 	int d, scnhgt;
94812111Sralph 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
94912111Sralph 
95012111Sralph 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
95112111Sralph 		strp = &outbuf[0];
95212111Sralph 		sp = scsp;
95312111Sralph 		for (nchrs = 0; ; ) {
95412111Sralph 			d = dropit(c = TRC(cc = *sp++));
95512111Sralph 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
95612111Sralph 				for (j = WIDTH; --j;)
95712111Sralph 					*strp++ = BACKGND;
95812111Sralph 			else
95912111Sralph 				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
96012111Sralph 			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
96112111Sralph 				break;
96212111Sralph 			*strp++ = BACKGND;
96312111Sralph 			*strp++ = BACKGND;
96412111Sralph 		}
96512111Sralph 		while (*--strp == BACKGND && strp >= outbuf)
96612111Sralph 			;
96712111Sralph 		strp++;
96812111Sralph 		*strp++ = '\n';
96912111Sralph 		(void) write(scfd, outbuf, strp-outbuf);
97012111Sralph 	}
97112111Sralph }
97212111Sralph 
97355474Sbostic static int
dropit(c)97412111Sralph dropit(c)
97555474Sbostic 	int c;
97612111Sralph {
97712111Sralph 	switch(c) {
97812111Sralph 
97912111Sralph 	case TRC('_'):
98012111Sralph 	case TRC(';'):
98112111Sralph 	case TRC(','):
98212111Sralph 	case TRC('g'):
98312111Sralph 	case TRC('j'):
98412111Sralph 	case TRC('p'):
98512111Sralph 	case TRC('q'):
98612111Sralph 	case TRC('y'):
98712111Sralph 		return (DROP);
98812111Sralph 
98912111Sralph 	default:
99012111Sralph 		return (0);
99112111Sralph 	}
99212111Sralph }
99312111Sralph 
99412111Sralph /*
99512111Sralph  * sendmail ---
99612111Sralph  *   tell people about job completion
99712111Sralph  */
99855474Sbostic static void
sendmail(user,bombed)99915811Sralph sendmail(user, bombed)
100015811Sralph 	char *user;
100112111Sralph 	int bombed;
100212111Sralph {
100312111Sralph 	register int i;
100415811Sralph 	int p[2], s;
100512111Sralph 	register char *cp;
100612111Sralph 	char buf[100];
100715811Sralph 	struct stat stb;
100815811Sralph 	FILE *fp;
100912111Sralph 
101012111Sralph 	pipe(p);
101115811Sralph 	if ((s = dofork(DORETURN)) == 0) {		/* child */
101212111Sralph 		dup2(p[0], 0);
101368972Stef 		closelog();
101412111Sralph 		for (i = 3; i < NOFILE; i++)
101512111Sralph 			(void) close(i);
101637968Sbostic 		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
101712111Sralph 			cp++;
101855474Sbostic 	else
101937968Sbostic 			cp = _PATH_SENDMAIL;
102015811Sralph 		sprintf(buf, "%s@%s", user, fromhost);
102137968Sbostic 		execl(_PATH_SENDMAIL, cp, buf, 0);
102212111Sralph 		exit(0);
102315811Sralph 	} else if (s > 0) {				/* parent */
102412111Sralph 		dup2(p[1], 1);
102515811Sralph 		printf("To: %s@%s\n", user, fromhost);
102669007Stef 		printf("Subject: %s printer job \"%s\"\n", printer,
102769007Stef 			*jobname ? jobname : "<unknown>");
102869007Stef 		printf("Reply-To: root@%s\n\n", host);
102912111Sralph 		printf("Your printer job ");
103012111Sralph 		if (*jobname)
103112111Sralph 			printf("(%s) ", jobname);
103212463Sralph 		switch (bombed) {
103317463Sralph 		case OK:
103412463Sralph 			printf("\ncompleted successfully\n");
103569008Stef 			cp = "OK";
103612463Sralph 			break;
103712463Sralph 		default:
103817463Sralph 		case FATALERR:
103912463Sralph 			printf("\ncould not be printed\n");
104069008Stef 			cp = "FATALERR";
104112463Sralph 			break;
104217463Sralph 		case NOACCT:
104312463Sralph 			printf("\ncould not be printed without an account on %s\n", host);
104469008Stef 			cp = "NOACCT";
104512463Sralph 			break;
104617463Sralph 		case FILTERERR:
104739954Smckusick 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
104839954Smckusick 			    (fp = fopen(tempfile, "r")) == NULL) {
104969007Stef 				printf("\nhad some errors and may not have printed\n");
105015811Sralph 				break;
105115811Sralph 			}
105269007Stef 			printf("\nhad the following errors and may not have printed:\n");
105315811Sralph 			while ((i = getc(fp)) != EOF)
105415811Sralph 				putchar(i);
105515811Sralph 			(void) fclose(fp);
105669008Stef 			cp = "FILTERERR";
105717463Sralph 			break;
105817463Sralph 		case ACCESS:
105917463Sralph 			printf("\nwas not printed because it was not linked to the original file\n");
106069008Stef 			cp = "ACCESS";
106112463Sralph 		}
106212111Sralph 		fflush(stdout);
106312111Sralph 		(void) close(1);
106412111Sralph 	}
106512111Sralph 	(void) close(p[0]);
106612111Sralph 	(void) close(p[1]);
106769061Stef 	wait(NULL);
106869008Stef 	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
106969008Stef 		user, *jobname ? jobname : "<unknown>", printer, cp);
107012111Sralph }
107112111Sralph 
107212111Sralph /*
107312111Sralph  * dofork - fork with retries on failure
107412111Sralph  */
107555474Sbostic static int
dofork(action)107612111Sralph dofork(action)
107712111Sralph 	int action;
107812111Sralph {
107912111Sralph 	register int i, pid;
108012111Sralph 
108112111Sralph 	for (i = 0; i < 20; i++) {
108212463Sralph 		if ((pid = fork()) < 0) {
108312111Sralph 			sleep((unsigned)(i*i));
108412463Sralph 			continue;
108512463Sralph 		}
108612463Sralph 		/*
108712463Sralph 		 * Child should run as daemon instead of root
108812463Sralph 		 */
108912463Sralph 		if (pid == 0)
109012463Sralph 			setuid(DU);
109112463Sralph 		return(pid);
109212111Sralph 	}
109316762Sralph 	syslog(LOG_ERR, "can't fork");
109412111Sralph 
109512111Sralph 	switch (action) {
109612111Sralph 	case DORETURN:
109712111Sralph 		return (-1);
109812111Sralph 	default:
109916762Sralph 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
110012111Sralph 		/*FALL THRU*/
110112111Sralph 	case DOABORT:
110212111Sralph 		exit(1);
110312111Sralph 	}
110412111Sralph 	/*NOTREACHED*/
110512111Sralph }
110612111Sralph 
110712111Sralph /*
110816762Sralph  * Kill child processes to abort current job.
110912111Sralph  */
111055474Sbostic static void
abortpr(signo)111155474Sbostic abortpr(signo)
111255474Sbostic 	int signo;
111312111Sralph {
111439954Smckusick 	(void) unlink(tempfile);
111512111Sralph 	kill(0, SIGINT);
111612111Sralph 	if (ofilter > 0)
111712111Sralph 		kill(ofilter, SIGCONT);
111846912Sbostic 	while (wait(NULL) > 0)
111912111Sralph 		;
112012111Sralph 	exit(0);
112112111Sralph }
112212111Sralph 
112355474Sbostic static void
init()112412111Sralph init()
112512111Sralph {
112612111Sralph 	int status;
112738736Stef 	char *s;
112812111Sralph 
112956123Selan 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
113025468Stef 		syslog(LOG_ERR, "can't open printer description file");
113125468Stef 		exit(1);
113256123Selan 	} else if (status == -1) {
113325468Stef 		syslog(LOG_ERR, "unknown printer: %s", printer);
113425468Stef 		exit(1);
113556123Selan 	} else if (status == -3)
113656123Selan 		fatal("potential reference loop detected in printcap file");
113756123Selan 
113856123Selan 	if (cgetstr(bp, "lp", &LP) == -1)
113937968Sbostic 		LP = _PATH_DEFDEVLP;
114056123Selan 	if (cgetstr(bp, "rp", &RP) == -1)
114112463Sralph 		RP = DEFLP;
114256123Selan 	if (cgetstr(bp, "lo", &LO) == -1)
114312111Sralph 		LO = DEFLOCK;
114456123Selan 	if (cgetstr(bp, "st", &ST) == -1)
114512111Sralph 		ST = DEFSTAT;
114656123Selan 	if (cgetstr(bp, "lf", &LF) == -1)
114737968Sbostic 		LF = _PATH_CONSOLE;
114856123Selan 	if (cgetstr(bp, "sd", &SD) == -1)
114937968Sbostic 		SD = _PATH_DEFSPOOL;
115056123Selan 	if (cgetnum(bp, "du", &DU) < 0)
115112111Sralph 		DU = DEFUID;
115256123Selan 	if (cgetstr(bp,"ff", &FF) == -1)
115312111Sralph 		FF = DEFFF;
115456123Selan 	if (cgetnum(bp, "pw", &PW) < 0)
115512111Sralph 		PW = DEFWIDTH;
115612111Sralph 	sprintf(&width[2], "%d", PW);
115756123Selan 	if (cgetnum(bp, "pl", &PL) < 0)
115812111Sralph 		PL = DEFLENGTH;
115912111Sralph 	sprintf(&length[2], "%d", PL);
116056123Selan 	if (cgetnum(bp,"px", &PX) < 0)
116112463Sralph 		PX = 0;
116212463Sralph 	sprintf(&pxwidth[2], "%d", PX);
116356123Selan 	if (cgetnum(bp, "py", &PY) < 0)
116412463Sralph 		PY = 0;
116512463Sralph 	sprintf(&pxlength[2], "%d", PY);
116656123Selan 	cgetstr(bp, "rm", &RM);
116738736Stef 	if (s = checkremote())
116838736Stef 		syslog(LOG_WARNING, s);
116925468Stef 
117056123Selan 	cgetstr(bp, "af", &AF);
117156123Selan 	cgetstr(bp, "of", &OF);
117256123Selan 	cgetstr(bp, "if", &IF);
117356123Selan 	cgetstr(bp, "rf", &RF);
117456123Selan 	cgetstr(bp, "tf", &TF);
117556123Selan 	cgetstr(bp, "nf", &NF);
117656123Selan 	cgetstr(bp, "df", &DF);
117756123Selan 	cgetstr(bp, "gf", &GF);
117856123Selan 	cgetstr(bp, "vf", &VF);
117956123Selan 	cgetstr(bp, "cf", &CF);
118056123Selan 	cgetstr(bp, "tr", &TR);
118156123Selan 
118256123Selan 	RS = (cgetcap(bp, "rs", ':') != NULL);
118356123Selan 	SF = (cgetcap(bp, "sf", ':') != NULL);
118456123Selan 	SH = (cgetcap(bp, "sh", ':') != NULL);
118556123Selan 	SB = (cgetcap(bp, "sb", ':') != NULL);
118656123Selan 	HL = (cgetcap(bp, "hl", ':') != NULL);
118756123Selan 	RW = (cgetcap(bp, "rw", ':') != NULL);
118856123Selan 
118956123Selan 	cgetnum(bp, "br", &BR);
119056123Selan 	if (cgetnum(bp, "fc", &FC) < 0)
119112111Sralph 		FC = 0;
119256123Selan 	if (cgetnum(bp, "fs", &FS) < 0)
119312111Sralph 		FS = 0;
119456123Selan 	if (cgetnum(bp, "xc", &XC) < 0)
119512111Sralph 		XC = 0;
119656123Selan 	if (cgetnum(bp, "xs", &XS) < 0)
119712111Sralph 		XS = 0;
119856123Selan 
119956123Selan 	tof = (cgetcap(bp, "fo", ':') == NULL);
120012111Sralph }
120112111Sralph 
120212463Sralph /*
120312463Sralph  * Acquire line printer or remote connection.
120412463Sralph  */
120555474Sbostic static void
openpr()120612463Sralph openpr()
120712463Sralph {
120869008Stef 	register int i;
120969008Stef 	char *cp;
121012463Sralph 
121169008Stef 	if (!remote && *LP) {
121269008Stef 		if (cp = index(LP, '@'))
121369008Stef 			opennet(cp);
121469008Stef 		else
121569008Stef 			opentty();
121669008Stef 	} else if (remote) {
121769008Stef 		openrem();
121812463Sralph 	} else {
121916762Sralph 		syslog(LOG_ERR, "%s: no line printer device or host name",
122016762Sralph 			printer);
122112463Sralph 		exit(1);
122212463Sralph 	}
122369008Stef 
122412463Sralph 	/*
122512463Sralph 	 * Start up an output filter, if needed.
122612463Sralph 	 */
122740049Stef 	if (!remote && OF) {
122812463Sralph 		int p[2];
122912463Sralph 
123012463Sralph 		pipe(p);
123112463Sralph 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
123212463Sralph 			dup2(p[0], 0);		/* pipe is std in */
123312463Sralph 			dup2(pfd, 1);		/* printer is std out */
123468972Stef 			closelog();
123512463Sralph 			for (i = 3; i < NOFILE; i++)
123612463Sralph 				(void) close(i);
123712463Sralph 			if ((cp = rindex(OF, '/')) == NULL)
123812463Sralph 				cp = OF;
123912463Sralph 			else
124012463Sralph 				cp++;
124112463Sralph 			execl(OF, cp, width, length, 0);
124216762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
124312463Sralph 			exit(1);
124412463Sralph 		}
124512463Sralph 		(void) close(p[0]);		/* close input side */
124612463Sralph 		ofd = p[1];			/* use pipe for output */
124712463Sralph 	} else {
124812463Sralph 		ofd = pfd;
124912463Sralph 		ofilter = 0;
125012463Sralph 	}
125112463Sralph }
125212463Sralph 
125369008Stef /*
125469008Stef  * Printer connected directly to the network
125569008Stef  * or to a terminal server on the net
125669008Stef  */
125769008Stef static void
opennet(cp)125869008Stef opennet(cp)
125969008Stef 	char *cp;
126069008Stef {
126169008Stef 	register int i;
126269008Stef 	int resp, port;
1263*69331Sbostic 	char save_ch;
126469008Stef 
1265*69331Sbostic 	save_ch = *cp;
1266*69331Sbostic 	*cp = '\0';
1267*69331Sbostic 	port = atoi(LP);
126869008Stef 	if (port <= 0) {
1269*69331Sbostic 		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
127069008Stef 		exit(1);
127169008Stef 	}
1272*69331Sbostic 	*cp++ = save_ch;
127369008Stef 
127469008Stef 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
127569008Stef 		resp = -1;
1276*69331Sbostic 		pfd = getport(cp, port);
127769008Stef 		if (pfd < 0 && errno == ECONNREFUSED)
127869008Stef 			resp = 1;
127969008Stef 		else if (pfd >= 0) {
128069008Stef 			/*
128169008Stef 			 * need to delay a bit for rs232 lines
128269008Stef 			 * to stabilize in case printer is
128369008Stef 			 * connected via a terminal server
128469008Stef 			 */
128569008Stef 			delay(500);
128669008Stef 			break;
128769008Stef 		}
128869008Stef 		if (i == 1) {
128969008Stef 		   if (resp < 0)
129069008Stef 			pstatus("waiting for %s to come up", LP);
129169008Stef 		   else
129269008Stef 			pstatus("waiting for access to printer on %s", LP);
129369008Stef 		}
129469008Stef 		sleep(i);
129569008Stef 	}
1296*69331Sbostic 	pstatus("sending to %s port %d", cp, port);
129769008Stef }
129869008Stef 
129969008Stef /*
130069008Stef  * Printer is connected to an RS232 port on this host
130169008Stef  */
130269008Stef static void
opentty()130369008Stef opentty()
130469008Stef {
130569008Stef 	register int i;
130669008Stef 	int resp, port;
130769008Stef 
130869008Stef 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
130969008Stef 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
131069008Stef 		if (pfd >= 0) {
131169008Stef 			delay(500);
131269008Stef 			break;
131369008Stef 		}
131469008Stef 		if (errno == ENOENT) {
131569008Stef 			syslog(LOG_ERR, "%s: %m", LP);
131669008Stef 			exit(1);
131769008Stef 		}
131869008Stef 		if (i == 1)
131969008Stef 			pstatus("waiting for %s to become ready (offline ?)",
132069008Stef 				printer);
132169008Stef 		sleep(i);
132269008Stef 	}
132369008Stef 	if (isatty(pfd))
132469008Stef 		setty();
132569008Stef 	pstatus("%s is ready and printing", printer);
132669008Stef }
132769008Stef 
132869008Stef /*
132969008Stef  * Printer is on a remote host
133069008Stef  */
133169008Stef static void
openrem()133269008Stef openrem()
133369008Stef {
133469008Stef 	register int i, n;
133569008Stef 	int resp, port;
133669008Stef 
133769008Stef 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
133869008Stef 		resp = -1;
133969008Stef 		pfd = getport(RM, 0);
134069008Stef 		if (pfd >= 0) {
134169008Stef 			(void) sprintf(line, "\2%s\n", RP);
134269008Stef 			n = strlen(line);
134369008Stef 			if (write(pfd, line, n) == n &&
134469008Stef 			    (resp = response()) == '\0')
134569008Stef 				break;
134669008Stef 			(void) close(pfd);
134769008Stef 		}
134869008Stef 		if (i == 1) {
134969008Stef 			if (resp < 0)
135069008Stef 				pstatus("waiting for %s to come up", RM);
135169008Stef 			else {
135269008Stef 				pstatus("waiting for queue to be enabled on %s",
135369008Stef 					RM);
135469008Stef 				i = 256;
135569008Stef 			}
135669008Stef 		}
135769008Stef 		sleep(i);
135869008Stef 	}
135969008Stef 	pstatus("sending to %s", RM);
136069008Stef }
136169008Stef 
136212111Sralph struct bauds {
136312111Sralph 	int	baud;
136412111Sralph 	int	speed;
136512111Sralph } bauds[] = {
136612111Sralph 	50,	B50,
136712111Sralph 	75,	B75,
136812111Sralph 	110,	B110,
136912111Sralph 	134,	B134,
137012111Sralph 	150,	B150,
137112111Sralph 	200,	B200,
137212111Sralph 	300,	B300,
137312111Sralph 	600,	B600,
137412111Sralph 	1200,	B1200,
137512111Sralph 	1800,	B1800,
137612111Sralph 	2400,	B2400,
137712111Sralph 	4800,	B4800,
137812111Sralph 	9600,	B9600,
137912111Sralph 	19200,	EXTA,
138012111Sralph 	38400,	EXTB,
138112111Sralph 	0,	0
138212111Sralph };
138312111Sralph 
138412111Sralph /*
138512111Sralph  * setup tty lines.
138612111Sralph  */
138755474Sbostic static void
setty()138812111Sralph setty()
138912111Sralph {
139012111Sralph 	struct sgttyb ttybuf;
139112111Sralph 	register struct bauds *bp;
139212111Sralph 
139312111Sralph 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
139416762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
139512111Sralph 		exit(1);
139612111Sralph 	}
139712111Sralph 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
139816762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
139912111Sralph 		exit(1);
140012111Sralph 	}
140112111Sralph 	if (BR > 0) {
140212111Sralph 		for (bp = bauds; bp->baud; bp++)
140312111Sralph 			if (BR == bp->baud)
140412111Sralph 				break;
140512111Sralph 		if (!bp->baud) {
140616762Sralph 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
140712111Sralph 			exit(1);
140812111Sralph 		}
140912111Sralph 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
141012111Sralph 	}
141113169Sralph 	ttybuf.sg_flags &= ~FC;
141213169Sralph 	ttybuf.sg_flags |= FS;
141312111Sralph 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
141416762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
141512111Sralph 		exit(1);
141612111Sralph 	}
141712111Sralph 	if (XC) {
141812111Sralph 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
141916762Sralph 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
142012111Sralph 			exit(1);
142112111Sralph 		}
142212111Sralph 	}
142312111Sralph 	if (XS) {
142412111Sralph 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
142516762Sralph 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
142612111Sralph 			exit(1);
142712111Sralph 		}
142812111Sralph 	}
142912111Sralph }
143012463Sralph 
143155474Sbostic #if __STDC__
143255474Sbostic #include <stdarg.h>
143355474Sbostic #else
143455474Sbostic #include <varargs.h>
143555474Sbostic #endif
143655474Sbostic 
143769061Stef static void
143855474Sbostic #if __STDC__
pstatus(const char * msg,...)143955474Sbostic pstatus(const char *msg, ...)
144055474Sbostic #else
144155474Sbostic pstatus(msg, va_alist)
144212463Sralph 	char *msg;
144355474Sbostic         va_dcl
144455474Sbostic #endif
144512463Sralph {
144612463Sralph 	register int fd;
144712463Sralph 	char buf[BUFSIZ];
144855474Sbostic 	va_list ap;
144955474Sbostic #if __STDC__
145055474Sbostic 	va_start(ap, msg);
145155474Sbostic #else
145255474Sbostic 	va_start(ap);
145355474Sbostic #endif
145412463Sralph 
145512463Sralph 	umask(0);
145613148Ssam 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
145716762Sralph 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
145816762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
145916762Sralph 		exit(1);
146016762Sralph 	}
146113148Ssam 	ftruncate(fd, 0);
146255474Sbostic 	(void)vsnprintf(buf, sizeof(buf), msg, ap);
146355474Sbostic 	va_end(ap);
146412463Sralph 	strcat(buf, "\n");
146512463Sralph 	(void) write(fd, buf, strlen(buf));
146612463Sralph 	(void) close(fd);
146712463Sralph }
1468