printjob.c revision 1553
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 : " ";
5321553Srgrimes			av[5] = 0;
5331553Srgrimes			fo = ofd;
5341553Srgrimes			goto start;
5351553Srgrimes		}
5361553Srgrimes		pipe(p);
5371553Srgrimes		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
5381553Srgrimes			dup2(fi, 0);		/* file is stdin */
5391553Srgrimes			dup2(p[1], 1);		/* pipe is stdout */
5401553Srgrimes			for (n = 3; n < NOFILE; n++)
5411553Srgrimes				(void) close(n);
5421553Srgrimes			execl(_PATH_PR, "pr", width, length,
5431553Srgrimes			    "-h", *title ? title : " ", 0);
5441553Srgrimes			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
5451553Srgrimes			exit(2);
5461553Srgrimes		}
5471553Srgrimes		(void) close(p[1]);		/* close output side */
5481553Srgrimes		(void) close(fi);
5491553Srgrimes		if (prchild < 0) {
5501553Srgrimes			prchild = 0;
5511553Srgrimes			(void) close(p[0]);
5521553Srgrimes			return(ERROR);
5531553Srgrimes		}
5541553Srgrimes		fi = p[0];			/* use pipe for input */
5551553Srgrimes	case 'f':	/* print plain text file */
5561553Srgrimes		prog = IF;
5571553Srgrimes		av[1] = width;
5581553Srgrimes		av[2] = length;
5591553Srgrimes		av[3] = indent;
5601553Srgrimes		n = 4;
5611553Srgrimes		break;
5621553Srgrimes	case 'l':	/* like 'f' but pass control characters */
5631553Srgrimes		prog = IF;
5641553Srgrimes		av[1] = "-c";
5651553Srgrimes		av[2] = width;
5661553Srgrimes		av[3] = length;
5671553Srgrimes		av[4] = indent;
5681553Srgrimes		n = 5;
5691553Srgrimes		break;
5701553Srgrimes	case 'r':	/* print a fortran text file */
5711553Srgrimes		prog = RF;
5721553Srgrimes		av[1] = width;
5731553Srgrimes		av[2] = length;
5741553Srgrimes		n = 3;
5751553Srgrimes		break;
5761553Srgrimes	case 't':	/* print troff output */
5771553Srgrimes	case 'n':	/* print ditroff output */
5781553Srgrimes	case 'd':	/* print tex output */
5791553Srgrimes		(void) unlink(".railmag");
5801553Srgrimes		if ((fo = creat(".railmag", FILMOD)) < 0) {
5811553Srgrimes			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
5821553Srgrimes			(void) unlink(".railmag");
5831553Srgrimes		} else {
5841553Srgrimes			for (n = 0; n < 4; n++) {
5851553Srgrimes				if (fonts[n][0] != '/')
5861553Srgrimes					(void) write(fo, _PATH_VFONT,
5871553Srgrimes					    sizeof(_PATH_VFONT) - 1);
5881553Srgrimes				(void) write(fo, fonts[n], strlen(fonts[n]));
5891553Srgrimes				(void) write(fo, "\n", 1);
5901553Srgrimes			}
5911553Srgrimes			(void) close(fo);
5921553Srgrimes		}
5931553Srgrimes		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
5941553Srgrimes		av[1] = pxwidth;
5951553Srgrimes		av[2] = pxlength;
5961553Srgrimes		n = 3;
5971553Srgrimes		break;
5981553Srgrimes	case 'c':	/* print cifplot output */
5991553Srgrimes		prog = CF;
6001553Srgrimes		av[1] = pxwidth;
6011553Srgrimes		av[2] = pxlength;
6021553Srgrimes		n = 3;
6031553Srgrimes		break;
6041553Srgrimes	case 'g':	/* print plot(1G) output */
6051553Srgrimes		prog = GF;
6061553Srgrimes		av[1] = pxwidth;
6071553Srgrimes		av[2] = pxlength;
6081553Srgrimes		n = 3;
6091553Srgrimes		break;
6101553Srgrimes	case 'v':	/* print raster output */
6111553Srgrimes		prog = VF;
6121553Srgrimes		av[1] = pxwidth;
6131553Srgrimes		av[2] = pxlength;
6141553Srgrimes		n = 3;
6151553Srgrimes		break;
6161553Srgrimes	default:
6171553Srgrimes		(void) close(fi);
6181553Srgrimes		syslog(LOG_ERR, "%s: illegal format character '%c'",
6191553Srgrimes			printer, format);
6201553Srgrimes		return(ERROR);
6211553Srgrimes	}
6221553Srgrimes	if ((av[0] = rindex(prog, '/')) != NULL)
6231553Srgrimes		av[0]++;
6241553Srgrimes	else
6251553Srgrimes		av[0] = prog;
6261553Srgrimes	av[n++] = "-n";
6271553Srgrimes	av[n++] = logname;
6281553Srgrimes	av[n++] = "-h";
6291553Srgrimes	av[n++] = fromhost;
6301553Srgrimes	av[n++] = AF;
6311553Srgrimes	av[n] = 0;
6321553Srgrimes	fo = pfd;
6331553Srgrimes	if (ofilter > 0) {		/* stop output filter */
6341553Srgrimes		write(ofd, "\031\1", 2);
6351553Srgrimes		while ((pid =
6361553Srgrimes		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
6371553Srgrimes			;
6381553Srgrimes		if (status.w_stopval != WSTOPPED) {
6391553Srgrimes			(void) close(fi);
6401553Srgrimes			syslog(LOG_WARNING, "%s: output filter died (%d)",
6411553Srgrimes				printer, status.w_retcode);
6421553Srgrimes			return(REPRINT);
6431553Srgrimes		}
6441553Srgrimes		stopped++;
6451553Srgrimes	}
6461553Srgrimesstart:
6471553Srgrimes	if ((child = dofork(DORETURN)) == 0) {	/* child */
6481553Srgrimes		dup2(fi, 0);
6491553Srgrimes		dup2(fo, 1);
6501553Srgrimes		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
6511553Srgrimes		if (n >= 0)
6521553Srgrimes			dup2(n, 2);
6531553Srgrimes		for (n = 3; n < NOFILE; n++)
6541553Srgrimes			(void) close(n);
6551553Srgrimes		execv(prog, av);
6561553Srgrimes		syslog(LOG_ERR, "cannot execv %s", prog);
6571553Srgrimes		exit(2);
6581553Srgrimes	}
6591553Srgrimes	(void) close(fi);
6601553Srgrimes	if (child < 0)
6611553Srgrimes		status.w_retcode = 100;
6621553Srgrimes	else
6631553Srgrimes		while ((pid = wait((int *)&status)) > 0 && pid != child)
6641553Srgrimes			;
6651553Srgrimes	child = 0;
6661553Srgrimes	prchild = 0;
6671553Srgrimes	if (stopped) {		/* restart output filter */
6681553Srgrimes		if (kill(ofilter, SIGCONT) < 0) {
6691553Srgrimes			syslog(LOG_ERR, "cannot restart output filter");
6701553Srgrimes			exit(1);
6711553Srgrimes		}
6721553Srgrimes	}
6731553Srgrimes	tof = 0;
6741553Srgrimes
6751553Srgrimes	/* Copy filter output to "lf" logfile */
6761553Srgrimes	if (fp = fopen(tempfile, "r")) {
6771553Srgrimes		while (fgets(buf, sizeof(buf), fp))
6781553Srgrimes			fputs(buf, stderr);
6791553Srgrimes		fclose(fp);
6801553Srgrimes	}
6811553Srgrimes
6821553Srgrimes	if (!WIFEXITED(status)) {
6831553Srgrimes		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
6841553Srgrimes			printer, format, status.w_termsig);
6851553Srgrimes		return(ERROR);
6861553Srgrimes	}
6871553Srgrimes	switch (status.w_retcode) {
6881553Srgrimes	case 0:
6891553Srgrimes		tof = 1;
6901553Srgrimes		return(OK);
6911553Srgrimes	case 1:
6921553Srgrimes		return(REPRINT);
6931553Srgrimes	default:
6941553Srgrimes		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
6951553Srgrimes			printer, format, status.w_retcode);
6961553Srgrimes	case 2:
6971553Srgrimes		return(ERROR);
6981553Srgrimes	}
6991553Srgrimes}
7001553Srgrimes
7011553Srgrimes/*
7021553Srgrimes * Send the daemon control file (cf) and any data files.
7031553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
7041553Srgrimes * 0 if all is well.
7051553Srgrimes */
7061553Srgrimesstatic int
7071553Srgrimessendit(file)
7081553Srgrimes	char *file;
7091553Srgrimes{
7101553Srgrimes	register int i, err = OK;
7111553Srgrimes	char *cp, last[BUFSIZ];
7121553Srgrimes
7131553Srgrimes	/*
7141553Srgrimes	 * open control file
7151553Srgrimes	 */
7161553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
7171553Srgrimes		return(OK);
7181553Srgrimes	/*
7191553Srgrimes	 *      read the control file for work to do
7201553Srgrimes	 *
7211553Srgrimes	 *      file format -- first character in the line is a command
7221553Srgrimes	 *      rest of the line is the argument.
7231553Srgrimes	 *      commands of interest are:
7241553Srgrimes	 *
7251553Srgrimes	 *            a-z -- "file name" name of file to print
7261553Srgrimes	 *              U -- "unlink" name of file to remove
7271553Srgrimes	 *                    (after we print it. (Pass 2 only)).
7281553Srgrimes	 */
7291553Srgrimes
7301553Srgrimes	/*
7311553Srgrimes	 * pass 1
7321553Srgrimes	 */
7331553Srgrimes	while (getline(cfp)) {
7341553Srgrimes	again:
7351553Srgrimes		if (line[0] == 'S') {
7361553Srgrimes			cp = line+1;
7371553Srgrimes			i = 0;
7381553Srgrimes			while (*cp >= '0' && *cp <= '9')
7391553Srgrimes				i = i * 10 + (*cp++ - '0');
7401553Srgrimes			fdev = i;
7411553Srgrimes			cp++;
7421553Srgrimes			i = 0;
7431553Srgrimes			while (*cp >= '0' && *cp <= '9')
7441553Srgrimes				i = i * 10 + (*cp++ - '0');
7451553Srgrimes			fino = i;
7461553Srgrimes			continue;
7471553Srgrimes		}
7481553Srgrimes		if (line[0] >= 'a' && line[0] <= 'z') {
7491553Srgrimes			strcpy(last, line);
7501553Srgrimes			while (i = getline(cfp))
7511553Srgrimes				if (strcmp(last, line))
7521553Srgrimes					break;
7531553Srgrimes			switch (sendfile('\3', last+1)) {
7541553Srgrimes			case OK:
7551553Srgrimes				if (i)
7561553Srgrimes					goto again;
7571553Srgrimes				break;
7581553Srgrimes			case REPRINT:
7591553Srgrimes				(void) fclose(cfp);
7601553Srgrimes				return(REPRINT);
7611553Srgrimes			case ACCESS:
7621553Srgrimes				sendmail(logname, ACCESS);
7631553Srgrimes			case ERROR:
7641553Srgrimes				err = ERROR;
7651553Srgrimes			}
7661553Srgrimes			break;
7671553Srgrimes		}
7681553Srgrimes	}
7691553Srgrimes	if (err == OK && sendfile('\2', file) > 0) {
7701553Srgrimes		(void) fclose(cfp);
7711553Srgrimes		return(REPRINT);
7721553Srgrimes	}
7731553Srgrimes	/*
7741553Srgrimes	 * pass 2
7751553Srgrimes	 */
7761553Srgrimes	fseek(cfp, 0L, 0);
7771553Srgrimes	while (getline(cfp))
7781553Srgrimes		if (line[0] == 'U')
7791553Srgrimes			(void) unlink(line+1);
7801553Srgrimes	/*
7811553Srgrimes	 * clean-up in case another control file exists
7821553Srgrimes	 */
7831553Srgrimes	(void) fclose(cfp);
7841553Srgrimes	(void) unlink(file);
7851553Srgrimes	return(err);
7861553Srgrimes}
7871553Srgrimes
7881553Srgrimes/*
7891553Srgrimes * Send a data file to the remote machine and spool it.
7901553Srgrimes * Return positive if we should try resending.
7911553Srgrimes */
7921553Srgrimesstatic int
7931553Srgrimessendfile(type, file)
7941553Srgrimes	int type;
7951553Srgrimes	char *file;
7961553Srgrimes{
7971553Srgrimes	register int f, i, amt;
7981553Srgrimes	struct stat stb;
7991553Srgrimes	char buf[BUFSIZ];
8001553Srgrimes	int sizerr, resp;
8011553Srgrimes
8021553Srgrimes	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
8031553Srgrimes		return(ERROR);
8041553Srgrimes	/*
8051553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
8061553Srgrimes	 * still point to the same file or someone is trying to print something
8071553Srgrimes	 * he shouldn't.
8081553Srgrimes	 */
8091553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
8101553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
8111553Srgrimes		return(ACCESS);
8121553Srgrimes	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
8131553Srgrimes	amt = strlen(buf);
8141553Srgrimes	for (i = 0;  ; i++) {
8151553Srgrimes		if (write(pfd, buf, amt) != amt ||
8161553Srgrimes		    (resp = response()) < 0 || resp == '\1') {
8171553Srgrimes			(void) close(f);
8181553Srgrimes			return(REPRINT);
8191553Srgrimes		} else if (resp == '\0')
8201553Srgrimes			break;
8211553Srgrimes		if (i == 0)
8221553Srgrimes			pstatus("no space on remote; waiting for queue to drain");
8231553Srgrimes		if (i == 10)
8241553Srgrimes			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
8251553Srgrimes				printer, RM);
8261553Srgrimes		sleep(5 * 60);
8271553Srgrimes	}
8281553Srgrimes	if (i)
8291553Srgrimes		pstatus("sending to %s", RM);
8301553Srgrimes	sizerr = 0;
8311553Srgrimes	for (i = 0; i < stb.st_size; i += BUFSIZ) {
8321553Srgrimes		amt = BUFSIZ;
8331553Srgrimes		if (i + amt > stb.st_size)
8341553Srgrimes			amt = stb.st_size - i;
8351553Srgrimes		if (sizerr == 0 && read(f, buf, amt) != amt)
8361553Srgrimes			sizerr = 1;
8371553Srgrimes		if (write(pfd, buf, amt) != amt) {
8381553Srgrimes			(void) close(f);
8391553Srgrimes			return(REPRINT);
8401553Srgrimes		}
8411553Srgrimes	}
8421553Srgrimes
8431553Srgrimes
8441553Srgrimes
8451553Srgrimes
8461553Srgrimes	(void) close(f);
8471553Srgrimes	if (sizerr) {
8481553Srgrimes		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
8491553Srgrimes		/* tell recvjob to ignore this file */
8501553Srgrimes		(void) write(pfd, "\1", 1);
8511553Srgrimes		return(ERROR);
8521553Srgrimes	}
8531553Srgrimes	if (write(pfd, "", 1) != 1 || response())
8541553Srgrimes		return(REPRINT);
8551553Srgrimes	return(OK);
8561553Srgrimes}
8571553Srgrimes
8581553Srgrimes/*
8591553Srgrimes * Check to make sure there have been no errors and that both programs
8601553Srgrimes * are in sync with eachother.
8611553Srgrimes * Return non-zero if the connection was lost.
8621553Srgrimes */
8631553Srgrimesstatic char
8641553Srgrimesresponse()
8651553Srgrimes{
8661553Srgrimes	char resp;
8671553Srgrimes
8681553Srgrimes	if (read(pfd, &resp, 1) != 1) {
8691553Srgrimes		syslog(LOG_INFO, "%s: lost connection", printer);
8701553Srgrimes		return(-1);
8711553Srgrimes	}
8721553Srgrimes	return(resp);
8731553Srgrimes}
8741553Srgrimes
8751553Srgrimes/*
8761553Srgrimes * Banner printing stuff
8771553Srgrimes */
8781553Srgrimesstatic void
8791553Srgrimesbanner(name1, name2)
8801553Srgrimes	char *name1, *name2;
8811553Srgrimes{
8821553Srgrimes	time_t tvec;
8831553Srgrimes	extern char *ctime();
8841553Srgrimes
8851553Srgrimes	time(&tvec);
8861553Srgrimes	if (!SF && !tof)
8871553Srgrimes		(void) write(ofd, FF, strlen(FF));
8881553Srgrimes	if (SB) {	/* short banner only */
8891553Srgrimes		if (class[0]) {
8901553Srgrimes			(void) write(ofd, class, strlen(class));
8911553Srgrimes			(void) write(ofd, ":", 1);
8921553Srgrimes		}
8931553Srgrimes		(void) write(ofd, name1, strlen(name1));
8941553Srgrimes		(void) write(ofd, "  Job: ", 7);
8951553Srgrimes		(void) write(ofd, name2, strlen(name2));
8961553Srgrimes		(void) write(ofd, "  Date: ", 8);
8971553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
8981553Srgrimes		(void) write(ofd, "\n", 1);
8991553Srgrimes	} else {	/* normal banner */
9001553Srgrimes		(void) write(ofd, "\n\n\n", 3);
9011553Srgrimes		scan_out(ofd, name1, '\0');
9021553Srgrimes		(void) write(ofd, "\n\n", 2);
9031553Srgrimes		scan_out(ofd, name2, '\0');
9041553Srgrimes		if (class[0]) {
9051553Srgrimes			(void) write(ofd,"\n\n\n",3);
9061553Srgrimes			scan_out(ofd, class, '\0');
9071553Srgrimes		}
9081553Srgrimes		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
9091553Srgrimes		(void) write(ofd, name2, strlen(name2));
9101553Srgrimes		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
9111553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
9121553Srgrimes		(void) write(ofd, "\n", 1);
9131553Srgrimes	}
9141553Srgrimes	if (!SF)
9151553Srgrimes		(void) write(ofd, FF, strlen(FF));
9161553Srgrimes	tof = 1;
9171553Srgrimes}
9181553Srgrimes
9191553Srgrimesstatic char *
9201553Srgrimesscnline(key, p, c)
9211553Srgrimes	register int key;
9221553Srgrimes	register char *p;
9231553Srgrimes	int c;
9241553Srgrimes{
9251553Srgrimes	register scnwidth;
9261553Srgrimes
9271553Srgrimes	for (scnwidth = WIDTH; --scnwidth;) {
9281553Srgrimes		key <<= 1;
9291553Srgrimes		*p++ = key & 0200 ? c : BACKGND;
9301553Srgrimes	}
9311553Srgrimes	return (p);
9321553Srgrimes}
9331553Srgrimes
9341553Srgrimes#define TRC(q)	(((q)-' ')&0177)
9351553Srgrimes
9361553Srgrimesstatic void
9371553Srgrimesscan_out(scfd, scsp, dlm)
9381553Srgrimes	int scfd, dlm;
9391553Srgrimes	char *scsp;
9401553Srgrimes{
9411553Srgrimes	register char *strp;
9421553Srgrimes	register nchrs, j;
9431553Srgrimes	char outbuf[LINELEN+1], *sp, c, cc;
9441553Srgrimes	int d, scnhgt;
9451553Srgrimes	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
9461553Srgrimes
9471553Srgrimes	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
9481553Srgrimes		strp = &outbuf[0];
9491553Srgrimes		sp = scsp;
9501553Srgrimes		for (nchrs = 0; ; ) {
9511553Srgrimes			d = dropit(c = TRC(cc = *sp++));
9521553Srgrimes			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
9531553Srgrimes				for (j = WIDTH; --j;)
9541553Srgrimes					*strp++ = BACKGND;
9551553Srgrimes			else
9561553Srgrimes				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
9571553Srgrimes			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
9581553Srgrimes				break;
9591553Srgrimes			*strp++ = BACKGND;
9601553Srgrimes			*strp++ = BACKGND;
9611553Srgrimes		}
9621553Srgrimes		while (*--strp == BACKGND && strp >= outbuf)
9631553Srgrimes			;
9641553Srgrimes		strp++;
9651553Srgrimes		*strp++ = '\n';
9661553Srgrimes		(void) write(scfd, outbuf, strp-outbuf);
9671553Srgrimes	}
9681553Srgrimes}
9691553Srgrimes
9701553Srgrimesstatic int
9711553Srgrimesdropit(c)
9721553Srgrimes	int c;
9731553Srgrimes{
9741553Srgrimes	switch(c) {
9751553Srgrimes
9761553Srgrimes	case TRC('_'):
9771553Srgrimes	case TRC(';'):
9781553Srgrimes	case TRC(','):
9791553Srgrimes	case TRC('g'):
9801553Srgrimes	case TRC('j'):
9811553Srgrimes	case TRC('p'):
9821553Srgrimes	case TRC('q'):
9831553Srgrimes	case TRC('y'):
9841553Srgrimes		return (DROP);
9851553Srgrimes
9861553Srgrimes	default:
9871553Srgrimes		return (0);
9881553Srgrimes	}
9891553Srgrimes}
9901553Srgrimes
9911553Srgrimes/*
9921553Srgrimes * sendmail ---
9931553Srgrimes *   tell people about job completion
9941553Srgrimes */
9951553Srgrimesstatic void
9961553Srgrimessendmail(user, bombed)
9971553Srgrimes	char *user;
9981553Srgrimes	int bombed;
9991553Srgrimes{
10001553Srgrimes	register int i;
10011553Srgrimes	int p[2], s;
10021553Srgrimes	register char *cp;
10031553Srgrimes	char buf[100];
10041553Srgrimes	struct stat stb;
10051553Srgrimes	FILE *fp;
10061553Srgrimes
10071553Srgrimes	pipe(p);
10081553Srgrimes	if ((s = dofork(DORETURN)) == 0) {		/* child */
10091553Srgrimes		dup2(p[0], 0);
10101553Srgrimes		for (i = 3; i < NOFILE; i++)
10111553Srgrimes			(void) close(i);
10121553Srgrimes		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
10131553Srgrimes			cp++;
10141553Srgrimes	else
10151553Srgrimes			cp = _PATH_SENDMAIL;
10161553Srgrimes		sprintf(buf, "%s@%s", user, fromhost);
10171553Srgrimes		execl(_PATH_SENDMAIL, cp, buf, 0);
10181553Srgrimes		exit(0);
10191553Srgrimes	} else if (s > 0) {				/* parent */
10201553Srgrimes		dup2(p[1], 1);
10211553Srgrimes		printf("To: %s@%s\n", user, fromhost);
10221553Srgrimes		printf("Subject: printer job\n\n");
10231553Srgrimes		printf("Your printer job ");
10241553Srgrimes		if (*jobname)
10251553Srgrimes			printf("(%s) ", jobname);
10261553Srgrimes		switch (bombed) {
10271553Srgrimes		case OK:
10281553Srgrimes			printf("\ncompleted successfully\n");
10291553Srgrimes			break;
10301553Srgrimes		default:
10311553Srgrimes		case FATALERR:
10321553Srgrimes			printf("\ncould not be printed\n");
10331553Srgrimes			break;
10341553Srgrimes		case NOACCT:
10351553Srgrimes			printf("\ncould not be printed without an account on %s\n", host);
10361553Srgrimes			break;
10371553Srgrimes		case FILTERERR:
10381553Srgrimes			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
10391553Srgrimes			    (fp = fopen(tempfile, "r")) == NULL) {
10401553Srgrimes				printf("\nwas printed but had some errors\n");
10411553Srgrimes				break;
10421553Srgrimes			}
10431553Srgrimes			printf("\nwas printed but had the following errors:\n");
10441553Srgrimes			while ((i = getc(fp)) != EOF)
10451553Srgrimes				putchar(i);
10461553Srgrimes			(void) fclose(fp);
10471553Srgrimes			break;
10481553Srgrimes		case ACCESS:
10491553Srgrimes			printf("\nwas not printed because it was not linked to the original file\n");
10501553Srgrimes		}
10511553Srgrimes		fflush(stdout);
10521553Srgrimes		(void) close(1);
10531553Srgrimes	}
10541553Srgrimes	(void) close(p[0]);
10551553Srgrimes	(void) close(p[1]);
10561553Srgrimes	wait(&s);
10571553Srgrimes}
10581553Srgrimes
10591553Srgrimes/*
10601553Srgrimes * dofork - fork with retries on failure
10611553Srgrimes */
10621553Srgrimesstatic int
10631553Srgrimesdofork(action)
10641553Srgrimes	int action;
10651553Srgrimes{
10661553Srgrimes	register int i, pid;
10671553Srgrimes
10681553Srgrimes	for (i = 0; i < 20; i++) {
10691553Srgrimes		if ((pid = fork()) < 0) {
10701553Srgrimes			sleep((unsigned)(i*i));
10711553Srgrimes			continue;
10721553Srgrimes		}
10731553Srgrimes		/*
10741553Srgrimes		 * Child should run as daemon instead of root
10751553Srgrimes		 */
10761553Srgrimes		if (pid == 0)
10771553Srgrimes			setuid(DU);
10781553Srgrimes		return(pid);
10791553Srgrimes	}
10801553Srgrimes	syslog(LOG_ERR, "can't fork");
10811553Srgrimes
10821553Srgrimes	switch (action) {
10831553Srgrimes	case DORETURN:
10841553Srgrimes		return (-1);
10851553Srgrimes	default:
10861553Srgrimes		syslog(LOG_ERR, "bad action (%d) to dofork", action);
10871553Srgrimes		/*FALL THRU*/
10881553Srgrimes	case DOABORT:
10891553Srgrimes		exit(1);
10901553Srgrimes	}
10911553Srgrimes	/*NOTREACHED*/
10921553Srgrimes}
10931553Srgrimes
10941553Srgrimes/*
10951553Srgrimes * Kill child processes to abort current job.
10961553Srgrimes */
10971553Srgrimesstatic void
10981553Srgrimesabortpr(signo)
10991553Srgrimes	int signo;
11001553Srgrimes{
11011553Srgrimes	(void) unlink(tempfile);
11021553Srgrimes	kill(0, SIGINT);
11031553Srgrimes	if (ofilter > 0)
11041553Srgrimes		kill(ofilter, SIGCONT);
11051553Srgrimes	while (wait(NULL) > 0)
11061553Srgrimes		;
11071553Srgrimes	exit(0);
11081553Srgrimes}
11091553Srgrimes
11101553Srgrimesstatic void
11111553Srgrimesinit()
11121553Srgrimes{
11131553Srgrimes	int status;
11141553Srgrimes	char *s;
11151553Srgrimes
11161553Srgrimes	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
11171553Srgrimes		syslog(LOG_ERR, "can't open printer description file");
11181553Srgrimes		exit(1);
11191553Srgrimes	} else if (status == -1) {
11201553Srgrimes		syslog(LOG_ERR, "unknown printer: %s", printer);
11211553Srgrimes		exit(1);
11221553Srgrimes	} else if (status == -3)
11231553Srgrimes		fatal("potential reference loop detected in printcap file");
11241553Srgrimes
11251553Srgrimes	if (cgetstr(bp, "lp", &LP) == -1)
11261553Srgrimes		LP = _PATH_DEFDEVLP;
11271553Srgrimes	if (cgetstr(bp, "rp", &RP) == -1)
11281553Srgrimes		RP = DEFLP;
11291553Srgrimes	if (cgetstr(bp, "lo", &LO) == -1)
11301553Srgrimes		LO = DEFLOCK;
11311553Srgrimes	if (cgetstr(bp, "st", &ST) == -1)
11321553Srgrimes		ST = DEFSTAT;
11331553Srgrimes	if (cgetstr(bp, "lf", &LF) == -1)
11341553Srgrimes		LF = _PATH_CONSOLE;
11351553Srgrimes	if (cgetstr(bp, "sd", &SD) == -1)
11361553Srgrimes		SD = _PATH_DEFSPOOL;
11371553Srgrimes	if (cgetnum(bp, "du", &DU) < 0)
11381553Srgrimes		DU = DEFUID;
11391553Srgrimes	if (cgetstr(bp,"ff", &FF) == -1)
11401553Srgrimes		FF = DEFFF;
11411553Srgrimes	if (cgetnum(bp, "pw", &PW) < 0)
11421553Srgrimes		PW = DEFWIDTH;
11431553Srgrimes	sprintf(&width[2], "%d", PW);
11441553Srgrimes	if (cgetnum(bp, "pl", &PL) < 0)
11451553Srgrimes		PL = DEFLENGTH;
11461553Srgrimes	sprintf(&length[2], "%d", PL);
11471553Srgrimes	if (cgetnum(bp,"px", &PX) < 0)
11481553Srgrimes		PX = 0;
11491553Srgrimes	sprintf(&pxwidth[2], "%d", PX);
11501553Srgrimes	if (cgetnum(bp, "py", &PY) < 0)
11511553Srgrimes		PY = 0;
11521553Srgrimes	sprintf(&pxlength[2], "%d", PY);
11531553Srgrimes	cgetstr(bp, "rm", &RM);
11541553Srgrimes	if (s = checkremote())
11551553Srgrimes		syslog(LOG_WARNING, s);
11561553Srgrimes
11571553Srgrimes	cgetstr(bp, "af", &AF);
11581553Srgrimes	cgetstr(bp, "of", &OF);
11591553Srgrimes	cgetstr(bp, "if", &IF);
11601553Srgrimes	cgetstr(bp, "rf", &RF);
11611553Srgrimes	cgetstr(bp, "tf", &TF);
11621553Srgrimes	cgetstr(bp, "nf", &NF);
11631553Srgrimes	cgetstr(bp, "df", &DF);
11641553Srgrimes	cgetstr(bp, "gf", &GF);
11651553Srgrimes	cgetstr(bp, "vf", &VF);
11661553Srgrimes	cgetstr(bp, "cf", &CF);
11671553Srgrimes	cgetstr(bp, "tr", &TR);
11681553Srgrimes
11691553Srgrimes	RS = (cgetcap(bp, "rs", ':') != NULL);
11701553Srgrimes	SF = (cgetcap(bp, "sf", ':') != NULL);
11711553Srgrimes	SH = (cgetcap(bp, "sh", ':') != NULL);
11721553Srgrimes	SB = (cgetcap(bp, "sb", ':') != NULL);
11731553Srgrimes	HL = (cgetcap(bp, "hl", ':') != NULL);
11741553Srgrimes	RW = (cgetcap(bp, "rw", ':') != NULL);
11751553Srgrimes
11761553Srgrimes	cgetnum(bp, "br", &BR);
11771553Srgrimes	if (cgetnum(bp, "fc", &FC) < 0)
11781553Srgrimes		FC = 0;
11791553Srgrimes	if (cgetnum(bp, "fs", &FS) < 0)
11801553Srgrimes		FS = 0;
11811553Srgrimes	if (cgetnum(bp, "xc", &XC) < 0)
11821553Srgrimes		XC = 0;
11831553Srgrimes	if (cgetnum(bp, "xs", &XS) < 0)
11841553Srgrimes		XS = 0;
11851553Srgrimes
11861553Srgrimes	tof = (cgetcap(bp, "fo", ':') == NULL);
11871553Srgrimes}
11881553Srgrimes
11891553Srgrimes/*
11901553Srgrimes * Acquire line printer or remote connection.
11911553Srgrimes */
11921553Srgrimesstatic void
11931553Srgrimesopenpr()
11941553Srgrimes{
11951553Srgrimes	register int i, n;
11961553Srgrimes	int resp;
11971553Srgrimes
11981553Srgrimes	if (!sendtorem && *LP) {
11991553Srgrimes		for (i = 1; ; i = i < 32 ? i << 1 : i) {
12001553Srgrimes			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
12011553Srgrimes			if (pfd >= 0)
12021553Srgrimes				break;
12031553Srgrimes			if (errno == ENOENT) {
12041553Srgrimes				syslog(LOG_ERR, "%s: %m", LP);
12051553Srgrimes				exit(1);
12061553Srgrimes			}
12071553Srgrimes			if (i == 1)
12081553Srgrimes				pstatus("waiting for %s to become ready (offline ?)", printer);
12091553Srgrimes			sleep(i);
12101553Srgrimes		}
12111553Srgrimes		if (isatty(pfd))
12121553Srgrimes			setty();
12131553Srgrimes		pstatus("%s is ready and printing", printer);
12141553Srgrimes	} else if (RM != NULL) {
12151553Srgrimes		for (i = 1; ; i = i < 256 ? i << 1 : i) {
12161553Srgrimes			resp = -1;
12171553Srgrimes			pfd = getport(RM);
12181553Srgrimes			if (pfd >= 0) {
12191553Srgrimes				(void) sprintf(line, "\2%s\n", RP);
12201553Srgrimes				n = strlen(line);
12211553Srgrimes				if (write(pfd, line, n) == n &&
12221553Srgrimes				    (resp = response()) == '\0')
12231553Srgrimes					break;
12241553Srgrimes				(void) close(pfd);
12251553Srgrimes			}
12261553Srgrimes			if (i == 1) {
12271553Srgrimes				if (resp < 0)
12281553Srgrimes					pstatus("waiting for %s to come up", RM);
12291553Srgrimes				else {
12301553Srgrimes					pstatus("waiting for queue to be enabled on %s", RM);
12311553Srgrimes					i = 256;
12321553Srgrimes				}
12331553Srgrimes			}
12341553Srgrimes			sleep(i);
12351553Srgrimes		}
12361553Srgrimes		pstatus("sending to %s", RM);
12371553Srgrimes		remote = 1;
12381553Srgrimes	} else {
12391553Srgrimes		syslog(LOG_ERR, "%s: no line printer device or host name",
12401553Srgrimes			printer);
12411553Srgrimes		exit(1);
12421553Srgrimes	}
12431553Srgrimes	/*
12441553Srgrimes	 * Start up an output filter, if needed.
12451553Srgrimes	 */
12461553Srgrimes	if (!remote && OF) {
12471553Srgrimes		int p[2];
12481553Srgrimes		char *cp;
12491553Srgrimes
12501553Srgrimes		pipe(p);
12511553Srgrimes		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
12521553Srgrimes			dup2(p[0], 0);		/* pipe is std in */
12531553Srgrimes			dup2(pfd, 1);		/* printer is std out */
12541553Srgrimes			for (i = 3; i < NOFILE; i++)
12551553Srgrimes				(void) close(i);
12561553Srgrimes			if ((cp = rindex(OF, '/')) == NULL)
12571553Srgrimes				cp = OF;
12581553Srgrimes			else
12591553Srgrimes				cp++;
12601553Srgrimes			execl(OF, cp, width, length, 0);
12611553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
12621553Srgrimes			exit(1);
12631553Srgrimes		}
12641553Srgrimes		(void) close(p[0]);		/* close input side */
12651553Srgrimes		ofd = p[1];			/* use pipe for output */
12661553Srgrimes	} else {
12671553Srgrimes		ofd = pfd;
12681553Srgrimes		ofilter = 0;
12691553Srgrimes	}
12701553Srgrimes}
12711553Srgrimes
12721553Srgrimesstruct bauds {
12731553Srgrimes	int	baud;
12741553Srgrimes	int	speed;
12751553Srgrimes} bauds[] = {
12761553Srgrimes	50,	B50,
12771553Srgrimes	75,	B75,
12781553Srgrimes	110,	B110,
12791553Srgrimes	134,	B134,
12801553Srgrimes	150,	B150,
12811553Srgrimes	200,	B200,
12821553Srgrimes	300,	B300,
12831553Srgrimes	600,	B600,
12841553Srgrimes	1200,	B1200,
12851553Srgrimes	1800,	B1800,
12861553Srgrimes	2400,	B2400,
12871553Srgrimes	4800,	B4800,
12881553Srgrimes	9600,	B9600,
12891553Srgrimes	19200,	EXTA,
12901553Srgrimes	38400,	EXTB,
12911553Srgrimes	0,	0
12921553Srgrimes};
12931553Srgrimes
12941553Srgrimes/*
12951553Srgrimes * setup tty lines.
12961553Srgrimes */
12971553Srgrimesstatic void
12981553Srgrimessetty()
12991553Srgrimes{
13001553Srgrimes	struct sgttyb ttybuf;
13011553Srgrimes	register struct bauds *bp;
13021553Srgrimes
13031553Srgrimes	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
13041553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
13051553Srgrimes		exit(1);
13061553Srgrimes	}
13071553Srgrimes	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
13081553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
13091553Srgrimes		exit(1);
13101553Srgrimes	}
13111553Srgrimes	if (BR > 0) {
13121553Srgrimes		for (bp = bauds; bp->baud; bp++)
13131553Srgrimes			if (BR == bp->baud)
13141553Srgrimes				break;
13151553Srgrimes		if (!bp->baud) {
13161553Srgrimes			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
13171553Srgrimes			exit(1);
13181553Srgrimes		}
13191553Srgrimes		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
13201553Srgrimes	}
13211553Srgrimes	ttybuf.sg_flags &= ~FC;
13221553Srgrimes	ttybuf.sg_flags |= FS;
13231553Srgrimes	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
13241553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
13251553Srgrimes		exit(1);
13261553Srgrimes	}
13271553Srgrimes	if (XC) {
13281553Srgrimes		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
13291553Srgrimes			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
13301553Srgrimes			exit(1);
13311553Srgrimes		}
13321553Srgrimes	}
13331553Srgrimes	if (XS) {
13341553Srgrimes		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
13351553Srgrimes			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
13361553Srgrimes			exit(1);
13371553Srgrimes		}
13381553Srgrimes	}
13391553Srgrimes}
13401553Srgrimes
13411553Srgrimes#if __STDC__
13421553Srgrimes#include <stdarg.h>
13431553Srgrimes#else
13441553Srgrimes#include <varargs.h>
13451553Srgrimes#endif
13461553Srgrimes
13471553Srgrimesvoid
13481553Srgrimes#if __STDC__
13491553Srgrimespstatus(const char *msg, ...)
13501553Srgrimes#else
13511553Srgrimespstatus(msg, va_alist)
13521553Srgrimes	char *msg;
13531553Srgrimes        va_dcl
13541553Srgrimes#endif
13551553Srgrimes{
13561553Srgrimes	register int fd;
13571553Srgrimes	char buf[BUFSIZ];
13581553Srgrimes	va_list ap;
13591553Srgrimes#if __STDC__
13601553Srgrimes	va_start(ap, msg);
13611553Srgrimes#else
13621553Srgrimes	va_start(ap);
13631553Srgrimes#endif
13641553Srgrimes
13651553Srgrimes	umask(0);
13661553Srgrimes	fd = open(ST, O_WRONLY|O_CREAT, 0664);
13671553Srgrimes	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
13681553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
13691553Srgrimes		exit(1);
13701553Srgrimes	}
13711553Srgrimes	ftruncate(fd, 0);
13721553Srgrimes	(void)vsnprintf(buf, sizeof(buf), msg, ap);
13731553Srgrimes	va_end(ap);
13741553Srgrimes	strcat(buf, "\n");
13751553Srgrimes	(void) write(fd, buf, strlen(buf));
13761553Srgrimes	(void) close(fd);
13771553Srgrimes}
1378