printjob.c revision 15032
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1983, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes *
61553Srgrimes * Redistribution and use in source and binary forms, with or without
71553Srgrimes * modification, are permitted provided that the following conditions
81553Srgrimes * are met:
91553Srgrimes * 1. Redistributions of source code must retain the above copyright
101553Srgrimes *    notice, this list of conditions and the following disclaimer.
111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121553Srgrimes *    notice, this list of conditions and the following disclaimer in the
131553Srgrimes *    documentation and/or other materials provided with the distribution.
141553Srgrimes * 3. All advertising materials mentioning features or use of this software
151553Srgrimes *    must display the following acknowledgement:
161553Srgrimes *	This product includes software developed by the University of
171553Srgrimes *	California, Berkeley and its contributors.
181553Srgrimes * 4. Neither the name of the University nor the names of its contributors
191553Srgrimes *    may be used to endorse or promote products derived from this software
201553Srgrimes *    without specific prior written permission.
211553Srgrimes *
221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321553Srgrimes * SUCH DAMAGE.
331553Srgrimes */
341553Srgrimes
351553Srgrimes#ifndef lint
361553Srgrimesstatic char copyright[] =
371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\
381553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
391553Srgrimes#endif /* not lint */
401553Srgrimes
411553Srgrimes#ifndef lint
421553Srgrimesstatic char sccsid[] = "@(#)printjob.c	8.2 (Berkeley) 4/16/94";
431553Srgrimes#endif /* not lint */
441553Srgrimes
451553Srgrimes
461553Srgrimes/*
471553Srgrimes * printjob -- print jobs in the queue.
481553Srgrimes *
491553Srgrimes *	NOTE: the lock file is used to pass information to lpq and lprm.
501553Srgrimes *	it does not need to be removed because file locks are dynamic.
511553Srgrimes */
521553Srgrimes
531553Srgrimes#include <sys/param.h>
541553Srgrimes#include <sys/wait.h>
551553Srgrimes#include <sys/stat.h>
561553Srgrimes#include <sys/types.h>
571553Srgrimes
581553Srgrimes#include <pwd.h>
591553Srgrimes#include <unistd.h>
601553Srgrimes#include <signal.h>
611553Srgrimes#include <syslog.h>
621553Srgrimes#include <fcntl.h>
631553Srgrimes#include <dirent.h>
641553Srgrimes#include <errno.h>
651553Srgrimes#include <stdio.h>
661553Srgrimes#include <string.h>
671553Srgrimes#include <stdlib.h>
6815032Ssef#include <sys/ioctl.h>
6915032Ssef#include <termios.h>
701553Srgrimes#include "lp.h"
711553Srgrimes#include "lp.local.h"
721553Srgrimes#include "pathnames.h"
731553Srgrimes#include "extern.h"
741553Srgrimes
751553Srgrimes#define DORETURN	0	/* absorb fork error */
761553Srgrimes#define DOABORT		1	/* abort if dofork fails */
771553Srgrimes
781553Srgrimes/*
791553Srgrimes * Error tokens
801553Srgrimes */
811553Srgrimes#define REPRINT		-2
821553Srgrimes#define ERROR		-1
831553Srgrimes#define	OK		0
841553Srgrimes#define	FATALERR	1
851553Srgrimes#define	NOACCT		2
861553Srgrimes#define	FILTERERR	3
871553Srgrimes#define	ACCESS		4
881553Srgrimes
891553Srgrimesstatic dev_t	 fdev;		/* device of file pointed to by symlink */
901553Srgrimesstatic ino_t	 fino;		/* inode of file pointed to by symlink */
911553Srgrimesstatic FILE	*cfp;		/* control file */
921553Srgrimesstatic int	 child;		/* id of any filters */
931553Srgrimesstatic int	 lfd;		/* lock file descriptor */
941553Srgrimesstatic int	 ofd;		/* output filter file descriptor */
951553Srgrimesstatic int	 ofilter;	/* id of output filter, if any */
961553Srgrimesstatic int	 pfd;		/* prstatic inter file descriptor */
971553Srgrimesstatic int	 pid;		/* pid of lpd process */
981553Srgrimesstatic int	 prchild;	/* id of pr process */
991553Srgrimesstatic int	 remote;	/* true if sending files to remote */
1001553Srgrimesstatic char	 title[80];	/* ``pr'' title */
1011553Srgrimesstatic int	 tof;		/* true if at top of form */
1021553Srgrimes
1031553Srgrimesstatic char	class[32];		/* classification field */
1041553Srgrimesstatic char	fromhost[32];		/* user's host machine */
1051553Srgrimes				/* indentation size in static characters */
1068857Srgrimesstatic char	indent[10] = "-i0";
1071553Srgrimesstatic char	jobname[100];		/* job or file name */
1081553Srgrimesstatic char	length[10] = "-l";	/* page length in lines */
1091553Srgrimesstatic char	logname[32];		/* user's login name */
1101553Srgrimesstatic char	pxlength[10] = "-y";	/* page length in pixels */
1111553Srgrimesstatic char	pxwidth[10] = "-x";	/* page width in pixels */
1121553Srgrimesstatic char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
1131553Srgrimesstatic char	width[10] = "-w";	/* page width in static characters */
1141553Srgrimes
1151553Srgrimesstatic void       abortpr __P((int));
1161553Srgrimesstatic void       banner __P((char *, char *));
1171553Srgrimesstatic int        dofork __P((int));
1181553Srgrimesstatic int        dropit __P((int));
1191553Srgrimesstatic void       init __P((void));
1201553Srgrimesstatic void       openpr __P((void));
1211553Srgrimesstatic int        print __P((int, char *));
1221553Srgrimesstatic int        printit __P((char *));
1231553Srgrimesstatic void       pstatus __P((const char *, ...));
1241553Srgrimesstatic char       response __P((void));
1251553Srgrimesstatic void       scan_out __P((int, char *, int));
1261553Srgrimesstatic char      *scnline __P((int, char *, int));
1271553Srgrimesstatic int        sendfile __P((int, char *));
1281553Srgrimesstatic int        sendit __P((char *));
1291553Srgrimesstatic void       sendmail __P((char *, int));
1301553Srgrimesstatic void       setty __P((void));
1311553Srgrimes
1321553Srgrimesvoid
1331553Srgrimesprintjob()
1341553Srgrimes{
1351553Srgrimes	struct stat stb;
1361553Srgrimes	register struct queue *q, **qp;
1371553Srgrimes	struct queue **queue;
1381553Srgrimes	register int i, nitems;
1391553Srgrimes	long pidoff;
1401553Srgrimes	int count = 0;
1411553Srgrimes
1421553Srgrimes	init();					/* set up capabilities */
1431553Srgrimes	(void) write(1, "", 1);			/* ack that daemon is started */
1441553Srgrimes	(void) close(2);			/* set up log file */
1451553Srgrimes	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
1461553Srgrimes		syslog(LOG_ERR, "%s: %m", LF);
1471553Srgrimes		(void) open(_PATH_DEVNULL, O_WRONLY);
1481553Srgrimes	}
1491553Srgrimes	setgid(getegid());
1501553Srgrimes	pid = getpid();				/* for use with lprm */
1511553Srgrimes	setpgrp(0, pid);
1521553Srgrimes	signal(SIGHUP, abortpr);
1531553Srgrimes	signal(SIGINT, abortpr);
1541553Srgrimes	signal(SIGQUIT, abortpr);
1551553Srgrimes	signal(SIGTERM, abortpr);
1561553Srgrimes
1571553Srgrimes	(void) mktemp(tempfile);
1581553Srgrimes
1591553Srgrimes	/*
1601553Srgrimes	 * uses short form file names
1611553Srgrimes	 */
1621553Srgrimes	if (chdir(SD) < 0) {
1631553Srgrimes		syslog(LOG_ERR, "%s: %m", SD);
1641553Srgrimes		exit(1);
1651553Srgrimes	}
1661553Srgrimes	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
1671553Srgrimes		exit(0);		/* printing disabled */
1681553Srgrimes	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
1691553Srgrimes	if (lfd < 0) {
1701553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1711553Srgrimes		exit(1);
1721553Srgrimes	}
1731553Srgrimes	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
1741553Srgrimes		if (errno == EWOULDBLOCK)	/* active deamon present */
1751553Srgrimes			exit(0);
1761553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1771553Srgrimes		exit(1);
1781553Srgrimes	}
1791553Srgrimes	ftruncate(lfd, 0);
1801553Srgrimes	/*
1811553Srgrimes	 * write process id for others to know
1821553Srgrimes	 */
1831553Srgrimes	sprintf(line, "%u\n", pid);
1841553Srgrimes	pidoff = i = strlen(line);
1851553Srgrimes	if (write(lfd, line, i) != i) {
1861553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1871553Srgrimes		exit(1);
1881553Srgrimes	}
1891553Srgrimes	/*
1901553Srgrimes	 * search the spool directory for work and sort by queue order.
1911553Srgrimes	 */
1921553Srgrimes	if ((nitems = getq(&queue)) < 0) {
1931553Srgrimes		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
1941553Srgrimes		exit(1);
1951553Srgrimes	}
1961553Srgrimes	if (nitems == 0)		/* no work to do */
1971553Srgrimes		exit(0);
1981553Srgrimes	if (stb.st_mode & 01) {		/* reset queue flag */
1991553Srgrimes		if (fchmod(lfd, stb.st_mode & 0776) < 0)
2001553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
2011553Srgrimes	}
2021553Srgrimes	openpr();			/* open printer or remote */
2031553Srgrimesagain:
2041553Srgrimes	/*
2051553Srgrimes	 * we found something to do now do it --
2061553Srgrimes	 *    write the name of the current control file into the lock file
2071553Srgrimes	 *    so the spool queue program can tell what we're working on
2081553Srgrimes	 */
2091553Srgrimes	for (qp = queue; nitems--; free((char *) q)) {
2101553Srgrimes		q = *qp++;
2111553Srgrimes		if (stat(q->q_name, &stb) < 0)
2121553Srgrimes			continue;
2131553Srgrimes	restart:
2141553Srgrimes		(void) lseek(lfd, (off_t)pidoff, 0);
2151553Srgrimes		(void) sprintf(line, "%s\n", q->q_name);
2161553Srgrimes		i = strlen(line);
2171553Srgrimes		if (write(lfd, line, i) != i)
2181553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
2191553Srgrimes		if (!remote)
2201553Srgrimes			i = printit(q->q_name);
2211553Srgrimes		else
2221553Srgrimes			i = sendit(q->q_name);
2231553Srgrimes		/*
2241553Srgrimes		 * Check to see if we are supposed to stop printing or
2251553Srgrimes		 * if we are to rebuild the queue.
2261553Srgrimes		 */
2271553Srgrimes		if (fstat(lfd, &stb) == 0) {
2281553Srgrimes			/* stop printing before starting next job? */
2291553Srgrimes			if (stb.st_mode & 0100)
2301553Srgrimes				goto done;
2311553Srgrimes			/* rebuild queue (after lpc topq) */
2321553Srgrimes			if (stb.st_mode & 01) {
2331553Srgrimes				for (free((char *) q); nitems--; free((char *) q))
2341553Srgrimes					q = *qp++;
2351553Srgrimes				if (fchmod(lfd, stb.st_mode & 0776) < 0)
2361553Srgrimes					syslog(LOG_WARNING, "%s: %s: %m",
2371553Srgrimes						printer, LO);
2381553Srgrimes				break;
2391553Srgrimes			}
2401553Srgrimes		}
2411553Srgrimes		if (i == OK)		/* file ok and printed */
2421553Srgrimes			count++;
2431553Srgrimes		else if (i == REPRINT) { /* try reprinting the job */
2441553Srgrimes			syslog(LOG_INFO, "restarting %s", printer);
2451553Srgrimes			if (ofilter > 0) {
2461553Srgrimes				kill(ofilter, SIGCONT);	/* to be sure */
2471553Srgrimes				(void) close(ofd);
2481553Srgrimes				while ((i = wait(0)) > 0 && i != ofilter)
2491553Srgrimes					;
2501553Srgrimes				ofilter = 0;
2511553Srgrimes			}
2521553Srgrimes			(void) close(pfd);	/* close printer */
2531553Srgrimes			if (ftruncate(lfd, pidoff) < 0)
2541553Srgrimes				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
2551553Srgrimes			openpr();		/* try to reopen printer */
2561553Srgrimes			goto restart;
2571553Srgrimes		}
2581553Srgrimes	}
2591553Srgrimes	free((char *) queue);
2601553Srgrimes	/*
2611553Srgrimes	 * search the spool directory for more work.
2621553Srgrimes	 */
2631553Srgrimes	if ((nitems = getq(&queue)) < 0) {
2641553Srgrimes		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
2651553Srgrimes		exit(1);
2661553Srgrimes	}
2671553Srgrimes	if (nitems == 0) {		/* no more work to do */
2681553Srgrimes	done:
2691553Srgrimes		if (count > 0) {	/* Files actually printed */
2701553Srgrimes			if (!SF && !tof)
2711553Srgrimes				(void) write(ofd, FF, strlen(FF));
2721553Srgrimes			if (TR != NULL)		/* output trailer */
2731553Srgrimes				(void) write(ofd, TR, strlen(TR));
2741553Srgrimes		}
2751553Srgrimes		(void) unlink(tempfile);
2761553Srgrimes		exit(0);
2771553Srgrimes	}
2781553Srgrimes	goto again;
2791553Srgrimes}
2801553Srgrimes
2811553Srgrimeschar	fonts[4][50];	/* fonts for troff */
2821553Srgrimes
2831553Srgrimeschar ifonts[4][40] = {
2841553Srgrimes	_PATH_VFONTR,
2851553Srgrimes	_PATH_VFONTI,
2861553Srgrimes	_PATH_VFONTB,
2871553Srgrimes	_PATH_VFONTS,
2881553Srgrimes};
2891553Srgrimes
2901553Srgrimes/*
2911553Srgrimes * The remaining part is the reading of the control file (cf)
2921553Srgrimes * and performing the various actions.
2931553Srgrimes */
2941553Srgrimesstatic int
2951553Srgrimesprintit(file)
2961553Srgrimes	char *file;
2971553Srgrimes{
2981553Srgrimes	register int i;
2991553Srgrimes	char *cp;
3001553Srgrimes	int bombed = OK;
3011553Srgrimes
3021553Srgrimes	/*
3031553Srgrimes	 * open control file; ignore if no longer there.
3041553Srgrimes	 */
3051553Srgrimes	if ((cfp = fopen(file, "r")) == NULL) {
3061553Srgrimes		syslog(LOG_INFO, "%s: %s: %m", printer, file);
3071553Srgrimes		return(OK);
3081553Srgrimes	}
3091553Srgrimes	/*
3101553Srgrimes	 * Reset troff fonts.
3111553Srgrimes	 */
3121553Srgrimes	for (i = 0; i < 4; i++)
3131553Srgrimes		strcpy(fonts[i], ifonts[i]);
3141553Srgrimes	sprintf(&width[2], "%d", PW);
3151553Srgrimes	strcpy(indent+2, "0");
3161553Srgrimes
3171553Srgrimes	/*
3181553Srgrimes	 *      read the control file for work to do
3191553Srgrimes	 *
3201553Srgrimes	 *      file format -- first character in the line is a command
3211553Srgrimes	 *      rest of the line is the argument.
3221553Srgrimes	 *      valid commands are:
3231553Srgrimes	 *
3241553Srgrimes	 *		S -- "stat info" for symbolic link protection
3251553Srgrimes	 *		J -- "job name" on banner page
3261553Srgrimes	 *		C -- "class name" on banner page
3271553Srgrimes	 *              L -- "literal" user's name to print on banner
3281553Srgrimes	 *		T -- "title" for pr
3291553Srgrimes	 *		H -- "host name" of machine where lpr was done
3301553Srgrimes	 *              P -- "person" user's login name
3311553Srgrimes	 *              I -- "indent" amount to indent output
3321553Srgrimes	 *              f -- "file name" name of text file to print
3331553Srgrimes	 *		l -- "file name" text file with control chars
3341553Srgrimes	 *		p -- "file name" text file to print with pr(1)
3351553Srgrimes	 *		t -- "file name" troff(1) file to print
3361553Srgrimes	 *		n -- "file name" ditroff(1) file to print
3371553Srgrimes	 *		d -- "file name" dvi file to print
3381553Srgrimes	 *		g -- "file name" plot(1G) file to print
3391553Srgrimes	 *		v -- "file name" plain raster file to print
3401553Srgrimes	 *		c -- "file name" cifplot file to print
3411553Srgrimes	 *		1 -- "R font file" for troff
3421553Srgrimes	 *		2 -- "I font file" for troff
3431553Srgrimes	 *		3 -- "B font file" for troff
3441553Srgrimes	 *		4 -- "S font file" for troff
3451553Srgrimes	 *		N -- "name" of file (used by lpq)
3461553Srgrimes	 *              U -- "unlink" name of file to remove
3471553Srgrimes	 *                    (after we print it. (Pass 2 only)).
3481553Srgrimes	 *		M -- "mail" to user when done printing
3491553Srgrimes	 *
3501553Srgrimes	 *      getline reads a line and expands tabs to blanks
3511553Srgrimes	 */
3521553Srgrimes
3531553Srgrimes	/* pass 1 */
3541553Srgrimes
3551553Srgrimes	while (getline(cfp))
3561553Srgrimes		switch (line[0]) {
3571553Srgrimes		case 'H':
3581553Srgrimes			strcpy(fromhost, line+1);
3591553Srgrimes			if (class[0] == '\0')
3601553Srgrimes				strncpy(class, line+1, sizeof(class)-1);
3611553Srgrimes			continue;
3621553Srgrimes
3631553Srgrimes		case 'P':
3641553Srgrimes			strncpy(logname, line+1, sizeof(logname)-1);
3651553Srgrimes			if (RS) {			/* restricted */
3661553Srgrimes				if (getpwnam(logname) == NULL) {
3671553Srgrimes					bombed = NOACCT;
3681553Srgrimes					sendmail(line+1, bombed);
3691553Srgrimes					goto pass2;
3701553Srgrimes				}
3711553Srgrimes			}
3721553Srgrimes			continue;
3731553Srgrimes
3741553Srgrimes		case 'S':
3751553Srgrimes			cp = line+1;
3761553Srgrimes			i = 0;
3771553Srgrimes			while (*cp >= '0' && *cp <= '9')
3781553Srgrimes				i = i * 10 + (*cp++ - '0');
3791553Srgrimes			fdev = i;
3801553Srgrimes			cp++;
3811553Srgrimes			i = 0;
3821553Srgrimes			while (*cp >= '0' && *cp <= '9')
3831553Srgrimes				i = i * 10 + (*cp++ - '0');
3841553Srgrimes			fino = i;
3851553Srgrimes			continue;
3861553Srgrimes
3871553Srgrimes		case 'J':
3881553Srgrimes			if (line[1] != '\0')
3891553Srgrimes				strncpy(jobname, line+1, sizeof(jobname)-1);
3901553Srgrimes			else
3911553Srgrimes				strcpy(jobname, " ");
3921553Srgrimes			continue;
3931553Srgrimes
3941553Srgrimes		case 'C':
3951553Srgrimes			if (line[1] != '\0')
3961553Srgrimes				strncpy(class, line+1, sizeof(class)-1);
3971553Srgrimes			else if (class[0] == '\0')
3981553Srgrimes				gethostname(class, sizeof(class));
3991553Srgrimes			continue;
4001553Srgrimes
4011553Srgrimes		case 'T':	/* header title for pr */
4021553Srgrimes			strncpy(title, line+1, sizeof(title)-1);
4031553Srgrimes			continue;
4041553Srgrimes
4051553Srgrimes		case 'L':	/* identification line */
4061553Srgrimes			if (!SH && !HL)
4071553Srgrimes				banner(line+1, jobname);
4081553Srgrimes			continue;
4091553Srgrimes
4101553Srgrimes		case '1':	/* troff fonts */
4111553Srgrimes		case '2':
4121553Srgrimes		case '3':
4131553Srgrimes		case '4':
4141553Srgrimes			if (line[1] != '\0')
4151553Srgrimes				strcpy(fonts[line[0]-'1'], line+1);
4161553Srgrimes			continue;
4171553Srgrimes
4181553Srgrimes		case 'W':	/* page width */
4191553Srgrimes			strncpy(width+2, line+1, sizeof(width)-3);
4201553Srgrimes			continue;
4211553Srgrimes
4221553Srgrimes		case 'I':	/* indent amount */
4231553Srgrimes			strncpy(indent+2, line+1, sizeof(indent)-3);
4241553Srgrimes			continue;
4251553Srgrimes
4261553Srgrimes		default:	/* some file to print */
4271553Srgrimes			switch (i = print(line[0], line+1)) {
4281553Srgrimes			case ERROR:
4291553Srgrimes				if (bombed == OK)
4301553Srgrimes					bombed = FATALERR;
4311553Srgrimes				break;
4321553Srgrimes			case REPRINT:
4331553Srgrimes				(void) fclose(cfp);
4341553Srgrimes				return(REPRINT);
4351553Srgrimes			case FILTERERR:
4361553Srgrimes			case ACCESS:
4371553Srgrimes				bombed = i;
4381553Srgrimes				sendmail(logname, bombed);
4391553Srgrimes			}
4401553Srgrimes			title[0] = '\0';
4411553Srgrimes			continue;
4421553Srgrimes
4431553Srgrimes		case 'N':
4441553Srgrimes		case 'U':
4451553Srgrimes		case 'M':
4461553Srgrimes			continue;
4471553Srgrimes		}
4481553Srgrimes
4491553Srgrimes	/* pass 2 */
4501553Srgrimes
4511553Srgrimespass2:
4521553Srgrimes	fseek(cfp, 0L, 0);
4531553Srgrimes	while (getline(cfp))
4541553Srgrimes		switch (line[0]) {
4551553Srgrimes		case 'L':	/* identification line */
4561553Srgrimes			if (!SH && HL)
4571553Srgrimes				banner(line+1, jobname);
4581553Srgrimes			continue;
4591553Srgrimes
4601553Srgrimes		case 'M':
4611553Srgrimes			if (bombed < NOACCT)	/* already sent if >= NOACCT */
4621553Srgrimes				sendmail(line+1, bombed);
4631553Srgrimes			continue;
4641553Srgrimes
4651553Srgrimes		case 'U':
4661553Srgrimes			(void) unlink(line+1);
4671553Srgrimes		}
4681553Srgrimes	/*
4691553Srgrimes	 * clean-up in case another control file exists
4701553Srgrimes	 */
4711553Srgrimes	(void) fclose(cfp);
4721553Srgrimes	(void) unlink(file);
4731553Srgrimes	return(bombed == OK ? OK : ERROR);
4741553Srgrimes}
4751553Srgrimes
4761553Srgrimes/*
4771553Srgrimes * Print a file.
4781553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
4791553Srgrimes * Return -1 if a non-recoverable error occured,
4801553Srgrimes * 2 if the filter detected some errors (but printed the job anyway),
4811553Srgrimes * 1 if we should try to reprint this job and
4821553Srgrimes * 0 if all is well.
4831553Srgrimes * Note: all filters take stdin as the file, stdout as the printer,
4841553Srgrimes * stderr as the log file, and must not ignore SIGINT.
4851553Srgrimes */
4861553Srgrimesstatic int
4871553Srgrimesprint(format, file)
4881553Srgrimes	int format;
4891553Srgrimes	char *file;
4901553Srgrimes{
4911553Srgrimes	register int n;
4921553Srgrimes	register char *prog;
4931553Srgrimes	int fi, fo;
4941553Srgrimes	FILE *fp;
4951553Srgrimes	char *av[15], buf[BUFSIZ];
4961553Srgrimes	int pid, p[2], stopped = 0;
4971553Srgrimes	union wait status;
4981553Srgrimes	struct stat stb;
4991553Srgrimes
5001553Srgrimes	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
5011553Srgrimes		return(ERROR);
5021553Srgrimes	/*
5031553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
5041553Srgrimes	 * still point to the same file or someone is trying to print
5051553Srgrimes	 * something he shouldn't.
5061553Srgrimes	 */
5071553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
5081553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
5091553Srgrimes		return(ACCESS);
5101553Srgrimes	if (!SF && !tof) {		/* start on a fresh page */
5111553Srgrimes		(void) write(ofd, FF, strlen(FF));
5121553Srgrimes		tof = 1;
5131553Srgrimes	}
5141553Srgrimes	if (IF == NULL && (format == 'f' || format == 'l')) {
5151553Srgrimes		tof = 0;
5161553Srgrimes		while ((n = read(fi, buf, BUFSIZ)) > 0)
5171553Srgrimes			if (write(ofd, buf, n) != n) {
5181553Srgrimes				(void) close(fi);
5191553Srgrimes				return(REPRINT);
5201553Srgrimes			}
5211553Srgrimes		(void) close(fi);
5221553Srgrimes		return(OK);
5231553Srgrimes	}
5241553Srgrimes	switch (format) {
5251553Srgrimes	case 'p':	/* print file using 'pr' */
5261553Srgrimes		if (IF == NULL) {	/* use output filter */
5271553Srgrimes			prog = _PATH_PR;
5281553Srgrimes			av[0] = "pr";
5291553Srgrimes			av[1] = width;
5301553Srgrimes			av[2] = length;
5311553Srgrimes			av[3] = "-h";
5321553Srgrimes			av[4] = *title ? title : " ";
5335445Sjoerg			av[5] = "-F";
5345445Sjoerg			av[6] = 0;
5351553Srgrimes			fo = ofd;
5361553Srgrimes			goto start;
5371553Srgrimes		}
5381553Srgrimes		pipe(p);
5391553Srgrimes		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
5401553Srgrimes			dup2(fi, 0);		/* file is stdin */
5411553Srgrimes			dup2(p[1], 1);		/* pipe is stdout */
5428094Sjkh			closelog();
5431553Srgrimes			for (n = 3; n < NOFILE; n++)
5441553Srgrimes				(void) close(n);
5451553Srgrimes			execl(_PATH_PR, "pr", width, length,
5465445Sjoerg			    "-h", *title ? title : " ", "-F", 0);
5478094Sjkh			openlog("lpd", LOG_PID, LOG_LPR);
5481553Srgrimes			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
5491553Srgrimes			exit(2);
5501553Srgrimes		}
5511553Srgrimes		(void) close(p[1]);		/* close output side */
5521553Srgrimes		(void) close(fi);
5531553Srgrimes		if (prchild < 0) {
5541553Srgrimes			prchild = 0;
5551553Srgrimes			(void) close(p[0]);
5561553Srgrimes			return(ERROR);
5571553Srgrimes		}
5581553Srgrimes		fi = p[0];			/* use pipe for input */
5591553Srgrimes	case 'f':	/* print plain text file */
5601553Srgrimes		prog = IF;
5611553Srgrimes		av[1] = width;
5621553Srgrimes		av[2] = length;
5631553Srgrimes		av[3] = indent;
5641553Srgrimes		n = 4;
5651553Srgrimes		break;
5661553Srgrimes	case 'l':	/* like 'f' but pass control characters */
5671553Srgrimes		prog = IF;
5681553Srgrimes		av[1] = "-c";
5691553Srgrimes		av[2] = width;
5701553Srgrimes		av[3] = length;
5711553Srgrimes		av[4] = indent;
5721553Srgrimes		n = 5;
5731553Srgrimes		break;
5741553Srgrimes	case 'r':	/* print a fortran text file */
5751553Srgrimes		prog = RF;
5761553Srgrimes		av[1] = width;
5771553Srgrimes		av[2] = length;
5781553Srgrimes		n = 3;
5791553Srgrimes		break;
5801553Srgrimes	case 't':	/* print troff output */
5811553Srgrimes	case 'n':	/* print ditroff output */
5821553Srgrimes	case 'd':	/* print tex output */
5831553Srgrimes		(void) unlink(".railmag");
5841553Srgrimes		if ((fo = creat(".railmag", FILMOD)) < 0) {
5851553Srgrimes			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
5861553Srgrimes			(void) unlink(".railmag");
5871553Srgrimes		} else {
5881553Srgrimes			for (n = 0; n < 4; n++) {
5891553Srgrimes				if (fonts[n][0] != '/')
5901553Srgrimes					(void) write(fo, _PATH_VFONT,
5911553Srgrimes					    sizeof(_PATH_VFONT) - 1);
5921553Srgrimes				(void) write(fo, fonts[n], strlen(fonts[n]));
5931553Srgrimes				(void) write(fo, "\n", 1);
5941553Srgrimes			}
5951553Srgrimes			(void) close(fo);
5961553Srgrimes		}
5971553Srgrimes		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
5981553Srgrimes		av[1] = pxwidth;
5991553Srgrimes		av[2] = pxlength;
6001553Srgrimes		n = 3;
6011553Srgrimes		break;
6021553Srgrimes	case 'c':	/* print cifplot output */
6031553Srgrimes		prog = CF;
6041553Srgrimes		av[1] = pxwidth;
6051553Srgrimes		av[2] = pxlength;
6061553Srgrimes		n = 3;
6071553Srgrimes		break;
6081553Srgrimes	case 'g':	/* print plot(1G) output */
6091553Srgrimes		prog = GF;
6101553Srgrimes		av[1] = pxwidth;
6111553Srgrimes		av[2] = pxlength;
6121553Srgrimes		n = 3;
6131553Srgrimes		break;
6141553Srgrimes	case 'v':	/* print raster output */
6151553Srgrimes		prog = VF;
6161553Srgrimes		av[1] = pxwidth;
6171553Srgrimes		av[2] = pxlength;
6181553Srgrimes		n = 3;
6191553Srgrimes		break;
6201553Srgrimes	default:
6211553Srgrimes		(void) close(fi);
6221553Srgrimes		syslog(LOG_ERR, "%s: illegal format character '%c'",
6231553Srgrimes			printer, format);
6241553Srgrimes		return(ERROR);
6251553Srgrimes	}
6261553Srgrimes	if ((av[0] = rindex(prog, '/')) != NULL)
6271553Srgrimes		av[0]++;
6281553Srgrimes	else
6291553Srgrimes		av[0] = prog;
6301553Srgrimes	av[n++] = "-n";
6311553Srgrimes	av[n++] = logname;
6321553Srgrimes	av[n++] = "-h";
6331553Srgrimes	av[n++] = fromhost;
6341553Srgrimes	av[n++] = AF;
6351553Srgrimes	av[n] = 0;
6361553Srgrimes	fo = pfd;
6371553Srgrimes	if (ofilter > 0) {		/* stop output filter */
6381553Srgrimes		write(ofd, "\031\1", 2);
6391553Srgrimes		while ((pid =
6401553Srgrimes		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
6411553Srgrimes			;
6421553Srgrimes		if (status.w_stopval != WSTOPPED) {
6431553Srgrimes			(void) close(fi);
6441553Srgrimes			syslog(LOG_WARNING, "%s: output filter died (%d)",
6451553Srgrimes				printer, status.w_retcode);
6461553Srgrimes			return(REPRINT);
6471553Srgrimes		}
6481553Srgrimes		stopped++;
6491553Srgrimes	}
6501553Srgrimesstart:
6511553Srgrimes	if ((child = dofork(DORETURN)) == 0) {	/* child */
6521553Srgrimes		dup2(fi, 0);
6531553Srgrimes		dup2(fo, 1);
6541553Srgrimes		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
6551553Srgrimes		if (n >= 0)
6561553Srgrimes			dup2(n, 2);
6578094Sjkh		closelog();
6581553Srgrimes		for (n = 3; n < NOFILE; n++)
6591553Srgrimes			(void) close(n);
6601553Srgrimes		execv(prog, av);
6618094Sjkh		openlog("lpd", LOG_PID, LOG_LPR);
6621553Srgrimes		syslog(LOG_ERR, "cannot execv %s", prog);
6631553Srgrimes		exit(2);
6641553Srgrimes	}
6651553Srgrimes	(void) close(fi);
6661553Srgrimes	if (child < 0)
6671553Srgrimes		status.w_retcode = 100;
6681553Srgrimes	else
6691553Srgrimes		while ((pid = wait((int *)&status)) > 0 && pid != child)
6701553Srgrimes			;
6711553Srgrimes	child = 0;
6721553Srgrimes	prchild = 0;
6731553Srgrimes	if (stopped) {		/* restart output filter */
6741553Srgrimes		if (kill(ofilter, SIGCONT) < 0) {
6751553Srgrimes			syslog(LOG_ERR, "cannot restart output filter");
6761553Srgrimes			exit(1);
6771553Srgrimes		}
6781553Srgrimes	}
6791553Srgrimes	tof = 0;
6801553Srgrimes
6811553Srgrimes	/* Copy filter output to "lf" logfile */
6821553Srgrimes	if (fp = fopen(tempfile, "r")) {
6831553Srgrimes		while (fgets(buf, sizeof(buf), fp))
6841553Srgrimes			fputs(buf, stderr);
6851553Srgrimes		fclose(fp);
6861553Srgrimes	}
6871553Srgrimes
6881553Srgrimes	if (!WIFEXITED(status)) {
6891553Srgrimes		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
6901553Srgrimes			printer, format, status.w_termsig);
6911553Srgrimes		return(ERROR);
6921553Srgrimes	}
6931553Srgrimes	switch (status.w_retcode) {
6941553Srgrimes	case 0:
6951553Srgrimes		tof = 1;
6961553Srgrimes		return(OK);
6971553Srgrimes	case 1:
6981553Srgrimes		return(REPRINT);
6991553Srgrimes	default:
7001553Srgrimes		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
7011553Srgrimes			printer, format, status.w_retcode);
7021553Srgrimes	case 2:
7031553Srgrimes		return(ERROR);
7041553Srgrimes	}
7051553Srgrimes}
7061553Srgrimes
7071553Srgrimes/*
7081553Srgrimes * Send the daemon control file (cf) and any data files.
7091553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
7101553Srgrimes * 0 if all is well.
7111553Srgrimes */
7121553Srgrimesstatic int
7131553Srgrimessendit(file)
7141553Srgrimes	char *file;
7151553Srgrimes{
7161553Srgrimes	register int i, err = OK;
7171553Srgrimes	char *cp, last[BUFSIZ];
7181553Srgrimes
7191553Srgrimes	/*
7201553Srgrimes	 * open control file
7211553Srgrimes	 */
7221553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
7231553Srgrimes		return(OK);
7241553Srgrimes	/*
7251553Srgrimes	 *      read the control file for work to do
7261553Srgrimes	 *
7271553Srgrimes	 *      file format -- first character in the line is a command
7281553Srgrimes	 *      rest of the line is the argument.
7291553Srgrimes	 *      commands of interest are:
7301553Srgrimes	 *
7311553Srgrimes	 *            a-z -- "file name" name of file to print
7321553Srgrimes	 *              U -- "unlink" name of file to remove
7331553Srgrimes	 *                    (after we print it. (Pass 2 only)).
7341553Srgrimes	 */
7351553Srgrimes
7361553Srgrimes	/*
7371553Srgrimes	 * pass 1
7381553Srgrimes	 */
7391553Srgrimes	while (getline(cfp)) {
7401553Srgrimes	again:
7411553Srgrimes		if (line[0] == 'S') {
7421553Srgrimes			cp = line+1;
7431553Srgrimes			i = 0;
7441553Srgrimes			while (*cp >= '0' && *cp <= '9')
7451553Srgrimes				i = i * 10 + (*cp++ - '0');
7461553Srgrimes			fdev = i;
7471553Srgrimes			cp++;
7481553Srgrimes			i = 0;
7491553Srgrimes			while (*cp >= '0' && *cp <= '9')
7501553Srgrimes				i = i * 10 + (*cp++ - '0');
7511553Srgrimes			fino = i;
7521553Srgrimes			continue;
7531553Srgrimes		}
7541553Srgrimes		if (line[0] >= 'a' && line[0] <= 'z') {
7551553Srgrimes			strcpy(last, line);
7561553Srgrimes			while (i = getline(cfp))
7571553Srgrimes				if (strcmp(last, line))
7581553Srgrimes					break;
7591553Srgrimes			switch (sendfile('\3', last+1)) {
7601553Srgrimes			case OK:
7611553Srgrimes				if (i)
7621553Srgrimes					goto again;
7631553Srgrimes				break;
7641553Srgrimes			case REPRINT:
7651553Srgrimes				(void) fclose(cfp);
7661553Srgrimes				return(REPRINT);
7671553Srgrimes			case ACCESS:
7681553Srgrimes				sendmail(logname, ACCESS);
7691553Srgrimes			case ERROR:
7701553Srgrimes				err = ERROR;
7711553Srgrimes			}
7721553Srgrimes			break;
7731553Srgrimes		}
7741553Srgrimes	}
7751553Srgrimes	if (err == OK && sendfile('\2', file) > 0) {
7761553Srgrimes		(void) fclose(cfp);
7771553Srgrimes		return(REPRINT);
7781553Srgrimes	}
7791553Srgrimes	/*
7801553Srgrimes	 * pass 2
7811553Srgrimes	 */
7821553Srgrimes	fseek(cfp, 0L, 0);
7831553Srgrimes	while (getline(cfp))
7841553Srgrimes		if (line[0] == 'U')
7851553Srgrimes			(void) unlink(line+1);
7861553Srgrimes	/*
7871553Srgrimes	 * clean-up in case another control file exists
7881553Srgrimes	 */
7891553Srgrimes	(void) fclose(cfp);
7901553Srgrimes	(void) unlink(file);
7911553Srgrimes	return(err);
7921553Srgrimes}
7931553Srgrimes
7941553Srgrimes/*
7951553Srgrimes * Send a data file to the remote machine and spool it.
7961553Srgrimes * Return positive if we should try resending.
7971553Srgrimes */
7981553Srgrimesstatic int
7991553Srgrimessendfile(type, file)
8001553Srgrimes	int type;
8011553Srgrimes	char *file;
8021553Srgrimes{
8031553Srgrimes	register int f, i, amt;
8041553Srgrimes	struct stat stb;
8051553Srgrimes	char buf[BUFSIZ];
8061553Srgrimes	int sizerr, resp;
8071553Srgrimes
8081553Srgrimes	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
8091553Srgrimes		return(ERROR);
8101553Srgrimes	/*
8111553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
8121553Srgrimes	 * still point to the same file or someone is trying to print something
8131553Srgrimes	 * he shouldn't.
8141553Srgrimes	 */
8151553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
8161553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
8171553Srgrimes		return(ACCESS);
8181553Srgrimes	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
8191553Srgrimes	amt = strlen(buf);
8201553Srgrimes	for (i = 0;  ; i++) {
8211553Srgrimes		if (write(pfd, buf, amt) != amt ||
8221553Srgrimes		    (resp = response()) < 0 || resp == '\1') {
8231553Srgrimes			(void) close(f);
8241553Srgrimes			return(REPRINT);
8251553Srgrimes		} else if (resp == '\0')
8261553Srgrimes			break;
8271553Srgrimes		if (i == 0)
8281553Srgrimes			pstatus("no space on remote; waiting for queue to drain");
8291553Srgrimes		if (i == 10)
8301553Srgrimes			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
8311553Srgrimes				printer, RM);
8321553Srgrimes		sleep(5 * 60);
8331553Srgrimes	}
8341553Srgrimes	if (i)
8351553Srgrimes		pstatus("sending to %s", RM);
8361553Srgrimes	sizerr = 0;
8371553Srgrimes	for (i = 0; i < stb.st_size; i += BUFSIZ) {
8381553Srgrimes		amt = BUFSIZ;
8391553Srgrimes		if (i + amt > stb.st_size)
8401553Srgrimes			amt = stb.st_size - i;
8411553Srgrimes		if (sizerr == 0 && read(f, buf, amt) != amt)
8421553Srgrimes			sizerr = 1;
8431553Srgrimes		if (write(pfd, buf, amt) != amt) {
8441553Srgrimes			(void) close(f);
8451553Srgrimes			return(REPRINT);
8461553Srgrimes		}
8471553Srgrimes	}
8481553Srgrimes
8491553Srgrimes
8501553Srgrimes
8511553Srgrimes
8521553Srgrimes	(void) close(f);
8531553Srgrimes	if (sizerr) {
8541553Srgrimes		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
8551553Srgrimes		/* tell recvjob to ignore this file */
8561553Srgrimes		(void) write(pfd, "\1", 1);
8571553Srgrimes		return(ERROR);
8581553Srgrimes	}
8591553Srgrimes	if (write(pfd, "", 1) != 1 || response())
8601553Srgrimes		return(REPRINT);
8611553Srgrimes	return(OK);
8621553Srgrimes}
8631553Srgrimes
8641553Srgrimes/*
8651553Srgrimes * Check to make sure there have been no errors and that both programs
8661553Srgrimes * are in sync with eachother.
8671553Srgrimes * Return non-zero if the connection was lost.
8681553Srgrimes */
8691553Srgrimesstatic char
8701553Srgrimesresponse()
8711553Srgrimes{
8721553Srgrimes	char resp;
8731553Srgrimes
8741553Srgrimes	if (read(pfd, &resp, 1) != 1) {
8751553Srgrimes		syslog(LOG_INFO, "%s: lost connection", printer);
8761553Srgrimes		return(-1);
8771553Srgrimes	}
8781553Srgrimes	return(resp);
8791553Srgrimes}
8801553Srgrimes
8811553Srgrimes/*
8821553Srgrimes * Banner printing stuff
8831553Srgrimes */
8841553Srgrimesstatic void
8851553Srgrimesbanner(name1, name2)
8861553Srgrimes	char *name1, *name2;
8871553Srgrimes{
8881553Srgrimes	time_t tvec;
8891553Srgrimes	extern char *ctime();
8901553Srgrimes
8911553Srgrimes	time(&tvec);
8921553Srgrimes	if (!SF && !tof)
8931553Srgrimes		(void) write(ofd, FF, strlen(FF));
8941553Srgrimes	if (SB) {	/* short banner only */
8951553Srgrimes		if (class[0]) {
8961553Srgrimes			(void) write(ofd, class, strlen(class));
8971553Srgrimes			(void) write(ofd, ":", 1);
8981553Srgrimes		}
8991553Srgrimes		(void) write(ofd, name1, strlen(name1));
9001553Srgrimes		(void) write(ofd, "  Job: ", 7);
9011553Srgrimes		(void) write(ofd, name2, strlen(name2));
9021553Srgrimes		(void) write(ofd, "  Date: ", 8);
9031553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
9041553Srgrimes		(void) write(ofd, "\n", 1);
9051553Srgrimes	} else {	/* normal banner */
9061553Srgrimes		(void) write(ofd, "\n\n\n", 3);
9071553Srgrimes		scan_out(ofd, name1, '\0');
9081553Srgrimes		(void) write(ofd, "\n\n", 2);
9091553Srgrimes		scan_out(ofd, name2, '\0');
9101553Srgrimes		if (class[0]) {
9111553Srgrimes			(void) write(ofd,"\n\n\n",3);
9121553Srgrimes			scan_out(ofd, class, '\0');
9131553Srgrimes		}
9141553Srgrimes		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
9151553Srgrimes		(void) write(ofd, name2, strlen(name2));
9161553Srgrimes		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
9171553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
9181553Srgrimes		(void) write(ofd, "\n", 1);
9191553Srgrimes	}
9201553Srgrimes	if (!SF)
9211553Srgrimes		(void) write(ofd, FF, strlen(FF));
9221553Srgrimes	tof = 1;
9231553Srgrimes}
9241553Srgrimes
9251553Srgrimesstatic char *
9261553Srgrimesscnline(key, p, c)
9271553Srgrimes	register int key;
9281553Srgrimes	register char *p;
9291553Srgrimes	int c;
9301553Srgrimes{
9311553Srgrimes	register scnwidth;
9321553Srgrimes
9331553Srgrimes	for (scnwidth = WIDTH; --scnwidth;) {
9341553Srgrimes		key <<= 1;
9351553Srgrimes		*p++ = key & 0200 ? c : BACKGND;
9361553Srgrimes	}
9371553Srgrimes	return (p);
9381553Srgrimes}
9391553Srgrimes
9401553Srgrimes#define TRC(q)	(((q)-' ')&0177)
9411553Srgrimes
9421553Srgrimesstatic void
9431553Srgrimesscan_out(scfd, scsp, dlm)
9441553Srgrimes	int scfd, dlm;
9451553Srgrimes	char *scsp;
9461553Srgrimes{
9471553Srgrimes	register char *strp;
9481553Srgrimes	register nchrs, j;
9491553Srgrimes	char outbuf[LINELEN+1], *sp, c, cc;
9501553Srgrimes	int d, scnhgt;
9511553Srgrimes	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
9521553Srgrimes
9531553Srgrimes	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
9541553Srgrimes		strp = &outbuf[0];
9551553Srgrimes		sp = scsp;
9561553Srgrimes		for (nchrs = 0; ; ) {
9571553Srgrimes			d = dropit(c = TRC(cc = *sp++));
9581553Srgrimes			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
9591553Srgrimes				for (j = WIDTH; --j;)
9601553Srgrimes					*strp++ = BACKGND;
9611553Srgrimes			else
9621553Srgrimes				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
9631553Srgrimes			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
9641553Srgrimes				break;
9651553Srgrimes			*strp++ = BACKGND;
9661553Srgrimes			*strp++ = BACKGND;
9671553Srgrimes		}
9681553Srgrimes		while (*--strp == BACKGND && strp >= outbuf)
9691553Srgrimes			;
9701553Srgrimes		strp++;
9718857Srgrimes		*strp++ = '\n';
9721553Srgrimes		(void) write(scfd, outbuf, strp-outbuf);
9731553Srgrimes	}
9741553Srgrimes}
9751553Srgrimes
9761553Srgrimesstatic int
9771553Srgrimesdropit(c)
9781553Srgrimes	int c;
9791553Srgrimes{
9801553Srgrimes	switch(c) {
9811553Srgrimes
9821553Srgrimes	case TRC('_'):
9831553Srgrimes	case TRC(';'):
9841553Srgrimes	case TRC(','):
9851553Srgrimes	case TRC('g'):
9861553Srgrimes	case TRC('j'):
9871553Srgrimes	case TRC('p'):
9881553Srgrimes	case TRC('q'):
9891553Srgrimes	case TRC('y'):
9901553Srgrimes		return (DROP);
9911553Srgrimes
9921553Srgrimes	default:
9931553Srgrimes		return (0);
9941553Srgrimes	}
9951553Srgrimes}
9961553Srgrimes
9971553Srgrimes/*
9981553Srgrimes * sendmail ---
9991553Srgrimes *   tell people about job completion
10001553Srgrimes */
10011553Srgrimesstatic void
10021553Srgrimessendmail(user, bombed)
10031553Srgrimes	char *user;
10041553Srgrimes	int bombed;
10051553Srgrimes{
10061553Srgrimes	register int i;
10071553Srgrimes	int p[2], s;
10081553Srgrimes	register char *cp;
10091553Srgrimes	char buf[100];
10101553Srgrimes	struct stat stb;
10111553Srgrimes	FILE *fp;
10121553Srgrimes
10131553Srgrimes	pipe(p);
10141553Srgrimes	if ((s = dofork(DORETURN)) == 0) {		/* child */
10151553Srgrimes		dup2(p[0], 0);
10168094Sjkh		closelog();
10171553Srgrimes		for (i = 3; i < NOFILE; i++)
10181553Srgrimes			(void) close(i);
10191553Srgrimes		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
10201553Srgrimes			cp++;
10211553Srgrimes	else
10221553Srgrimes			cp = _PATH_SENDMAIL;
10231553Srgrimes		sprintf(buf, "%s@%s", user, fromhost);
10241553Srgrimes		execl(_PATH_SENDMAIL, cp, buf, 0);
10258094Sjkh		openlog("lpd", LOG_PID, LOG_LPR);
10268094Sjkh		syslog(LOG_ERR, "cannot execl %s", _PATH_SENDMAIL);
10271553Srgrimes		exit(0);
10281553Srgrimes	} else if (s > 0) {				/* parent */
10291553Srgrimes		dup2(p[1], 1);
10301553Srgrimes		printf("To: %s@%s\n", user, fromhost);
10311553Srgrimes		printf("Subject: printer job\n\n");
10321553Srgrimes		printf("Your printer job ");
10331553Srgrimes		if (*jobname)
10341553Srgrimes			printf("(%s) ", jobname);
10351553Srgrimes		switch (bombed) {
10361553Srgrimes		case OK:
10371553Srgrimes			printf("\ncompleted successfully\n");
10381553Srgrimes			break;
10391553Srgrimes		default:
10401553Srgrimes		case FATALERR:
10411553Srgrimes			printf("\ncould not be printed\n");
10421553Srgrimes			break;
10431553Srgrimes		case NOACCT:
10441553Srgrimes			printf("\ncould not be printed without an account on %s\n", host);
10451553Srgrimes			break;
10461553Srgrimes		case FILTERERR:
10471553Srgrimes			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
10481553Srgrimes			    (fp = fopen(tempfile, "r")) == NULL) {
10491553Srgrimes				printf("\nwas printed but had some errors\n");
10501553Srgrimes				break;
10511553Srgrimes			}
10521553Srgrimes			printf("\nwas printed but had the following errors:\n");
10531553Srgrimes			while ((i = getc(fp)) != EOF)
10541553Srgrimes				putchar(i);
10551553Srgrimes			(void) fclose(fp);
10561553Srgrimes			break;
10571553Srgrimes		case ACCESS:
10581553Srgrimes			printf("\nwas not printed because it was not linked to the original file\n");
10591553Srgrimes		}
10601553Srgrimes		fflush(stdout);
10611553Srgrimes		(void) close(1);
10621553Srgrimes	}
10631553Srgrimes	(void) close(p[0]);
10641553Srgrimes	(void) close(p[1]);
10651553Srgrimes	wait(&s);
10661553Srgrimes}
10671553Srgrimes
10681553Srgrimes/*
10691553Srgrimes * dofork - fork with retries on failure
10701553Srgrimes */
10711553Srgrimesstatic int
10721553Srgrimesdofork(action)
10731553Srgrimes	int action;
10741553Srgrimes{
10751553Srgrimes	register int i, pid;
107610530Smpp	struct passwd *pwd;
10771553Srgrimes
10781553Srgrimes	for (i = 0; i < 20; i++) {
10791553Srgrimes		if ((pid = fork()) < 0) {
10801553Srgrimes			sleep((unsigned)(i*i));
10811553Srgrimes			continue;
10821553Srgrimes		}
10831553Srgrimes		/*
10841553Srgrimes		 * Child should run as daemon instead of root
10851553Srgrimes		 */
108610530Smpp		if (pid == 0) {
108710530Smpp			if ((pwd = getpwuid(DU)) == NULL) {
108810530Smpp				syslog(LOG_ERR, "Can't lookup default uid in password file");
108910530Smpp				break;
109010530Smpp			}
109110530Smpp			initgroups(pwd->pw_name, pwd->pw_gid);
109210530Smpp			setgid(pwd->pw_gid);
10931553Srgrimes			setuid(DU);
109410530Smpp		}
10951553Srgrimes		return(pid);
10961553Srgrimes	}
10971553Srgrimes	syslog(LOG_ERR, "can't fork");
10981553Srgrimes
10991553Srgrimes	switch (action) {
11001553Srgrimes	case DORETURN:
11011553Srgrimes		return (-1);
11021553Srgrimes	default:
11031553Srgrimes		syslog(LOG_ERR, "bad action (%d) to dofork", action);
11041553Srgrimes		/*FALL THRU*/
11051553Srgrimes	case DOABORT:
11061553Srgrimes		exit(1);
11071553Srgrimes	}
11081553Srgrimes	/*NOTREACHED*/
11091553Srgrimes}
11101553Srgrimes
11111553Srgrimes/*
11121553Srgrimes * Kill child processes to abort current job.
11131553Srgrimes */
11141553Srgrimesstatic void
11151553Srgrimesabortpr(signo)
11161553Srgrimes	int signo;
11171553Srgrimes{
11181553Srgrimes	(void) unlink(tempfile);
11191553Srgrimes	kill(0, SIGINT);
11201553Srgrimes	if (ofilter > 0)
11211553Srgrimes		kill(ofilter, SIGCONT);
11221553Srgrimes	while (wait(NULL) > 0)
11231553Srgrimes		;
11241553Srgrimes	exit(0);
11251553Srgrimes}
11261553Srgrimes
11271553Srgrimesstatic void
11281553Srgrimesinit()
11291553Srgrimes{
11301553Srgrimes	int status;
11311553Srgrimes	char *s;
11321553Srgrimes
11331553Srgrimes	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
11341553Srgrimes		syslog(LOG_ERR, "can't open printer description file");
11351553Srgrimes		exit(1);
11361553Srgrimes	} else if (status == -1) {
11371553Srgrimes		syslog(LOG_ERR, "unknown printer: %s", printer);
11381553Srgrimes		exit(1);
11391553Srgrimes	} else if (status == -3)
11401553Srgrimes		fatal("potential reference loop detected in printcap file");
11411553Srgrimes
11421553Srgrimes	if (cgetstr(bp, "lp", &LP) == -1)
11431553Srgrimes		LP = _PATH_DEFDEVLP;
11441553Srgrimes	if (cgetstr(bp, "rp", &RP) == -1)
11451553Srgrimes		RP = DEFLP;
11461553Srgrimes	if (cgetstr(bp, "lo", &LO) == -1)
11471553Srgrimes		LO = DEFLOCK;
11481553Srgrimes	if (cgetstr(bp, "st", &ST) == -1)
11491553Srgrimes		ST = DEFSTAT;
11501553Srgrimes	if (cgetstr(bp, "lf", &LF) == -1)
11511553Srgrimes		LF = _PATH_CONSOLE;
11521553Srgrimes	if (cgetstr(bp, "sd", &SD) == -1)
11531553Srgrimes		SD = _PATH_DEFSPOOL;
11541553Srgrimes	if (cgetnum(bp, "du", &DU) < 0)
11551553Srgrimes		DU = DEFUID;
11561553Srgrimes	if (cgetstr(bp,"ff", &FF) == -1)
11571553Srgrimes		FF = DEFFF;
11581553Srgrimes	if (cgetnum(bp, "pw", &PW) < 0)
11591553Srgrimes		PW = DEFWIDTH;
11601553Srgrimes	sprintf(&width[2], "%d", PW);
11611553Srgrimes	if (cgetnum(bp, "pl", &PL) < 0)
11621553Srgrimes		PL = DEFLENGTH;
11631553Srgrimes	sprintf(&length[2], "%d", PL);
11641553Srgrimes	if (cgetnum(bp,"px", &PX) < 0)
11651553Srgrimes		PX = 0;
11661553Srgrimes	sprintf(&pxwidth[2], "%d", PX);
11671553Srgrimes	if (cgetnum(bp, "py", &PY) < 0)
11681553Srgrimes		PY = 0;
11691553Srgrimes	sprintf(&pxlength[2], "%d", PY);
11701553Srgrimes	cgetstr(bp, "rm", &RM);
11711553Srgrimes	if (s = checkremote())
11721553Srgrimes		syslog(LOG_WARNING, s);
11731553Srgrimes
11741553Srgrimes	cgetstr(bp, "af", &AF);
11751553Srgrimes	cgetstr(bp, "of", &OF);
11761553Srgrimes	cgetstr(bp, "if", &IF);
11771553Srgrimes	cgetstr(bp, "rf", &RF);
11781553Srgrimes	cgetstr(bp, "tf", &TF);
11791553Srgrimes	cgetstr(bp, "nf", &NF);
11801553Srgrimes	cgetstr(bp, "df", &DF);
11811553Srgrimes	cgetstr(bp, "gf", &GF);
11821553Srgrimes	cgetstr(bp, "vf", &VF);
11831553Srgrimes	cgetstr(bp, "cf", &CF);
11841553Srgrimes	cgetstr(bp, "tr", &TR);
118515032Ssef	cgetstr(bp, "ms", &MS);
11861553Srgrimes
11871553Srgrimes	RS = (cgetcap(bp, "rs", ':') != NULL);
11881553Srgrimes	SF = (cgetcap(bp, "sf", ':') != NULL);
11891553Srgrimes	SH = (cgetcap(bp, "sh", ':') != NULL);
11901553Srgrimes	SB = (cgetcap(bp, "sb", ':') != NULL);
11911553Srgrimes	HL = (cgetcap(bp, "hl", ':') != NULL);
11921553Srgrimes	RW = (cgetcap(bp, "rw", ':') != NULL);
11931553Srgrimes
11941553Srgrimes	cgetnum(bp, "br", &BR);
11951553Srgrimes
11961553Srgrimes	tof = (cgetcap(bp, "fo", ':') == NULL);
11971553Srgrimes}
11981553Srgrimes
11991553Srgrimes/*
12001553Srgrimes * Acquire line printer or remote connection.
12011553Srgrimes */
12021553Srgrimesstatic void
12031553Srgrimesopenpr()
12041553Srgrimes{
12051553Srgrimes	register int i, n;
12061553Srgrimes	int resp;
12071553Srgrimes
12081553Srgrimes	if (!sendtorem && *LP) {
12091553Srgrimes		for (i = 1; ; i = i < 32 ? i << 1 : i) {
12101553Srgrimes			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
12111553Srgrimes			if (pfd >= 0)
12121553Srgrimes				break;
12131553Srgrimes			if (errno == ENOENT) {
12141553Srgrimes				syslog(LOG_ERR, "%s: %m", LP);
12151553Srgrimes				exit(1);
12161553Srgrimes			}
12171553Srgrimes			if (i == 1)
12181553Srgrimes				pstatus("waiting for %s to become ready (offline ?)", printer);
12191553Srgrimes			sleep(i);
12201553Srgrimes		}
12211553Srgrimes		if (isatty(pfd))
12221553Srgrimes			setty();
12231553Srgrimes		pstatus("%s is ready and printing", printer);
12241553Srgrimes	} else if (RM != NULL) {
12251553Srgrimes		for (i = 1; ; i = i < 256 ? i << 1 : i) {
12261553Srgrimes			resp = -1;
12271553Srgrimes			pfd = getport(RM);
12281553Srgrimes			if (pfd >= 0) {
12291553Srgrimes				(void) sprintf(line, "\2%s\n", RP);
12301553Srgrimes				n = strlen(line);
12311553Srgrimes				if (write(pfd, line, n) == n &&
12321553Srgrimes				    (resp = response()) == '\0')
12331553Srgrimes					break;
12341553Srgrimes				(void) close(pfd);
12351553Srgrimes			}
12361553Srgrimes			if (i == 1) {
12371553Srgrimes				if (resp < 0)
12381553Srgrimes					pstatus("waiting for %s to come up", RM);
12391553Srgrimes				else {
12401553Srgrimes					pstatus("waiting for queue to be enabled on %s", RM);
12411553Srgrimes					i = 256;
12421553Srgrimes				}
12431553Srgrimes			}
12441553Srgrimes			sleep(i);
12451553Srgrimes		}
12461553Srgrimes		pstatus("sending to %s", RM);
12471553Srgrimes		remote = 1;
12481553Srgrimes	} else {
12491553Srgrimes		syslog(LOG_ERR, "%s: no line printer device or host name",
12501553Srgrimes			printer);
12511553Srgrimes		exit(1);
12521553Srgrimes	}
12531553Srgrimes	/*
12541553Srgrimes	 * Start up an output filter, if needed.
12551553Srgrimes	 */
12561553Srgrimes	if (!remote && OF) {
12571553Srgrimes		int p[2];
12581553Srgrimes		char *cp;
12591553Srgrimes
12601553Srgrimes		pipe(p);
12611553Srgrimes		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
12621553Srgrimes			dup2(p[0], 0);		/* pipe is std in */
12631553Srgrimes			dup2(pfd, 1);		/* printer is std out */
12648094Sjkh			closelog();
12651553Srgrimes			for (i = 3; i < NOFILE; i++)
12661553Srgrimes				(void) close(i);
12671553Srgrimes			if ((cp = rindex(OF, '/')) == NULL)
12681553Srgrimes				cp = OF;
12691553Srgrimes			else
12701553Srgrimes				cp++;
12711553Srgrimes			execl(OF, cp, width, length, 0);
12728094Sjkh			openlog("lpd", LOG_PID, LOG_LPR);
12731553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
12741553Srgrimes			exit(1);
12751553Srgrimes		}
12761553Srgrimes		(void) close(p[0]);		/* close input side */
12771553Srgrimes		ofd = p[1];			/* use pipe for output */
12781553Srgrimes	} else {
12791553Srgrimes		ofd = pfd;
12801553Srgrimes		ofilter = 0;
12811553Srgrimes	}
12821553Srgrimes}
12831553Srgrimes
12841553Srgrimesstruct bauds {
12851553Srgrimes	int	baud;
12861553Srgrimes	int	speed;
12871553Srgrimes} bauds[] = {
12881553Srgrimes	50,	B50,
12891553Srgrimes	75,	B75,
12901553Srgrimes	110,	B110,
12911553Srgrimes	134,	B134,
12921553Srgrimes	150,	B150,
12931553Srgrimes	200,	B200,
12941553Srgrimes	300,	B300,
12951553Srgrimes	600,	B600,
12961553Srgrimes	1200,	B1200,
12971553Srgrimes	1800,	B1800,
12981553Srgrimes	2400,	B2400,
12991553Srgrimes	4800,	B4800,
13001553Srgrimes	9600,	B9600,
13011553Srgrimes	19200,	EXTA,
13021553Srgrimes	38400,	EXTB,
13039821Swpaul	57600,	B57600,
13049821Swpaul	115200,	B115200,
13051553Srgrimes	0,	0
13061553Srgrimes};
13071553Srgrimes
13081553Srgrimes/*
13091553Srgrimes * setup tty lines.
13101553Srgrimes */
13111553Srgrimesstatic void
13121553Srgrimessetty()
13131553Srgrimes{
131415032Ssef	struct termios ttybuf;
131515032Ssef	struct bauds *bp;
13161553Srgrimes
13171553Srgrimes	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
13181553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
13191553Srgrimes		exit(1);
13201553Srgrimes	}
132115032Ssef	if (tcgetattr(pfd, &ttybuf) < 0) {
132215032Ssef		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
13231553Srgrimes		exit(1);
13241553Srgrimes	}
13251553Srgrimes	if (BR > 0) {
13261553Srgrimes		for (bp = bauds; bp->baud; bp++)
13271553Srgrimes			if (BR == bp->baud)
13281553Srgrimes				break;
13291553Srgrimes		if (!bp->baud) {
13301553Srgrimes			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
13311553Srgrimes			exit(1);
13321553Srgrimes		}
133315032Ssef		cfsetspeed(&ttybuf, bp->speed);
13341553Srgrimes	}
133515032Ssef	if (MS) {
133615032Ssef		char *s = strdup(MS), *tmp;
133715032Ssef
133815032Ssef		while (tmp = strsep (&s, ",")) {
133915032Ssef			msearch(tmp, &ttybuf);
13401553Srgrimes		}
13411553Srgrimes	}
134215032Ssef	if (MS || (BR > 0)) {
134315032Ssef		if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
134415032Ssef			syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
13451553Srgrimes		}
13461553Srgrimes	}
13471553Srgrimes}
13481553Srgrimes
13491553Srgrimes#if __STDC__
13501553Srgrimes#include <stdarg.h>
13511553Srgrimes#else
13521553Srgrimes#include <varargs.h>
13531553Srgrimes#endif
13541553Srgrimes
13551553Srgrimesvoid
13561553Srgrimes#if __STDC__
13571553Srgrimespstatus(const char *msg, ...)
13581553Srgrimes#else
13591553Srgrimespstatus(msg, va_alist)
13601553Srgrimes	char *msg;
13611553Srgrimes        va_dcl
13621553Srgrimes#endif
13631553Srgrimes{
13641553Srgrimes	register int fd;
13651553Srgrimes	char buf[BUFSIZ];
13661553Srgrimes	va_list ap;
13671553Srgrimes#if __STDC__
13681553Srgrimes	va_start(ap, msg);
13691553Srgrimes#else
13701553Srgrimes	va_start(ap);
13711553Srgrimes#endif
13721553Srgrimes
13731553Srgrimes	umask(0);
13741553Srgrimes	fd = open(ST, O_WRONLY|O_CREAT, 0664);
13751553Srgrimes	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
13761553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
13771553Srgrimes		exit(1);
13781553Srgrimes	}
13791553Srgrimes	ftruncate(fd, 0);
13801553Srgrimes	(void)vsnprintf(buf, sizeof(buf), msg, ap);
13811553Srgrimes	va_end(ap);
13821553Srgrimes	strcat(buf, "\n");
13831553Srgrimes	(void) write(fd, buf, strlen(buf));
13841553Srgrimes	(void) close(fd);
13851553Srgrimes}
1386