xref: /csrg-svn/usr.sbin/lpr/lpd/printjob.c (revision 56251)
122437Sdist /*
2*56251Selan  * Copyright (c) 1983 The Regents of the University of California.
334203Sbostic  * All rights reserved.
434203Sbostic  *
556123Selan  *
6*56251Selan  * %sccs.include.redist.c%
722437Sdist  */
822437Sdist 
913954Ssam #ifndef lint
10*56251Selan char copyright[] =
11*56251Selan "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
12*56251Selan  All rights reserved.\n";
1334203Sbostic #endif /* not lint */
1413954Ssam 
15*56251Selan #ifndef lint
16*56251Selan static char sccsid[] = "@(#)printjob.c	5.17 (Berkeley) 09/15/92";
17*56251Selan #endif /* not lint */
18*56251Selan 
19*56251Selan 
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 int	 remote;	/* true if sending files to remote */
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));
9455474Sbostic static int        print __P((int, char *));
9555474Sbostic static int        printit __P((char *));
9655474Sbostic static void       pstatus __P((const char *, ...));
9755474Sbostic static char       response __P((void));
9855474Sbostic static void       scan_out __P((int, char *, int));
9955474Sbostic static char      *scnline __P((int, char *, int));
10055474Sbostic static int        sendfile __P((int, char *));
10155474Sbostic static int        sendit __P((char *));
10255474Sbostic static void       sendmail __P((char *, int));
10355474Sbostic static void       setty __P((void));
10455474Sbostic 
10555474Sbostic void
10612111Sralph printjob()
10712111Sralph {
10812111Sralph 	struct stat stb;
10912111Sralph 	register struct queue *q, **qp;
11012111Sralph 	struct queue **queue;
11112111Sralph 	register int i, nitems;
11212111Sralph 	long pidoff;
11316762Sralph 	int count = 0;
11412111Sralph 
11512111Sralph 	init();					/* set up capabilities */
11613442Sralph 	(void) write(1, "", 1);			/* ack that daemon is started */
11725496Seric 	(void) close(2);			/* set up log file */
11825496Seric 	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
11925496Seric 		syslog(LOG_ERR, "%s: %m", LF);
12037968Sbostic 		(void) open(_PATH_DEVNULL, O_WRONLY);
12125496Seric 	}
12216762Sralph 	setgid(getegid());
12312463Sralph 	pid = getpid();				/* for use with lprm */
12412111Sralph 	setpgrp(0, pid);
12516762Sralph 	signal(SIGHUP, abortpr);
12616762Sralph 	signal(SIGINT, abortpr);
12716762Sralph 	signal(SIGQUIT, abortpr);
12816762Sralph 	signal(SIGTERM, abortpr);
12912111Sralph 
13039954Smckusick 	(void) mktemp(tempfile);
13115811Sralph 
13212111Sralph 	/*
13312111Sralph 	 * uses short form file names
13412111Sralph 	 */
13512111Sralph 	if (chdir(SD) < 0) {
13616762Sralph 		syslog(LOG_ERR, "%s: %m", SD);
13712111Sralph 		exit(1);
13812111Sralph 	}
13912463Sralph 	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
14012463Sralph 		exit(0);		/* printing disabled */
14114150Sralph 	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
14213169Sralph 	if (lfd < 0) {
14316762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
14413169Sralph 		exit(1);
14513169Sralph 	}
14613169Sralph 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
14712111Sralph 		if (errno == EWOULDBLOCK)	/* active deamon present */
14812111Sralph 			exit(0);
14916762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
15012111Sralph 		exit(1);
15112111Sralph 	}
15213148Ssam 	ftruncate(lfd, 0);
15312111Sralph 	/*
15412111Sralph 	 * write process id for others to know
15512111Sralph 	 */
15612111Sralph 	sprintf(line, "%u\n", pid);
15712111Sralph 	pidoff = i = strlen(line);
15812463Sralph 	if (write(lfd, line, i) != i) {
15916762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
16012111Sralph 		exit(1);
16112111Sralph 	}
16212111Sralph 	/*
16312111Sralph 	 * search the spool directory for work and sort by queue order.
16412111Sralph 	 */
16512111Sralph 	if ((nitems = getq(&queue)) < 0) {
16616762Sralph 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
16712111Sralph 		exit(1);
16812111Sralph 	}
16912463Sralph 	if (nitems == 0)		/* no work to do */
17012111Sralph 		exit(0);
17113169Sralph 	if (stb.st_mode & 01) {		/* reset queue flag */
17213169Sralph 		if (fchmod(lfd, stb.st_mode & 0776) < 0)
17316762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
17413169Sralph 	}
17512463Sralph 	openpr();			/* open printer or remote */
17612463Sralph again:
17712111Sralph 	/*
17812111Sralph 	 * we found something to do now do it --
17912111Sralph 	 *    write the name of the current control file into the lock file
18012111Sralph 	 *    so the spool queue program can tell what we're working on
18112111Sralph 	 */
18212111Sralph 	for (qp = queue; nitems--; free((char *) q)) {
18312111Sralph 		q = *qp++;
18412111Sralph 		if (stat(q->q_name, &stb) < 0)
18512111Sralph 			continue;
18612463Sralph 	restart:
18755474Sbostic 		(void) lseek(lfd, (off_t)pidoff, 0);
18812111Sralph 		(void) sprintf(line, "%s\n", q->q_name);
18912111Sralph 		i = strlen(line);
19012111Sralph 		if (write(lfd, line, i) != i)
19116762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
19212111Sralph 		if (!remote)
19312111Sralph 			i = printit(q->q_name);
19412111Sralph 		else
19512111Sralph 			i = sendit(q->q_name);
19612463Sralph 		/*
19713169Sralph 		 * Check to see if we are supposed to stop printing or
19813169Sralph 		 * if we are to rebuild the queue.
19912463Sralph 		 */
20013169Sralph 		if (fstat(lfd, &stb) == 0) {
20116762Sralph 			/* stop printing before starting next job? */
20213169Sralph 			if (stb.st_mode & 0100)
20313169Sralph 				goto done;
20416762Sralph 			/* rebuild queue (after lpc topq) */
20513169Sralph 			if (stb.st_mode & 01) {
20613169Sralph 				for (free((char *) q); nitems--; free((char *) q))
20713169Sralph 					q = *qp++;
20813169Sralph 				if (fchmod(lfd, stb.st_mode & 0776) < 0)
20916762Sralph 					syslog(LOG_WARNING, "%s: %s: %m",
21016762Sralph 						printer, LO);
21113169Sralph 				break;
21213169Sralph 			}
21313169Sralph 		}
21417463Sralph 		if (i == OK)		/* file ok and printed */
21514150Sralph 			count++;
21617463Sralph 		else if (i == REPRINT) { /* try reprinting the job */
21716762Sralph 			syslog(LOG_INFO, "restarting %s", printer);
21812111Sralph 			if (ofilter > 0) {
21912111Sralph 				kill(ofilter, SIGCONT);	/* to be sure */
22012111Sralph 				(void) close(ofd);
22112111Sralph 				while ((i = wait(0)) > 0 && i != ofilter)
22212111Sralph 					;
22312111Sralph 				ofilter = 0;
22412111Sralph 			}
22512463Sralph 			(void) close(pfd);	/* close printer */
22615811Sralph 			if (ftruncate(lfd, pidoff) < 0)
22716762Sralph 				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
22812463Sralph 			openpr();		/* try to reopen printer */
22912111Sralph 			goto restart;
23012111Sralph 		}
23112111Sralph 	}
23212111Sralph 	free((char *) queue);
23312463Sralph 	/*
23412463Sralph 	 * search the spool directory for more work.
23512463Sralph 	 */
23612463Sralph 	if ((nitems = getq(&queue)) < 0) {
23716762Sralph 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
23812463Sralph 		exit(1);
23912463Sralph 	}
24012463Sralph 	if (nitems == 0) {		/* no more work to do */
24112463Sralph 	done:
24214150Sralph 		if (count > 0) {	/* Files actually printed */
24314150Sralph 			if (!SF && !tof)
24414150Sralph 				(void) write(ofd, FF, strlen(FF));
24514150Sralph 			if (TR != NULL)		/* output trailer */
24614150Sralph 				(void) write(ofd, TR, strlen(TR));
24714150Sralph 		}
24839954Smckusick 		(void) unlink(tempfile);
24912463Sralph 		exit(0);
25012463Sralph 	}
25112111Sralph 	goto again;
25212111Sralph }
25312111Sralph 
25412111Sralph char	fonts[4][50];	/* fonts for troff */
25512111Sralph 
25637968Sbostic char ifonts[4][40] = {
25737968Sbostic 	_PATH_VFONTR,
25837968Sbostic 	_PATH_VFONTI,
25937968Sbostic 	_PATH_VFONTB,
26037968Sbostic 	_PATH_VFONTS,
26112111Sralph };
26212111Sralph 
26312111Sralph /*
26412111Sralph  * The remaining part is the reading of the control file (cf)
26512111Sralph  * and performing the various actions.
26612111Sralph  */
26755474Sbostic static int
26812111Sralph printit(file)
26912111Sralph 	char *file;
27012111Sralph {
27112111Sralph 	register int i;
27217463Sralph 	char *cp;
27317463Sralph 	int bombed = OK;
27412111Sralph 
27512111Sralph 	/*
27617463Sralph 	 * open control file; ignore if no longer there.
27712111Sralph 	 */
27812111Sralph 	if ((cfp = fopen(file, "r")) == NULL) {
27916762Sralph 		syslog(LOG_INFO, "%s: %s: %m", printer, file);
28017463Sralph 		return(OK);
28112111Sralph 	}
28212111Sralph 	/*
28312111Sralph 	 * Reset troff fonts.
28412111Sralph 	 */
28512111Sralph 	for (i = 0; i < 4; i++)
28612111Sralph 		strcpy(fonts[i], ifonts[i]);
28717339Sralph 	strcpy(width+2, "0");
28817302Sralph 	strcpy(indent+2, "0");
28912111Sralph 
29012111Sralph 	/*
29112111Sralph 	 *      read the control file for work to do
29212111Sralph 	 *
29312111Sralph 	 *      file format -- first character in the line is a command
29412111Sralph 	 *      rest of the line is the argument.
29512111Sralph 	 *      valid commands are:
29612111Sralph 	 *
29717463Sralph 	 *		S -- "stat info" for symbolic link protection
29812111Sralph 	 *		J -- "job name" on banner page
29912111Sralph 	 *		C -- "class name" on banner page
30012111Sralph 	 *              L -- "literal" user's name to print on banner
30112111Sralph 	 *		T -- "title" for pr
30212111Sralph 	 *		H -- "host name" of machine where lpr was done
30312111Sralph 	 *              P -- "person" user's login name
30412581Sralph 	 *              I -- "indent" amount to indent output
30512111Sralph 	 *              f -- "file name" name of text file to print
30612111Sralph 	 *		l -- "file name" text file with control chars
30712111Sralph 	 *		p -- "file name" text file to print with pr(1)
30812111Sralph 	 *		t -- "file name" troff(1) file to print
30913233Sralph 	 *		n -- "file name" ditroff(1) file to print
31012111Sralph 	 *		d -- "file name" dvi file to print
31112111Sralph 	 *		g -- "file name" plot(1G) file to print
31212111Sralph 	 *		v -- "file name" plain raster file to print
31312111Sralph 	 *		c -- "file name" cifplot file to print
31412111Sralph 	 *		1 -- "R font file" for troff
31512111Sralph 	 *		2 -- "I font file" for troff
31612111Sralph 	 *		3 -- "B font file" for troff
31712111Sralph 	 *		4 -- "S font file" for troff
31812111Sralph 	 *		N -- "name" of file (used by lpq)
31912111Sralph 	 *              U -- "unlink" name of file to remove
32012111Sralph 	 *                    (after we print it. (Pass 2 only)).
32112111Sralph 	 *		M -- "mail" to user when done printing
32212111Sralph 	 *
32312111Sralph 	 *      getline reads a line and expands tabs to blanks
32412111Sralph 	 */
32512111Sralph 
32612111Sralph 	/* pass 1 */
32712111Sralph 
32812111Sralph 	while (getline(cfp))
32912111Sralph 		switch (line[0]) {
33012111Sralph 		case 'H':
33114150Sralph 			strcpy(fromhost, line+1);
33212111Sralph 			if (class[0] == '\0')
33315552Sralph 				strncpy(class, line+1, sizeof(class)-1);
33412111Sralph 			continue;
33512111Sralph 
33612111Sralph 		case 'P':
33715552Sralph 			strncpy(logname, line+1, sizeof(logname)-1);
33812463Sralph 			if (RS) {			/* restricted */
33955474Sbostic 				if (getpwnam(logname) == NULL) {
34017463Sralph 					bombed = NOACCT;
34115811Sralph 					sendmail(line+1, bombed);
34212463Sralph 					goto pass2;
34312463Sralph 				}
34412463Sralph 			}
34512111Sralph 			continue;
34612111Sralph 
34717463Sralph 		case 'S':
34817463Sralph 			cp = line+1;
34917463Sralph 			i = 0;
35017463Sralph 			while (*cp >= '0' && *cp <= '9')
35117463Sralph 				i = i * 10 + (*cp++ - '0');
35217463Sralph 			fdev = i;
35317463Sralph 			cp++;
35417463Sralph 			i = 0;
35517463Sralph 			while (*cp >= '0' && *cp <= '9')
35617463Sralph 				i = i * 10 + (*cp++ - '0');
35717463Sralph 			fino = i;
35817463Sralph 			continue;
35917463Sralph 
36012111Sralph 		case 'J':
36112111Sralph 			if (line[1] != '\0')
36215552Sralph 				strncpy(jobname, line+1, sizeof(jobname)-1);
36312111Sralph 			else
36412111Sralph 				strcpy(jobname, " ");
36512111Sralph 			continue;
36612111Sralph 
36712111Sralph 		case 'C':
36812111Sralph 			if (line[1] != '\0')
36915552Sralph 				strncpy(class, line+1, sizeof(class)-1);
37012111Sralph 			else if (class[0] == '\0')
37115811Sralph 				gethostname(class, sizeof(class));
37212111Sralph 			continue;
37312111Sralph 
37412111Sralph 		case 'T':	/* header title for pr */
37515552Sralph 			strncpy(title, line+1, sizeof(title)-1);
37612111Sralph 			continue;
37712111Sralph 
37812111Sralph 		case 'L':	/* identification line */
37918127Sralph 			if (!SH && !HL)
38012111Sralph 				banner(line+1, jobname);
38112111Sralph 			continue;
38212111Sralph 
38312111Sralph 		case '1':	/* troff fonts */
38412111Sralph 		case '2':
38512111Sralph 		case '3':
38612111Sralph 		case '4':
38712111Sralph 			if (line[1] != '\0')
38812111Sralph 				strcpy(fonts[line[0]-'1'], line+1);
38912111Sralph 			continue;
39012111Sralph 
39112111Sralph 		case 'W':	/* page width */
39215552Sralph 			strncpy(width+2, line+1, sizeof(width)-3);
39312111Sralph 			continue;
39412111Sralph 
39512581Sralph 		case 'I':	/* indent amount */
39615552Sralph 			strncpy(indent+2, line+1, sizeof(indent)-3);
39712581Sralph 			continue;
39812581Sralph 
39912111Sralph 		default:	/* some file to print */
40015811Sralph 			switch (i = print(line[0], line+1)) {
40117463Sralph 			case ERROR:
40217463Sralph 				if (bombed == OK)
40317463Sralph 					bombed = FATALERR;
40415811Sralph 				break;
40517463Sralph 			case REPRINT:
40612111Sralph 				(void) fclose(cfp);
40717463Sralph 				return(REPRINT);
40817463Sralph 			case FILTERERR:
40917463Sralph 			case ACCESS:
41017463Sralph 				bombed = i;
41115811Sralph 				sendmail(logname, bombed);
41215811Sralph 			}
41312111Sralph 			title[0] = '\0';
41412111Sralph 			continue;
41512111Sralph 
41612111Sralph 		case 'N':
41712111Sralph 		case 'U':
41812111Sralph 		case 'M':
41912111Sralph 			continue;
42012111Sralph 		}
42112111Sralph 
42212111Sralph 	/* pass 2 */
42312111Sralph 
42412463Sralph pass2:
42512111Sralph 	fseek(cfp, 0L, 0);
42612111Sralph 	while (getline(cfp))
42712111Sralph 		switch (line[0]) {
42818127Sralph 		case 'L':	/* identification line */
42918127Sralph 			if (!SH && HL)
43018127Sralph 				banner(line+1, jobname);
43118127Sralph 			continue;
43218127Sralph 
43312111Sralph 		case 'M':
43417463Sralph 			if (bombed < NOACCT)	/* already sent if >= NOACCT */
43515811Sralph 				sendmail(line+1, bombed);
43612111Sralph 			continue;
43712111Sralph 
43812111Sralph 		case 'U':
43912111Sralph 			(void) unlink(line+1);
44012111Sralph 		}
44112111Sralph 	/*
44215811Sralph 	 * clean-up in case another control file exists
44312111Sralph 	 */
44412111Sralph 	(void) fclose(cfp);
44512111Sralph 	(void) unlink(file);
44617463Sralph 	return(bombed == OK ? OK : ERROR);
44712111Sralph }
44812111Sralph 
44912111Sralph /*
45012111Sralph  * Print a file.
45113233Sralph  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
45215811Sralph  * Return -1 if a non-recoverable error occured,
45315811Sralph  * 2 if the filter detected some errors (but printed the job anyway),
45415811Sralph  * 1 if we should try to reprint this job and
45512111Sralph  * 0 if all is well.
45612111Sralph  * Note: all filters take stdin as the file, stdout as the printer,
45712111Sralph  * stderr as the log file, and must not ignore SIGINT.
45812111Sralph  */
45955474Sbostic static int
46012111Sralph print(format, file)
46112111Sralph 	int format;
46212111Sralph 	char *file;
46312111Sralph {
46415811Sralph 	register int n;
46512111Sralph 	register char *prog;
46615811Sralph 	int fi, fo;
46739954Smckusick 	FILE *fp;
46812111Sralph 	char *av[15], buf[BUFSIZ];
46912111Sralph 	int pid, p[2], stopped = 0;
47012111Sralph 	union wait status;
47117463Sralph 	struct stat stb;
47212111Sralph 
47317463Sralph 	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
47417463Sralph 		return(ERROR);
47517463Sralph 	/*
47617463Sralph 	 * Check to see if data file is a symbolic link. If so, it should
47717463Sralph 	 * still point to the same file or someone is trying to print
47817463Sralph 	 * something he shouldn't.
47917463Sralph 	 */
48017463Sralph 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
48117463Sralph 	    (stb.st_dev != fdev || stb.st_ino != fino))
48217463Sralph 		return(ACCESS);
48312111Sralph 	if (!SF && !tof) {		/* start on a fresh page */
48412111Sralph 		(void) write(ofd, FF, strlen(FF));
48512111Sralph 		tof = 1;
48612111Sralph 	}
48712111Sralph 	if (IF == NULL && (format == 'f' || format == 'l')) {
48812111Sralph 		tof = 0;
48912111Sralph 		while ((n = read(fi, buf, BUFSIZ)) > 0)
49012111Sralph 			if (write(ofd, buf, n) != n) {
49112111Sralph 				(void) close(fi);
49217463Sralph 				return(REPRINT);
49312111Sralph 			}
49412111Sralph 		(void) close(fi);
49517463Sralph 		return(OK);
49612111Sralph 	}
49712111Sralph 	switch (format) {
49812111Sralph 	case 'p':	/* print file using 'pr' */
49912111Sralph 		if (IF == NULL) {	/* use output filter */
50037968Sbostic 			prog = _PATH_PR;
50112111Sralph 			av[0] = "pr";
50212111Sralph 			av[1] = width;
50312111Sralph 			av[2] = length;
50412111Sralph 			av[3] = "-h";
50512111Sralph 			av[4] = *title ? title : " ";
50612111Sralph 			av[5] = 0;
50712111Sralph 			fo = ofd;
50812111Sralph 			goto start;
50912111Sralph 		}
51012111Sralph 		pipe(p);
51112111Sralph 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
51212111Sralph 			dup2(fi, 0);		/* file is stdin */
51312111Sralph 			dup2(p[1], 1);		/* pipe is stdout */
51412111Sralph 			for (n = 3; n < NOFILE; n++)
51512111Sralph 				(void) close(n);
51637968Sbostic 			execl(_PATH_PR, "pr", width, length,
51737968Sbostic 			    "-h", *title ? title : " ", 0);
51837968Sbostic 			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
51912111Sralph 			exit(2);
52012111Sralph 		}
52112111Sralph 		(void) close(p[1]);		/* close output side */
52212111Sralph 		(void) close(fi);
52312111Sralph 		if (prchild < 0) {
52412111Sralph 			prchild = 0;
52512111Sralph 			(void) close(p[0]);
52617463Sralph 			return(ERROR);
52712111Sralph 		}
52812111Sralph 		fi = p[0];			/* use pipe for input */
52912111Sralph 	case 'f':	/* print plain text file */
53012111Sralph 		prog = IF;
53112111Sralph 		av[1] = width;
53212111Sralph 		av[2] = length;
53312581Sralph 		av[3] = indent;
53412581Sralph 		n = 4;
53512111Sralph 		break;
53612111Sralph 	case 'l':	/* like 'f' but pass control characters */
53712111Sralph 		prog = IF;
53814325Sralph 		av[1] = "-c";
53912111Sralph 		av[2] = width;
54012111Sralph 		av[3] = length;
54112581Sralph 		av[4] = indent;
54212581Sralph 		n = 5;
54312111Sralph 		break;
54412463Sralph 	case 'r':	/* print a fortran text file */
54512463Sralph 		prog = RF;
54612463Sralph 		av[1] = width;
54712463Sralph 		av[2] = length;
54812463Sralph 		n = 3;
54912463Sralph 		break;
55012111Sralph 	case 't':	/* print troff output */
55113233Sralph 	case 'n':	/* print ditroff output */
55212463Sralph 	case 'd':	/* print tex output */
55312111Sralph 		(void) unlink(".railmag");
55412463Sralph 		if ((fo = creat(".railmag", FILMOD)) < 0) {
55516762Sralph 			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
55612111Sralph 			(void) unlink(".railmag");
55712111Sralph 		} else {
55812111Sralph 			for (n = 0; n < 4; n++) {
55912111Sralph 				if (fonts[n][0] != '/')
56054520Sbostic 					(void) write(fo, _PATH_VFONT,
56154520Sbostic 					    sizeof(_PATH_VFONT) - 1);
56212111Sralph 				(void) write(fo, fonts[n], strlen(fonts[n]));
56312111Sralph 				(void) write(fo, "\n", 1);
56412111Sralph 			}
56512111Sralph 			(void) close(fo);
56612111Sralph 		}
56713233Sralph 		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
56812463Sralph 		av[1] = pxwidth;
56912463Sralph 		av[2] = pxlength;
57012463Sralph 		n = 3;
57112111Sralph 		break;
57212111Sralph 	case 'c':	/* print cifplot output */
57312111Sralph 		prog = CF;
57412463Sralph 		av[1] = pxwidth;
57512463Sralph 		av[2] = pxlength;
57612463Sralph 		n = 3;
57712111Sralph 		break;
57812111Sralph 	case 'g':	/* print plot(1G) output */
57912111Sralph 		prog = GF;
58012463Sralph 		av[1] = pxwidth;
58112463Sralph 		av[2] = pxlength;
58212463Sralph 		n = 3;
58312111Sralph 		break;
58412111Sralph 	case 'v':	/* print raster output */
58512111Sralph 		prog = VF;
58612463Sralph 		av[1] = pxwidth;
58712463Sralph 		av[2] = pxlength;
58812463Sralph 		n = 3;
58912111Sralph 		break;
59012111Sralph 	default:
59112111Sralph 		(void) close(fi);
59216762Sralph 		syslog(LOG_ERR, "%s: illegal format character '%c'",
59316762Sralph 			printer, format);
59417463Sralph 		return(ERROR);
59512111Sralph 	}
59612111Sralph 	if ((av[0] = rindex(prog, '/')) != NULL)
59712111Sralph 		av[0]++;
59812111Sralph 	else
59912111Sralph 		av[0] = prog;
60012111Sralph 	av[n++] = "-n";
60112111Sralph 	av[n++] = logname;
60212111Sralph 	av[n++] = "-h";
60314150Sralph 	av[n++] = fromhost;
60412111Sralph 	av[n++] = AF;
60512111Sralph 	av[n] = 0;
60612111Sralph 	fo = pfd;
60712111Sralph 	if (ofilter > 0) {		/* stop output filter */
60812111Sralph 		write(ofd, "\031\1", 2);
60946912Sbostic 		while ((pid =
61046912Sbostic 		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
61112111Sralph 			;
61212111Sralph 		if (status.w_stopval != WSTOPPED) {
61312111Sralph 			(void) close(fi);
61416762Sralph 			syslog(LOG_WARNING, "%s: output filter died (%d)",
61516762Sralph 				printer, status.w_retcode);
61617463Sralph 			return(REPRINT);
61712111Sralph 		}
61812111Sralph 		stopped++;
61912111Sralph 	}
62012111Sralph start:
62112111Sralph 	if ((child = dofork(DORETURN)) == 0) {	/* child */
62212111Sralph 		dup2(fi, 0);
62312111Sralph 		dup2(fo, 1);
62439954Smckusick 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
62515811Sralph 		if (n >= 0)
62615811Sralph 			dup2(n, 2);
62712111Sralph 		for (n = 3; n < NOFILE; n++)
62812111Sralph 			(void) close(n);
62912111Sralph 		execv(prog, av);
63016762Sralph 		syslog(LOG_ERR, "cannot execv %s", prog);
63112111Sralph 		exit(2);
63212111Sralph 	}
63312111Sralph 	(void) close(fi);
63412111Sralph 	if (child < 0)
63512111Sralph 		status.w_retcode = 100;
63612111Sralph 	else
63746912Sbostic 		while ((pid = wait((int *)&status)) > 0 && pid != child)
63812111Sralph 			;
63912111Sralph 	child = 0;
64012111Sralph 	prchild = 0;
64112111Sralph 	if (stopped) {		/* restart output filter */
64212111Sralph 		if (kill(ofilter, SIGCONT) < 0) {
64316762Sralph 			syslog(LOG_ERR, "cannot restart output filter");
64412111Sralph 			exit(1);
64512111Sralph 		}
64612111Sralph 	}
64712111Sralph 	tof = 0;
64839954Smckusick 
64939954Smckusick 	/* Copy filter output to "lf" logfile */
65039954Smckusick 	if (fp = fopen(tempfile, "r")) {
65139954Smckusick 		while (fgets(buf, sizeof(buf), fp))
65239954Smckusick 			fputs(buf, stderr);
65356123Selan 		fclose(fp);
65439954Smckusick 	}
65539954Smckusick 
65615811Sralph 	if (!WIFEXITED(status)) {
65716762Sralph 		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
65816762Sralph 			printer, format, status.w_termsig);
65917463Sralph 		return(ERROR);
66017463Sralph 	}
66117463Sralph 	switch (status.w_retcode) {
66217463Sralph 	case 0:
66317463Sralph 		tof = 1;
66417463Sralph 		return(OK);
66517463Sralph 	case 1:
66617463Sralph 		return(REPRINT);
66717463Sralph 	default:
66816762Sralph 		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
66916762Sralph 			printer, format, status.w_retcode);
67017463Sralph 	case 2:
67117463Sralph 		return(ERROR);
67217463Sralph 	}
67312111Sralph }
67412111Sralph 
67512111Sralph /*
67612111Sralph  * Send the daemon control file (cf) and any data files.
67712111Sralph  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
67812111Sralph  * 0 if all is well.
67912111Sralph  */
68055474Sbostic static int
68112111Sralph sendit(file)
68212111Sralph 	char *file;
68312111Sralph {
68417463Sralph 	register int i, err = OK;
68517463Sralph 	char *cp, last[BUFSIZ];
68612111Sralph 
68712111Sralph 	/*
68812111Sralph 	 * open control file
68912111Sralph 	 */
69016762Sralph 	if ((cfp = fopen(file, "r")) == NULL)
69117463Sralph 		return(OK);
69212111Sralph 	/*
69312111Sralph 	 *      read the control file for work to do
69412111Sralph 	 *
69512111Sralph 	 *      file format -- first character in the line is a command
69612111Sralph 	 *      rest of the line is the argument.
69712111Sralph 	 *      commands of interest are:
69812111Sralph 	 *
69912111Sralph 	 *            a-z -- "file name" name of file to print
70012111Sralph 	 *              U -- "unlink" name of file to remove
70112111Sralph 	 *                    (after we print it. (Pass 2 only)).
70212111Sralph 	 */
70312111Sralph 
70412111Sralph 	/*
70512111Sralph 	 * pass 1
70612111Sralph 	 */
70712111Sralph 	while (getline(cfp)) {
70812111Sralph 	again:
70917463Sralph 		if (line[0] == 'S') {
71017463Sralph 			cp = line+1;
71117463Sralph 			i = 0;
71217463Sralph 			while (*cp >= '0' && *cp <= '9')
71317463Sralph 				i = i * 10 + (*cp++ - '0');
71417463Sralph 			fdev = i;
71517463Sralph 			cp++;
71617463Sralph 			i = 0;
71717463Sralph 			while (*cp >= '0' && *cp <= '9')
71817463Sralph 				i = i * 10 + (*cp++ - '0');
71917463Sralph 			fino = i;
72017463Sralph 			continue;
72117463Sralph 		}
72212111Sralph 		if (line[0] >= 'a' && line[0] <= 'z') {
72312111Sralph 			strcpy(last, line);
72417463Sralph 			while (i = getline(cfp))
72512111Sralph 				if (strcmp(last, line))
72612111Sralph 					break;
72717463Sralph 			switch (sendfile('\3', last+1)) {
72817463Sralph 			case OK:
72917463Sralph 				if (i)
73017463Sralph 					goto again;
73117463Sralph 				break;
73217463Sralph 			case REPRINT:
73312111Sralph 				(void) fclose(cfp);
73417463Sralph 				return(REPRINT);
73517463Sralph 			case ACCESS:
73617463Sralph 				sendmail(logname, ACCESS);
73717463Sralph 			case ERROR:
73817463Sralph 				err = ERROR;
73917463Sralph 			}
74012111Sralph 			break;
74112111Sralph 		}
74212111Sralph 	}
74317463Sralph 	if (err == OK && sendfile('\2', file) > 0) {
74412111Sralph 		(void) fclose(cfp);
74517463Sralph 		return(REPRINT);
74612111Sralph 	}
74712111Sralph 	/*
74812111Sralph 	 * pass 2
74912111Sralph 	 */
75012111Sralph 	fseek(cfp, 0L, 0);
75112111Sralph 	while (getline(cfp))
75212111Sralph 		if (line[0] == 'U')
75312111Sralph 			(void) unlink(line+1);
75412111Sralph 	/*
75517463Sralph 	 * clean-up in case another control file exists
75612111Sralph 	 */
75712111Sralph 	(void) fclose(cfp);
75812111Sralph 	(void) unlink(file);
75917463Sralph 	return(err);
76012111Sralph }
76112111Sralph 
76212111Sralph /*
76312111Sralph  * Send a data file to the remote machine and spool it.
76412111Sralph  * Return positive if we should try resending.
76512111Sralph  */
76655474Sbostic static int
76712111Sralph sendfile(type, file)
76855474Sbostic 	int type;
76955474Sbostic 	char *file;
77012111Sralph {
77112111Sralph 	register int f, i, amt;
77212111Sralph 	struct stat stb;
77312111Sralph 	char buf[BUFSIZ];
77416762Sralph 	int sizerr, resp;
77512111Sralph 
77617463Sralph 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
77717463Sralph 		return(ERROR);
77817463Sralph 	/*
77917463Sralph 	 * Check to see if data file is a symbolic link. If so, it should
78017463Sralph 	 * still point to the same file or someone is trying to print something
78117463Sralph 	 * he shouldn't.
78217463Sralph 	 */
78317463Sralph 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
78417463Sralph 	    (stb.st_dev != fdev || stb.st_ino != fino))
78517463Sralph 		return(ACCESS);
78655474Sbostic 	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
78712111Sralph 	amt = strlen(buf);
78816762Sralph 	for (i = 0;  ; i++) {
78916762Sralph 		if (write(pfd, buf, amt) != amt ||
79016762Sralph 		    (resp = response()) < 0 || resp == '\1') {
79116762Sralph 			(void) close(f);
79217463Sralph 			return(REPRINT);
79316762Sralph 		} else if (resp == '\0')
79416762Sralph 			break;
79516762Sralph 		if (i == 0)
79655474Sbostic 			pstatus("no space on remote; waiting for queue to drain");
79716762Sralph 		if (i == 10)
79824861Seric 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
79916762Sralph 				printer, RM);
80016762Sralph 		sleep(5 * 60);
80112692Sralph 	}
80216762Sralph 	if (i)
80355474Sbostic 		pstatus("sending to %s", RM);
80412111Sralph 	sizerr = 0;
80512111Sralph 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
80612111Sralph 		amt = BUFSIZ;
80712111Sralph 		if (i + amt > stb.st_size)
80812111Sralph 			amt = stb.st_size - i;
80912111Sralph 		if (sizerr == 0 && read(f, buf, amt) != amt)
81012111Sralph 			sizerr = 1;
81112692Sralph 		if (write(pfd, buf, amt) != amt) {
81212692Sralph 			(void) close(f);
81317463Sralph 			return(REPRINT);
81412692Sralph 		}
81512111Sralph 	}
81655474Sbostic 
81755474Sbostic 
81855474Sbostic 
81955474Sbostic 
82012111Sralph 	(void) close(f);
82112111Sralph 	if (sizerr) {
82216762Sralph 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
82317463Sralph 		/* tell recvjob to ignore this file */
82417463Sralph 		(void) write(pfd, "\1", 1);
82517463Sralph 		return(ERROR);
82617463Sralph 	}
82717463Sralph 	if (write(pfd, "", 1) != 1 || response())
82817463Sralph 		return(REPRINT);
82917463Sralph 	return(OK);
83012111Sralph }
83112111Sralph 
83212111Sralph /*
83312111Sralph  * Check to make sure there have been no errors and that both programs
83412111Sralph  * are in sync with eachother.
83512111Sralph  * Return non-zero if the connection was lost.
83612111Sralph  */
83755474Sbostic static char
83816762Sralph response()
83912111Sralph {
84012111Sralph 	char resp;
84112111Sralph 
84216762Sralph 	if (read(pfd, &resp, 1) != 1) {
84316762Sralph 		syslog(LOG_INFO, "%s: lost connection", printer);
84416762Sralph 		return(-1);
84512111Sralph 	}
84616762Sralph 	return(resp);
84712111Sralph }
84812111Sralph 
84912111Sralph /*
85012111Sralph  * Banner printing stuff
85112111Sralph  */
85255474Sbostic static void
85312111Sralph banner(name1, name2)
85412111Sralph 	char *name1, *name2;
85512111Sralph {
85612111Sralph 	time_t tvec;
85712111Sralph 	extern char *ctime();
85812111Sralph 
85912111Sralph 	time(&tvec);
86012111Sralph 	if (!SF && !tof)
86112111Sralph 		(void) write(ofd, FF, strlen(FF));
86212111Sralph 	if (SB) {	/* short banner only */
86312111Sralph 		if (class[0]) {
86412111Sralph 			(void) write(ofd, class, strlen(class));
86512111Sralph 			(void) write(ofd, ":", 1);
86612111Sralph 		}
86712111Sralph 		(void) write(ofd, name1, strlen(name1));
86812111Sralph 		(void) write(ofd, "  Job: ", 7);
86912111Sralph 		(void) write(ofd, name2, strlen(name2));
87012111Sralph 		(void) write(ofd, "  Date: ", 8);
87112111Sralph 		(void) write(ofd, ctime(&tvec), 24);
87212111Sralph 		(void) write(ofd, "\n", 1);
87312111Sralph 	} else {	/* normal banner */
87412111Sralph 		(void) write(ofd, "\n\n\n", 3);
87512111Sralph 		scan_out(ofd, name1, '\0');
87612111Sralph 		(void) write(ofd, "\n\n", 2);
87712111Sralph 		scan_out(ofd, name2, '\0');
87812111Sralph 		if (class[0]) {
87912111Sralph 			(void) write(ofd,"\n\n\n",3);
88012111Sralph 			scan_out(ofd, class, '\0');
88112111Sralph 		}
88212111Sralph 		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
88312111Sralph 		(void) write(ofd, name2, strlen(name2));
88412111Sralph 		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
88512111Sralph 		(void) write(ofd, ctime(&tvec), 24);
88612111Sralph 		(void) write(ofd, "\n", 1);
88712111Sralph 	}
88812111Sralph 	if (!SF)
88912111Sralph 		(void) write(ofd, FF, strlen(FF));
89012111Sralph 	tof = 1;
89112111Sralph }
89212111Sralph 
89355474Sbostic static char *
89412111Sralph scnline(key, p, c)
89555474Sbostic 	register int key;
89655474Sbostic 	register char *p;
89755474Sbostic 	int c;
89812111Sralph {
89912111Sralph 	register scnwidth;
90012111Sralph 
90112111Sralph 	for (scnwidth = WIDTH; --scnwidth;) {
90212111Sralph 		key <<= 1;
90312111Sralph 		*p++ = key & 0200 ? c : BACKGND;
90412111Sralph 	}
90512111Sralph 	return (p);
90612111Sralph }
90712111Sralph 
90812111Sralph #define TRC(q)	(((q)-' ')&0177)
90912111Sralph 
91055474Sbostic static void
91112111Sralph scan_out(scfd, scsp, dlm)
91255474Sbostic 	int scfd, dlm;
91355474Sbostic 	char *scsp;
91412111Sralph {
91512111Sralph 	register char *strp;
91612111Sralph 	register nchrs, j;
91712111Sralph 	char outbuf[LINELEN+1], *sp, c, cc;
91812111Sralph 	int d, scnhgt;
91912111Sralph 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
92012111Sralph 
92112111Sralph 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
92212111Sralph 		strp = &outbuf[0];
92312111Sralph 		sp = scsp;
92412111Sralph 		for (nchrs = 0; ; ) {
92512111Sralph 			d = dropit(c = TRC(cc = *sp++));
92612111Sralph 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
92712111Sralph 				for (j = WIDTH; --j;)
92812111Sralph 					*strp++ = BACKGND;
92912111Sralph 			else
93012111Sralph 				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
93112111Sralph 			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
93212111Sralph 				break;
93312111Sralph 			*strp++ = BACKGND;
93412111Sralph 			*strp++ = BACKGND;
93512111Sralph 		}
93612111Sralph 		while (*--strp == BACKGND && strp >= outbuf)
93712111Sralph 			;
93812111Sralph 		strp++;
93912111Sralph 		*strp++ = '\n';
94012111Sralph 		(void) write(scfd, outbuf, strp-outbuf);
94112111Sralph 	}
94212111Sralph }
94312111Sralph 
94455474Sbostic static int
94512111Sralph dropit(c)
94655474Sbostic 	int c;
94712111Sralph {
94812111Sralph 	switch(c) {
94912111Sralph 
95012111Sralph 	case TRC('_'):
95112111Sralph 	case TRC(';'):
95212111Sralph 	case TRC(','):
95312111Sralph 	case TRC('g'):
95412111Sralph 	case TRC('j'):
95512111Sralph 	case TRC('p'):
95612111Sralph 	case TRC('q'):
95712111Sralph 	case TRC('y'):
95812111Sralph 		return (DROP);
95912111Sralph 
96012111Sralph 	default:
96112111Sralph 		return (0);
96212111Sralph 	}
96312111Sralph }
96412111Sralph 
96512111Sralph /*
96612111Sralph  * sendmail ---
96712111Sralph  *   tell people about job completion
96812111Sralph  */
96955474Sbostic static void
97015811Sralph sendmail(user, bombed)
97115811Sralph 	char *user;
97212111Sralph 	int bombed;
97312111Sralph {
97412111Sralph 	register int i;
97515811Sralph 	int p[2], s;
97612111Sralph 	register char *cp;
97712111Sralph 	char buf[100];
97815811Sralph 	struct stat stb;
97915811Sralph 	FILE *fp;
98012111Sralph 
98112111Sralph 	pipe(p);
98215811Sralph 	if ((s = dofork(DORETURN)) == 0) {		/* child */
98312111Sralph 		dup2(p[0], 0);
98412111Sralph 		for (i = 3; i < NOFILE; i++)
98512111Sralph 			(void) close(i);
98637968Sbostic 		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
98712111Sralph 			cp++;
98855474Sbostic 	else
98937968Sbostic 			cp = _PATH_SENDMAIL;
99015811Sralph 		sprintf(buf, "%s@%s", user, fromhost);
99137968Sbostic 		execl(_PATH_SENDMAIL, cp, buf, 0);
99212111Sralph 		exit(0);
99315811Sralph 	} else if (s > 0) {				/* parent */
99412111Sralph 		dup2(p[1], 1);
99515811Sralph 		printf("To: %s@%s\n", user, fromhost);
99612111Sralph 		printf("Subject: printer job\n\n");
99712111Sralph 		printf("Your printer job ");
99812111Sralph 		if (*jobname)
99912111Sralph 			printf("(%s) ", jobname);
100012463Sralph 		switch (bombed) {
100117463Sralph 		case OK:
100212463Sralph 			printf("\ncompleted successfully\n");
100312463Sralph 			break;
100412463Sralph 		default:
100517463Sralph 		case FATALERR:
100612463Sralph 			printf("\ncould not be printed\n");
100712463Sralph 			break;
100817463Sralph 		case NOACCT:
100912463Sralph 			printf("\ncould not be printed without an account on %s\n", host);
101012463Sralph 			break;
101117463Sralph 		case FILTERERR:
101239954Smckusick 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
101339954Smckusick 			    (fp = fopen(tempfile, "r")) == NULL) {
101415811Sralph 				printf("\nwas printed but had some errors\n");
101515811Sralph 				break;
101615811Sralph 			}
101715811Sralph 			printf("\nwas printed but had the following errors:\n");
101815811Sralph 			while ((i = getc(fp)) != EOF)
101915811Sralph 				putchar(i);
102015811Sralph 			(void) fclose(fp);
102117463Sralph 			break;
102217463Sralph 		case ACCESS:
102317463Sralph 			printf("\nwas not printed because it was not linked to the original file\n");
102412463Sralph 		}
102512111Sralph 		fflush(stdout);
102612111Sralph 		(void) close(1);
102712111Sralph 	}
102812111Sralph 	(void) close(p[0]);
102912111Sralph 	(void) close(p[1]);
103015811Sralph 	wait(&s);
103112111Sralph }
103212111Sralph 
103312111Sralph /*
103412111Sralph  * dofork - fork with retries on failure
103512111Sralph  */
103655474Sbostic static int
103712111Sralph dofork(action)
103812111Sralph 	int action;
103912111Sralph {
104012111Sralph 	register int i, pid;
104112111Sralph 
104212111Sralph 	for (i = 0; i < 20; i++) {
104312463Sralph 		if ((pid = fork()) < 0) {
104412111Sralph 			sleep((unsigned)(i*i));
104512463Sralph 			continue;
104612463Sralph 		}
104712463Sralph 		/*
104812463Sralph 		 * Child should run as daemon instead of root
104912463Sralph 		 */
105012463Sralph 		if (pid == 0)
105112463Sralph 			setuid(DU);
105212463Sralph 		return(pid);
105312111Sralph 	}
105416762Sralph 	syslog(LOG_ERR, "can't fork");
105512111Sralph 
105612111Sralph 	switch (action) {
105712111Sralph 	case DORETURN:
105812111Sralph 		return (-1);
105912111Sralph 	default:
106016762Sralph 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
106112111Sralph 		/*FALL THRU*/
106212111Sralph 	case DOABORT:
106312111Sralph 		exit(1);
106412111Sralph 	}
106512111Sralph 	/*NOTREACHED*/
106612111Sralph }
106712111Sralph 
106812111Sralph /*
106916762Sralph  * Kill child processes to abort current job.
107012111Sralph  */
107155474Sbostic static void
107255474Sbostic abortpr(signo)
107355474Sbostic 	int signo;
107412111Sralph {
107539954Smckusick 	(void) unlink(tempfile);
107612111Sralph 	kill(0, SIGINT);
107712111Sralph 	if (ofilter > 0)
107812111Sralph 		kill(ofilter, SIGCONT);
107946912Sbostic 	while (wait(NULL) > 0)
108012111Sralph 		;
108112111Sralph 	exit(0);
108212111Sralph }
108312111Sralph 
108455474Sbostic static void
108512111Sralph init()
108612111Sralph {
108712111Sralph 	int status;
108838736Stef 	char *s;
108912111Sralph 
109056123Selan 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
109125468Stef 		syslog(LOG_ERR, "can't open printer description file");
109225468Stef 		exit(1);
109356123Selan 	} else if (status == -1) {
109425468Stef 		syslog(LOG_ERR, "unknown printer: %s", printer);
109525468Stef 		exit(1);
109656123Selan 	} else if (status == -3)
109756123Selan 		fatal("potential reference loop detected in printcap file");
109856123Selan 
109956123Selan 	if (cgetstr(bp, "lp", &LP) == -1)
110037968Sbostic 		LP = _PATH_DEFDEVLP;
110156123Selan 	if (cgetstr(bp, "rp", &RP) == -1)
110212463Sralph 		RP = DEFLP;
110356123Selan 	if (cgetstr(bp, "lo", &LO) == -1)
110412111Sralph 		LO = DEFLOCK;
110556123Selan 	if (cgetstr(bp, "st", &ST) == -1)
110612111Sralph 		ST = DEFSTAT;
110756123Selan 	if (cgetstr(bp, "lf", &LF) == -1)
110837968Sbostic 		LF = _PATH_CONSOLE;
110956123Selan 	if (cgetstr(bp, "sd", &SD) == -1)
111037968Sbostic 		SD = _PATH_DEFSPOOL;
111156123Selan 	if (cgetnum(bp, "du", &DU) < 0)
111212111Sralph 		DU = DEFUID;
111356123Selan 	if (cgetstr(bp,"ff", &FF) == -1)
111412111Sralph 		FF = DEFFF;
111556123Selan 	if (cgetnum(bp, "pw", &PW) < 0)
111612111Sralph 		PW = DEFWIDTH;
111712111Sralph 	sprintf(&width[2], "%d", PW);
111856123Selan 	if (cgetnum(bp, "pl", &PL) < 0)
111912111Sralph 		PL = DEFLENGTH;
112012111Sralph 	sprintf(&length[2], "%d", PL);
112156123Selan 	if (cgetnum(bp,"px", &PX) < 0)
112212463Sralph 		PX = 0;
112312463Sralph 	sprintf(&pxwidth[2], "%d", PX);
112456123Selan 	if (cgetnum(bp, "py", &PY) < 0)
112512463Sralph 		PY = 0;
112612463Sralph 	sprintf(&pxlength[2], "%d", PY);
112756123Selan 	cgetstr(bp, "rm", &RM);
112838736Stef 	if (s = checkremote())
112938736Stef 		syslog(LOG_WARNING, s);
113025468Stef 
113156123Selan 	cgetstr(bp, "af", &AF);
113256123Selan 	cgetstr(bp, "of", &OF);
113356123Selan 	cgetstr(bp, "if", &IF);
113456123Selan 	cgetstr(bp, "rf", &RF);
113556123Selan 	cgetstr(bp, "tf", &TF);
113656123Selan 	cgetstr(bp, "nf", &NF);
113756123Selan 	cgetstr(bp, "df", &DF);
113856123Selan 	cgetstr(bp, "gf", &GF);
113956123Selan 	cgetstr(bp, "vf", &VF);
114056123Selan 	cgetstr(bp, "cf", &CF);
114156123Selan 	cgetstr(bp, "tr", &TR);
114256123Selan 
114356123Selan 	RS = (cgetcap(bp, "rs", ':') != NULL);
114456123Selan 	SF = (cgetcap(bp, "sf", ':') != NULL);
114556123Selan 	SH = (cgetcap(bp, "sh", ':') != NULL);
114656123Selan 	SB = (cgetcap(bp, "sb", ':') != NULL);
114756123Selan 	HL = (cgetcap(bp, "hl", ':') != NULL);
114856123Selan 	RW = (cgetcap(bp, "rw", ':') != NULL);
114956123Selan 
115056123Selan 	cgetnum(bp, "br", &BR);
115156123Selan 	if (cgetnum(bp, "fc", &FC) < 0)
115212111Sralph 		FC = 0;
115356123Selan 	if (cgetnum(bp, "fs", &FS) < 0)
115412111Sralph 		FS = 0;
115556123Selan 	if (cgetnum(bp, "xc", &XC) < 0)
115612111Sralph 		XC = 0;
115756123Selan 	if (cgetnum(bp, "xs", &XS) < 0)
115812111Sralph 		XS = 0;
115956123Selan 
116056123Selan 	tof = (cgetcap(bp, "fo", ':') == NULL);
116112111Sralph }
116212111Sralph 
116312463Sralph /*
116412463Sralph  * Acquire line printer or remote connection.
116512463Sralph  */
116655474Sbostic static void
116712463Sralph openpr()
116812463Sralph {
116912463Sralph 	register int i, n;
117016762Sralph 	int resp;
117112463Sralph 
117238736Stef 	if (!sendtorem && *LP) {
117312463Sralph 		for (i = 1; ; i = i < 32 ? i << 1 : i) {
117413148Ssam 			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
117512463Sralph 			if (pfd >= 0)
117612463Sralph 				break;
117712463Sralph 			if (errno == ENOENT) {
117816762Sralph 				syslog(LOG_ERR, "%s: %m", LP);
117912463Sralph 				exit(1);
118012463Sralph 			}
118112463Sralph 			if (i == 1)
118255474Sbostic 				pstatus("waiting for %s to become ready (offline ?)", printer);
118312463Sralph 			sleep(i);
118412463Sralph 		}
118512463Sralph 		if (isatty(pfd))
118612463Sralph 			setty();
118755474Sbostic 		pstatus("%s is ready and printing", printer);
118812463Sralph 	} else if (RM != NULL) {
118916762Sralph 		for (i = 1; ; i = i < 256 ? i << 1 : i) {
119016762Sralph 			resp = -1;
119112528Sralph 			pfd = getport(RM);
119212463Sralph 			if (pfd >= 0) {
119312463Sralph 				(void) sprintf(line, "\2%s\n", RP);
119412463Sralph 				n = strlen(line);
119516762Sralph 				if (write(pfd, line, n) == n &&
119616762Sralph 				    (resp = response()) == '\0')
119712463Sralph 					break;
119816031Sralph 				(void) close(pfd);
119912463Sralph 			}
120016031Sralph 			if (i == 1) {
120116762Sralph 				if (resp < 0)
120255474Sbostic 					pstatus("waiting for %s to come up", RM);
120316762Sralph 				else {
120455474Sbostic 					pstatus("waiting for queue to be enabled on %s", RM);
120516762Sralph 					i = 256;
120616762Sralph 				}
120716031Sralph 			}
120812463Sralph 			sleep(i);
120912463Sralph 		}
121055474Sbostic 		pstatus("sending to %s", RM);
121112463Sralph 		remote = 1;
121212463Sralph 	} else {
121316762Sralph 		syslog(LOG_ERR, "%s: no line printer device or host name",
121416762Sralph 			printer);
121512463Sralph 		exit(1);
121612463Sralph 	}
121712463Sralph 	/*
121812463Sralph 	 * Start up an output filter, if needed.
121912463Sralph 	 */
122040049Stef 	if (!remote && OF) {
122112463Sralph 		int p[2];
122212463Sralph 		char *cp;
122312463Sralph 
122412463Sralph 		pipe(p);
122512463Sralph 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
122612463Sralph 			dup2(p[0], 0);		/* pipe is std in */
122712463Sralph 			dup2(pfd, 1);		/* printer is std out */
122812463Sralph 			for (i = 3; i < NOFILE; i++)
122912463Sralph 				(void) close(i);
123012463Sralph 			if ((cp = rindex(OF, '/')) == NULL)
123112463Sralph 				cp = OF;
123212463Sralph 			else
123312463Sralph 				cp++;
123412463Sralph 			execl(OF, cp, width, length, 0);
123516762Sralph 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
123612463Sralph 			exit(1);
123712463Sralph 		}
123812463Sralph 		(void) close(p[0]);		/* close input side */
123912463Sralph 		ofd = p[1];			/* use pipe for output */
124012463Sralph 	} else {
124112463Sralph 		ofd = pfd;
124212463Sralph 		ofilter = 0;
124312463Sralph 	}
124412463Sralph }
124512463Sralph 
124612111Sralph struct bauds {
124712111Sralph 	int	baud;
124812111Sralph 	int	speed;
124912111Sralph } bauds[] = {
125012111Sralph 	50,	B50,
125112111Sralph 	75,	B75,
125212111Sralph 	110,	B110,
125312111Sralph 	134,	B134,
125412111Sralph 	150,	B150,
125512111Sralph 	200,	B200,
125612111Sralph 	300,	B300,
125712111Sralph 	600,	B600,
125812111Sralph 	1200,	B1200,
125912111Sralph 	1800,	B1800,
126012111Sralph 	2400,	B2400,
126112111Sralph 	4800,	B4800,
126212111Sralph 	9600,	B9600,
126312111Sralph 	19200,	EXTA,
126412111Sralph 	38400,	EXTB,
126512111Sralph 	0,	0
126612111Sralph };
126712111Sralph 
126812111Sralph /*
126912111Sralph  * setup tty lines.
127012111Sralph  */
127155474Sbostic static void
127212111Sralph setty()
127312111Sralph {
127412111Sralph 	struct sgttyb ttybuf;
127512111Sralph 	register struct bauds *bp;
127612111Sralph 
127712111Sralph 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
127816762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
127912111Sralph 		exit(1);
128012111Sralph 	}
128112111Sralph 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
128216762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
128312111Sralph 		exit(1);
128412111Sralph 	}
128512111Sralph 	if (BR > 0) {
128612111Sralph 		for (bp = bauds; bp->baud; bp++)
128712111Sralph 			if (BR == bp->baud)
128812111Sralph 				break;
128912111Sralph 		if (!bp->baud) {
129016762Sralph 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
129112111Sralph 			exit(1);
129212111Sralph 		}
129312111Sralph 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
129412111Sralph 	}
129513169Sralph 	ttybuf.sg_flags &= ~FC;
129613169Sralph 	ttybuf.sg_flags |= FS;
129712111Sralph 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
129816762Sralph 		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
129912111Sralph 		exit(1);
130012111Sralph 	}
130112111Sralph 	if (XC) {
130212111Sralph 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
130316762Sralph 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
130412111Sralph 			exit(1);
130512111Sralph 		}
130612111Sralph 	}
130712111Sralph 	if (XS) {
130812111Sralph 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
130916762Sralph 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
131012111Sralph 			exit(1);
131112111Sralph 		}
131212111Sralph 	}
131312111Sralph }
131412463Sralph 
131555474Sbostic #if __STDC__
131655474Sbostic #include <stdarg.h>
131755474Sbostic #else
131855474Sbostic #include <varargs.h>
131955474Sbostic #endif
132055474Sbostic 
132155474Sbostic void
132255474Sbostic #if __STDC__
132355474Sbostic pstatus(const char *msg, ...)
132455474Sbostic #else
132555474Sbostic pstatus(msg, va_alist)
132612463Sralph 	char *msg;
132755474Sbostic         va_dcl
132855474Sbostic #endif
132912463Sralph {
133012463Sralph 	register int fd;
133112463Sralph 	char buf[BUFSIZ];
133255474Sbostic 	va_list ap;
133355474Sbostic #if __STDC__
133455474Sbostic 	va_start(ap, msg);
133555474Sbostic #else
133655474Sbostic 	va_start(ap);
133755474Sbostic #endif
133812463Sralph 
133912463Sralph 	umask(0);
134013148Ssam 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
134116762Sralph 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
134216762Sralph 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
134316762Sralph 		exit(1);
134416762Sralph 	}
134513148Ssam 	ftruncate(fd, 0);
134655474Sbostic 	(void)vsnprintf(buf, sizeof(buf), msg, ap);
134755474Sbostic 	va_end(ap);
134812463Sralph 	strcat(buf, "\n");
134912463Sralph 	(void) write(fd, buf, strlen(buf));
135012463Sralph 	(void) close(fd);
135112463Sralph }
1352