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