printjob.c revision 19202
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
4215648Sjoergstatic char sccsid[] = "@(#)printjob.c	8.7 (Berkeley) 5/10/95";
431553Srgrimes#endif /* not lint */
441553Srgrimes
451553Srgrimes
461553Srgrimes/*
471553Srgrimes * printjob -- print jobs in the queue.
481553Srgrimes *
491553Srgrimes *	NOTE: the lock file is used to pass information to lpq and lprm.
501553Srgrimes *	it does not need to be removed because file locks are dynamic.
511553Srgrimes */
521553Srgrimes
531553Srgrimes#include <sys/param.h>
541553Srgrimes#include <sys/wait.h>
551553Srgrimes#include <sys/stat.h>
561553Srgrimes#include <sys/types.h>
571553Srgrimes
581553Srgrimes#include <pwd.h>
591553Srgrimes#include <unistd.h>
601553Srgrimes#include <signal.h>
611553Srgrimes#include <syslog.h>
621553Srgrimes#include <fcntl.h>
631553Srgrimes#include <dirent.h>
641553Srgrimes#include <errno.h>
651553Srgrimes#include <stdio.h>
661553Srgrimes#include <string.h>
671553Srgrimes#include <stdlib.h>
6815032Ssef#include <sys/ioctl.h>
6915032Ssef#include <termios.h>
7015703Sjoerg#include <time.h>
711553Srgrimes#include "lp.h"
721553Srgrimes#include "lp.local.h"
731553Srgrimes#include "pathnames.h"
741553Srgrimes#include "extern.h"
751553Srgrimes
761553Srgrimes#define DORETURN	0	/* absorb fork error */
771553Srgrimes#define DOABORT		1	/* abort if dofork fails */
781553Srgrimes
791553Srgrimes/*
801553Srgrimes * Error tokens
811553Srgrimes */
821553Srgrimes#define REPRINT		-2
831553Srgrimes#define ERROR		-1
841553Srgrimes#define	OK		0
851553Srgrimes#define	FATALERR	1
861553Srgrimes#define	NOACCT		2
871553Srgrimes#define	FILTERERR	3
881553Srgrimes#define	ACCESS		4
891553Srgrimes
901553Srgrimesstatic dev_t	 fdev;		/* device of file pointed to by symlink */
911553Srgrimesstatic ino_t	 fino;		/* inode of file pointed to by symlink */
921553Srgrimesstatic FILE	*cfp;		/* control file */
931553Srgrimesstatic int	 child;		/* id of any filters */
941553Srgrimesstatic int	 lfd;		/* lock file descriptor */
951553Srgrimesstatic int	 ofd;		/* output filter file descriptor */
961553Srgrimesstatic int	 ofilter;	/* id of output filter, if any */
971553Srgrimesstatic int	 pfd;		/* prstatic inter file descriptor */
981553Srgrimesstatic int	 pid;		/* pid of lpd process */
991553Srgrimesstatic int	 prchild;	/* id of pr process */
1001553Srgrimesstatic char	 title[80];	/* ``pr'' title */
1011553Srgrimesstatic int	 tof;		/* true if at top of form */
1021553Srgrimes
1031553Srgrimesstatic char	class[32];		/* classification field */
1041553Srgrimesstatic char	fromhost[32];		/* user's host machine */
1051553Srgrimes				/* indentation size in static characters */
1068857Srgrimesstatic char	indent[10] = "-i0";
1071553Srgrimesstatic char	jobname[100];		/* job or file name */
1081553Srgrimesstatic char	length[10] = "-l";	/* page length in lines */
1091553Srgrimesstatic char	logname[32];		/* user's login name */
1101553Srgrimesstatic char	pxlength[10] = "-y";	/* page length in pixels */
1111553Srgrimesstatic char	pxwidth[10] = "-x";	/* page width in pixels */
1121553Srgrimesstatic char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
1131553Srgrimesstatic char	width[10] = "-w";	/* page width in static characters */
1141553Srgrimes
1151553Srgrimesstatic void       abortpr __P((int));
1161553Srgrimesstatic void       banner __P((char *, char *));
1171553Srgrimesstatic int        dofork __P((int));
1181553Srgrimesstatic int        dropit __P((int));
1191553Srgrimesstatic void       init __P((void));
1201553Srgrimesstatic void       openpr __P((void));
12115648Sjoergstatic void       opennet __P((char *));
12215648Sjoergstatic void       opentty __P((void));
12315648Sjoergstatic void       openrem __P((void));
1241553Srgrimesstatic int        print __P((int, char *));
1251553Srgrimesstatic int        printit __P((char *));
1261553Srgrimesstatic void       pstatus __P((const char *, ...));
1271553Srgrimesstatic char       response __P((void));
1281553Srgrimesstatic void       scan_out __P((int, char *, int));
1291553Srgrimesstatic char      *scnline __P((int, char *, int));
1301553Srgrimesstatic int        sendfile __P((int, char *));
1311553Srgrimesstatic int        sendit __P((char *));
1321553Srgrimesstatic void       sendmail __P((char *, int));
1331553Srgrimesstatic void       setty __P((void));
1341553Srgrimes
1351553Srgrimesvoid
1361553Srgrimesprintjob()
1371553Srgrimes{
1381553Srgrimes	struct stat stb;
1391553Srgrimes	register struct queue *q, **qp;
1401553Srgrimes	struct queue **queue;
1411553Srgrimes	register int i, nitems;
14215648Sjoerg	off_t pidoff;
14315648Sjoerg	int errcnt, count = 0;
1441553Srgrimes
1451553Srgrimes	init();					/* set up capabilities */
1461553Srgrimes	(void) write(1, "", 1);			/* ack that daemon is started */
1471553Srgrimes	(void) close(2);			/* set up log file */
1481553Srgrimes	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
1491553Srgrimes		syslog(LOG_ERR, "%s: %m", LF);
1501553Srgrimes		(void) open(_PATH_DEVNULL, O_WRONLY);
1511553Srgrimes	}
1521553Srgrimes	setgid(getegid());
1531553Srgrimes	pid = getpid();				/* for use with lprm */
1541553Srgrimes	setpgrp(0, pid);
1551553Srgrimes	signal(SIGHUP, abortpr);
1561553Srgrimes	signal(SIGINT, abortpr);
1571553Srgrimes	signal(SIGQUIT, abortpr);
1581553Srgrimes	signal(SIGTERM, abortpr);
1591553Srgrimes
1601553Srgrimes	(void) mktemp(tempfile);
1611553Srgrimes
1621553Srgrimes	/*
1631553Srgrimes	 * uses short form file names
1641553Srgrimes	 */
1651553Srgrimes	if (chdir(SD) < 0) {
1661553Srgrimes		syslog(LOG_ERR, "%s: %m", SD);
1671553Srgrimes		exit(1);
1681553Srgrimes	}
1691553Srgrimes	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
1701553Srgrimes		exit(0);		/* printing disabled */
1711553Srgrimes	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
1721553Srgrimes	if (lfd < 0) {
1731553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1741553Srgrimes		exit(1);
1751553Srgrimes	}
1761553Srgrimes	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
1771553Srgrimes		if (errno == EWOULDBLOCK)	/* active deamon present */
1781553Srgrimes			exit(0);
1791553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1801553Srgrimes		exit(1);
1811553Srgrimes	}
1821553Srgrimes	ftruncate(lfd, 0);
1831553Srgrimes	/*
1841553Srgrimes	 * write process id for others to know
1851553Srgrimes	 */
1861553Srgrimes	sprintf(line, "%u\n", pid);
1871553Srgrimes	pidoff = i = strlen(line);
1881553Srgrimes	if (write(lfd, line, i) != i) {
1891553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1901553Srgrimes		exit(1);
1911553Srgrimes	}
1921553Srgrimes	/*
1931553Srgrimes	 * search the spool directory for work and sort by queue order.
1941553Srgrimes	 */
1951553Srgrimes	if ((nitems = getq(&queue)) < 0) {
1961553Srgrimes		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
1971553Srgrimes		exit(1);
1981553Srgrimes	}
1991553Srgrimes	if (nitems == 0)		/* no work to do */
2001553Srgrimes		exit(0);
2011553Srgrimes	if (stb.st_mode & 01) {		/* reset queue flag */
2021553Srgrimes		if (fchmod(lfd, stb.st_mode & 0776) < 0)
2031553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
2041553Srgrimes	}
2051553Srgrimes	openpr();			/* open printer or remote */
2061553Srgrimesagain:
2071553Srgrimes	/*
2081553Srgrimes	 * we found something to do now do it --
2091553Srgrimes	 *    write the name of the current control file into the lock file
2101553Srgrimes	 *    so the spool queue program can tell what we're working on
2111553Srgrimes	 */
2121553Srgrimes	for (qp = queue; nitems--; free((char *) q)) {
2131553Srgrimes		q = *qp++;
2141553Srgrimes		if (stat(q->q_name, &stb) < 0)
2151553Srgrimes			continue;
21615648Sjoerg		errcnt = 0;
2171553Srgrimes	restart:
21815648Sjoerg		(void) lseek(lfd, pidoff, 0);
2191553Srgrimes		(void) sprintf(line, "%s\n", q->q_name);
2201553Srgrimes		i = strlen(line);
2211553Srgrimes		if (write(lfd, line, i) != i)
2221553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
2231553Srgrimes		if (!remote)
2241553Srgrimes			i = printit(q->q_name);
2251553Srgrimes		else
2261553Srgrimes			i = sendit(q->q_name);
2271553Srgrimes		/*
2281553Srgrimes		 * Check to see if we are supposed to stop printing or
2291553Srgrimes		 * if we are to rebuild the queue.
2301553Srgrimes		 */
2311553Srgrimes		if (fstat(lfd, &stb) == 0) {
2321553Srgrimes			/* stop printing before starting next job? */
2331553Srgrimes			if (stb.st_mode & 0100)
2341553Srgrimes				goto done;
2351553Srgrimes			/* rebuild queue (after lpc topq) */
2361553Srgrimes			if (stb.st_mode & 01) {
2371553Srgrimes				for (free((char *) q); nitems--; free((char *) q))
2381553Srgrimes					q = *qp++;
2391553Srgrimes				if (fchmod(lfd, stb.st_mode & 0776) < 0)
2401553Srgrimes					syslog(LOG_WARNING, "%s: %s: %m",
2411553Srgrimes						printer, LO);
2421553Srgrimes				break;
2431553Srgrimes			}
2441553Srgrimes		}
2451553Srgrimes		if (i == OK)		/* file ok and printed */
2461553Srgrimes			count++;
24715648Sjoerg		else if (i == REPRINT && ++errcnt < 5) {
24815648Sjoerg			/* try reprinting the job */
2491553Srgrimes			syslog(LOG_INFO, "restarting %s", printer);
2501553Srgrimes			if (ofilter > 0) {
2511553Srgrimes				kill(ofilter, SIGCONT);	/* to be sure */
2521553Srgrimes				(void) close(ofd);
25315648Sjoerg				while ((i = wait(NULL)) > 0 && i != ofilter)
2541553Srgrimes					;
2551553Srgrimes				ofilter = 0;
2561553Srgrimes			}
2571553Srgrimes			(void) close(pfd);	/* close printer */
2581553Srgrimes			if (ftruncate(lfd, pidoff) < 0)
2591553Srgrimes				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
2601553Srgrimes			openpr();		/* try to reopen printer */
2611553Srgrimes			goto restart;
26215648Sjoerg		} else {
26315648Sjoerg			syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
26415648Sjoerg				remote ? "sent to remote host" : "printed", q->q_name);
26515648Sjoerg			if (i == REPRINT) {
26615648Sjoerg				/* insure we don't attempt this job again */
26715648Sjoerg				(void) unlink(q->q_name);
26815648Sjoerg				q->q_name[0] = 'd';
26915648Sjoerg				(void) unlink(q->q_name);
27015648Sjoerg				if (logname[0])
27115648Sjoerg					sendmail(logname, FATALERR);
27215648Sjoerg			}
2731553Srgrimes		}
2741553Srgrimes	}
2751553Srgrimes	free((char *) queue);
2761553Srgrimes	/*
2771553Srgrimes	 * search the spool directory for more work.
2781553Srgrimes	 */
2791553Srgrimes	if ((nitems = getq(&queue)) < 0) {
2801553Srgrimes		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
2811553Srgrimes		exit(1);
2821553Srgrimes	}
2831553Srgrimes	if (nitems == 0) {		/* no more work to do */
2841553Srgrimes	done:
2851553Srgrimes		if (count > 0) {	/* Files actually printed */
2861553Srgrimes			if (!SF && !tof)
2871553Srgrimes				(void) write(ofd, FF, strlen(FF));
2881553Srgrimes			if (TR != NULL)		/* output trailer */
2891553Srgrimes				(void) write(ofd, TR, strlen(TR));
2901553Srgrimes		}
29119202Simp		(void) close(ofd);
29219202Simp		(void) wait(NULL);
2931553Srgrimes		(void) unlink(tempfile);
2941553Srgrimes		exit(0);
2951553Srgrimes	}
2961553Srgrimes	goto again;
2971553Srgrimes}
2981553Srgrimes
2991553Srgrimeschar	fonts[4][50];	/* fonts for troff */
3001553Srgrimes
3011553Srgrimeschar ifonts[4][40] = {
3021553Srgrimes	_PATH_VFONTR,
3031553Srgrimes	_PATH_VFONTI,
3041553Srgrimes	_PATH_VFONTB,
3051553Srgrimes	_PATH_VFONTS,
3061553Srgrimes};
3071553Srgrimes
3081553Srgrimes/*
3091553Srgrimes * The remaining part is the reading of the control file (cf)
3101553Srgrimes * and performing the various actions.
3111553Srgrimes */
3121553Srgrimesstatic int
3131553Srgrimesprintit(file)
3141553Srgrimes	char *file;
3151553Srgrimes{
3161553Srgrimes	register int i;
3171553Srgrimes	char *cp;
3181553Srgrimes	int bombed = OK;
3191553Srgrimes
3201553Srgrimes	/*
3211553Srgrimes	 * open control file; ignore if no longer there.
3221553Srgrimes	 */
3231553Srgrimes	if ((cfp = fopen(file, "r")) == NULL) {
3241553Srgrimes		syslog(LOG_INFO, "%s: %s: %m", printer, file);
3251553Srgrimes		return(OK);
3261553Srgrimes	}
3271553Srgrimes	/*
3281553Srgrimes	 * Reset troff fonts.
3291553Srgrimes	 */
3301553Srgrimes	for (i = 0; i < 4; i++)
3311553Srgrimes		strcpy(fonts[i], ifonts[i]);
3321553Srgrimes	sprintf(&width[2], "%d", PW);
3331553Srgrimes	strcpy(indent+2, "0");
3341553Srgrimes
3351553Srgrimes	/*
3361553Srgrimes	 *      read the control file for work to do
3371553Srgrimes	 *
3381553Srgrimes	 *      file format -- first character in the line is a command
3391553Srgrimes	 *      rest of the line is the argument.
3401553Srgrimes	 *      valid commands are:
3411553Srgrimes	 *
3421553Srgrimes	 *		S -- "stat info" for symbolic link protection
3431553Srgrimes	 *		J -- "job name" on banner page
3441553Srgrimes	 *		C -- "class name" on banner page
3451553Srgrimes	 *              L -- "literal" user's name to print on banner
3461553Srgrimes	 *		T -- "title" for pr
3471553Srgrimes	 *		H -- "host name" of machine where lpr was done
3481553Srgrimes	 *              P -- "person" user's login name
3491553Srgrimes	 *              I -- "indent" amount to indent output
35015648Sjoerg	 *		R -- laser dpi "resolution"
3511553Srgrimes	 *              f -- "file name" name of text file to print
3521553Srgrimes	 *		l -- "file name" text file with control chars
3531553Srgrimes	 *		p -- "file name" text file to print with pr(1)
3541553Srgrimes	 *		t -- "file name" troff(1) file to print
3551553Srgrimes	 *		n -- "file name" ditroff(1) file to print
3561553Srgrimes	 *		d -- "file name" dvi file to print
3571553Srgrimes	 *		g -- "file name" plot(1G) file to print
3581553Srgrimes	 *		v -- "file name" plain raster file to print
3591553Srgrimes	 *		c -- "file name" cifplot file to print
3601553Srgrimes	 *		1 -- "R font file" for troff
3611553Srgrimes	 *		2 -- "I font file" for troff
3621553Srgrimes	 *		3 -- "B font file" for troff
3631553Srgrimes	 *		4 -- "S font file" for troff
3641553Srgrimes	 *		N -- "name" of file (used by lpq)
3651553Srgrimes	 *              U -- "unlink" name of file to remove
3661553Srgrimes	 *                    (after we print it. (Pass 2 only)).
3671553Srgrimes	 *		M -- "mail" to user when done printing
3681553Srgrimes	 *
3691553Srgrimes	 *      getline reads a line and expands tabs to blanks
3701553Srgrimes	 */
3711553Srgrimes
3721553Srgrimes	/* pass 1 */
3731553Srgrimes
3741553Srgrimes	while (getline(cfp))
3751553Srgrimes		switch (line[0]) {
3761553Srgrimes		case 'H':
3771553Srgrimes			strcpy(fromhost, line+1);
3781553Srgrimes			if (class[0] == '\0')
3791553Srgrimes				strncpy(class, line+1, sizeof(class)-1);
3801553Srgrimes			continue;
3811553Srgrimes
3821553Srgrimes		case 'P':
3831553Srgrimes			strncpy(logname, line+1, sizeof(logname)-1);
3841553Srgrimes			if (RS) {			/* restricted */
3851553Srgrimes				if (getpwnam(logname) == NULL) {
3861553Srgrimes					bombed = NOACCT;
3871553Srgrimes					sendmail(line+1, bombed);
3881553Srgrimes					goto pass2;
3891553Srgrimes				}
3901553Srgrimes			}
3911553Srgrimes			continue;
3921553Srgrimes
3931553Srgrimes		case 'S':
3941553Srgrimes			cp = line+1;
3951553Srgrimes			i = 0;
3961553Srgrimes			while (*cp >= '0' && *cp <= '9')
3971553Srgrimes				i = i * 10 + (*cp++ - '0');
3981553Srgrimes			fdev = i;
3991553Srgrimes			cp++;
4001553Srgrimes			i = 0;
4011553Srgrimes			while (*cp >= '0' && *cp <= '9')
4021553Srgrimes				i = i * 10 + (*cp++ - '0');
4031553Srgrimes			fino = i;
4041553Srgrimes			continue;
4051553Srgrimes
4061553Srgrimes		case 'J':
4071553Srgrimes			if (line[1] != '\0')
4081553Srgrimes				strncpy(jobname, line+1, sizeof(jobname)-1);
4091553Srgrimes			else
4101553Srgrimes				strcpy(jobname, " ");
4111553Srgrimes			continue;
4121553Srgrimes
4131553Srgrimes		case 'C':
4141553Srgrimes			if (line[1] != '\0')
4151553Srgrimes				strncpy(class, line+1, sizeof(class)-1);
4161553Srgrimes			else if (class[0] == '\0')
4171553Srgrimes				gethostname(class, sizeof(class));
4181553Srgrimes			continue;
4191553Srgrimes
4201553Srgrimes		case 'T':	/* header title for pr */
4211553Srgrimes			strncpy(title, line+1, sizeof(title)-1);
4221553Srgrimes			continue;
4231553Srgrimes
4241553Srgrimes		case 'L':	/* identification line */
4251553Srgrimes			if (!SH && !HL)
4261553Srgrimes				banner(line+1, jobname);
4271553Srgrimes			continue;
4281553Srgrimes
4291553Srgrimes		case '1':	/* troff fonts */
4301553Srgrimes		case '2':
4311553Srgrimes		case '3':
4321553Srgrimes		case '4':
4331553Srgrimes			if (line[1] != '\0')
4341553Srgrimes				strcpy(fonts[line[0]-'1'], line+1);
4351553Srgrimes			continue;
4361553Srgrimes
4371553Srgrimes		case 'W':	/* page width */
4381553Srgrimes			strncpy(width+2, line+1, sizeof(width)-3);
4391553Srgrimes			continue;
4401553Srgrimes
4411553Srgrimes		case 'I':	/* indent amount */
4421553Srgrimes			strncpy(indent+2, line+1, sizeof(indent)-3);
4431553Srgrimes			continue;
4441553Srgrimes
4451553Srgrimes		default:	/* some file to print */
4461553Srgrimes			switch (i = print(line[0], line+1)) {
4471553Srgrimes			case ERROR:
4481553Srgrimes				if (bombed == OK)
4491553Srgrimes					bombed = FATALERR;
4501553Srgrimes				break;
4511553Srgrimes			case REPRINT:
4521553Srgrimes				(void) fclose(cfp);
4531553Srgrimes				return(REPRINT);
4541553Srgrimes			case FILTERERR:
4551553Srgrimes			case ACCESS:
4561553Srgrimes				bombed = i;
4571553Srgrimes				sendmail(logname, bombed);
4581553Srgrimes			}
4591553Srgrimes			title[0] = '\0';
4601553Srgrimes			continue;
4611553Srgrimes
4621553Srgrimes		case 'N':
4631553Srgrimes		case 'U':
4641553Srgrimes		case 'M':
46515648Sjoerg		case 'R':
4661553Srgrimes			continue;
4671553Srgrimes		}
4681553Srgrimes
4691553Srgrimes	/* pass 2 */
4701553Srgrimes
4711553Srgrimespass2:
4721553Srgrimes	fseek(cfp, 0L, 0);
4731553Srgrimes	while (getline(cfp))
4741553Srgrimes		switch (line[0]) {
4751553Srgrimes		case 'L':	/* identification line */
4761553Srgrimes			if (!SH && HL)
4771553Srgrimes				banner(line+1, jobname);
4781553Srgrimes			continue;
4791553Srgrimes
4801553Srgrimes		case 'M':
4811553Srgrimes			if (bombed < NOACCT)	/* already sent if >= NOACCT */
4821553Srgrimes				sendmail(line+1, bombed);
4831553Srgrimes			continue;
4841553Srgrimes
4851553Srgrimes		case 'U':
4861553Srgrimes			(void) unlink(line+1);
4871553Srgrimes		}
4881553Srgrimes	/*
4891553Srgrimes	 * clean-up in case another control file exists
4901553Srgrimes	 */
4911553Srgrimes	(void) fclose(cfp);
4921553Srgrimes	(void) unlink(file);
4931553Srgrimes	return(bombed == OK ? OK : ERROR);
4941553Srgrimes}
4951553Srgrimes
4961553Srgrimes/*
4971553Srgrimes * Print a file.
4981553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
4991553Srgrimes * Return -1 if a non-recoverable error occured,
5001553Srgrimes * 2 if the filter detected some errors (but printed the job anyway),
5011553Srgrimes * 1 if we should try to reprint this job and
5021553Srgrimes * 0 if all is well.
5031553Srgrimes * Note: all filters take stdin as the file, stdout as the printer,
5041553Srgrimes * stderr as the log file, and must not ignore SIGINT.
5051553Srgrimes */
5061553Srgrimesstatic int
5071553Srgrimesprint(format, file)
5081553Srgrimes	int format;
5091553Srgrimes	char *file;
5101553Srgrimes{
5111553Srgrimes	register int n;
5121553Srgrimes	register char *prog;
51318569Sbde	int dtablesize, fi, fo;
5141553Srgrimes	FILE *fp;
5151553Srgrimes	char *av[15], buf[BUFSIZ];
5161553Srgrimes	int pid, p[2], stopped = 0;
5171553Srgrimes	union wait status;
5181553Srgrimes	struct stat stb;
5191553Srgrimes
5201553Srgrimes	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
5211553Srgrimes		return(ERROR);
5221553Srgrimes	/*
5231553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
5241553Srgrimes	 * still point to the same file or someone is trying to print
5251553Srgrimes	 * something he shouldn't.
5261553Srgrimes	 */
5271553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
5281553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
5291553Srgrimes		return(ACCESS);
5301553Srgrimes	if (!SF && !tof) {		/* start on a fresh page */
5311553Srgrimes		(void) write(ofd, FF, strlen(FF));
5321553Srgrimes		tof = 1;
5331553Srgrimes	}
5341553Srgrimes	if (IF == NULL && (format == 'f' || format == 'l')) {
5351553Srgrimes		tof = 0;
5361553Srgrimes		while ((n = read(fi, buf, BUFSIZ)) > 0)
5371553Srgrimes			if (write(ofd, buf, n) != n) {
5381553Srgrimes				(void) close(fi);
5391553Srgrimes				return(REPRINT);
5401553Srgrimes			}
5411553Srgrimes		(void) close(fi);
5421553Srgrimes		return(OK);
5431553Srgrimes	}
5441553Srgrimes	switch (format) {
5451553Srgrimes	case 'p':	/* print file using 'pr' */
5461553Srgrimes		if (IF == NULL) {	/* use output filter */
5471553Srgrimes			prog = _PATH_PR;
5481553Srgrimes			av[0] = "pr";
5491553Srgrimes			av[1] = width;
5501553Srgrimes			av[2] = length;
5511553Srgrimes			av[3] = "-h";
5521553Srgrimes			av[4] = *title ? title : " ";
5535445Sjoerg			av[5] = "-F";
5545445Sjoerg			av[6] = 0;
5551553Srgrimes			fo = ofd;
5561553Srgrimes			goto start;
5571553Srgrimes		}
5581553Srgrimes		pipe(p);
5591553Srgrimes		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
5601553Srgrimes			dup2(fi, 0);		/* file is stdin */
5611553Srgrimes			dup2(p[1], 1);		/* pipe is stdout */
5628094Sjkh			closelog();
56318569Sbde			for (n = 3, dtablesize = getdtablesize();
56418569Sbde			     n < dtablesize; n++)
5651553Srgrimes				(void) close(n);
5661553Srgrimes			execl(_PATH_PR, "pr", width, length,
5675445Sjoerg			    "-h", *title ? title : " ", "-F", 0);
5681553Srgrimes			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
5691553Srgrimes			exit(2);
5701553Srgrimes		}
5711553Srgrimes		(void) close(p[1]);		/* close output side */
5721553Srgrimes		(void) close(fi);
5731553Srgrimes		if (prchild < 0) {
5741553Srgrimes			prchild = 0;
5751553Srgrimes			(void) close(p[0]);
5761553Srgrimes			return(ERROR);
5771553Srgrimes		}
5781553Srgrimes		fi = p[0];			/* use pipe for input */
5791553Srgrimes	case 'f':	/* print plain text file */
5801553Srgrimes		prog = IF;
5811553Srgrimes		av[1] = width;
5821553Srgrimes		av[2] = length;
5831553Srgrimes		av[3] = indent;
5841553Srgrimes		n = 4;
5851553Srgrimes		break;
5861553Srgrimes	case 'l':	/* like 'f' but pass control characters */
5871553Srgrimes		prog = IF;
5881553Srgrimes		av[1] = "-c";
5891553Srgrimes		av[2] = width;
5901553Srgrimes		av[3] = length;
5911553Srgrimes		av[4] = indent;
5921553Srgrimes		n = 5;
5931553Srgrimes		break;
5941553Srgrimes	case 'r':	/* print a fortran text file */
5951553Srgrimes		prog = RF;
5961553Srgrimes		av[1] = width;
5971553Srgrimes		av[2] = length;
5981553Srgrimes		n = 3;
5991553Srgrimes		break;
6001553Srgrimes	case 't':	/* print troff output */
6011553Srgrimes	case 'n':	/* print ditroff output */
6021553Srgrimes	case 'd':	/* print tex output */
6031553Srgrimes		(void) unlink(".railmag");
6041553Srgrimes		if ((fo = creat(".railmag", FILMOD)) < 0) {
6051553Srgrimes			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
6061553Srgrimes			(void) unlink(".railmag");
6071553Srgrimes		} else {
6081553Srgrimes			for (n = 0; n < 4; n++) {
6091553Srgrimes				if (fonts[n][0] != '/')
6101553Srgrimes					(void) write(fo, _PATH_VFONT,
6111553Srgrimes					    sizeof(_PATH_VFONT) - 1);
6121553Srgrimes				(void) write(fo, fonts[n], strlen(fonts[n]));
6131553Srgrimes				(void) write(fo, "\n", 1);
6141553Srgrimes			}
6151553Srgrimes			(void) close(fo);
6161553Srgrimes		}
6171553Srgrimes		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
6181553Srgrimes		av[1] = pxwidth;
6191553Srgrimes		av[2] = pxlength;
6201553Srgrimes		n = 3;
6211553Srgrimes		break;
6221553Srgrimes	case 'c':	/* print cifplot output */
6231553Srgrimes		prog = CF;
6241553Srgrimes		av[1] = pxwidth;
6251553Srgrimes		av[2] = pxlength;
6261553Srgrimes		n = 3;
6271553Srgrimes		break;
6281553Srgrimes	case 'g':	/* print plot(1G) output */
6291553Srgrimes		prog = GF;
6301553Srgrimes		av[1] = pxwidth;
6311553Srgrimes		av[2] = pxlength;
6321553Srgrimes		n = 3;
6331553Srgrimes		break;
6341553Srgrimes	case 'v':	/* print raster output */
6351553Srgrimes		prog = VF;
6361553Srgrimes		av[1] = pxwidth;
6371553Srgrimes		av[2] = pxlength;
6381553Srgrimes		n = 3;
6391553Srgrimes		break;
6401553Srgrimes	default:
6411553Srgrimes		(void) close(fi);
6421553Srgrimes		syslog(LOG_ERR, "%s: illegal format character '%c'",
6431553Srgrimes			printer, format);
6441553Srgrimes		return(ERROR);
6451553Srgrimes	}
64615648Sjoerg	if (prog == NULL) {
64715648Sjoerg		(void) close(fi);
64815648Sjoerg		syslog(LOG_ERR,
64915648Sjoerg		   "%s: no filter found in printcap for format character '%c'",
65015648Sjoerg		   printer, format);
65115648Sjoerg		return(ERROR);
65215648Sjoerg	}
6531553Srgrimes	if ((av[0] = rindex(prog, '/')) != NULL)
6541553Srgrimes		av[0]++;
6551553Srgrimes	else
6561553Srgrimes		av[0] = prog;
6571553Srgrimes	av[n++] = "-n";
6581553Srgrimes	av[n++] = logname;
6591553Srgrimes	av[n++] = "-h";
6601553Srgrimes	av[n++] = fromhost;
6611553Srgrimes	av[n++] = AF;
6621553Srgrimes	av[n] = 0;
6631553Srgrimes	fo = pfd;
6641553Srgrimes	if (ofilter > 0) {		/* stop output filter */
6651553Srgrimes		write(ofd, "\031\1", 2);
6661553Srgrimes		while ((pid =
6671553Srgrimes		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
6681553Srgrimes			;
6691553Srgrimes		if (status.w_stopval != WSTOPPED) {
6701553Srgrimes			(void) close(fi);
67115648Sjoerg			syslog(LOG_WARNING,
67215648Sjoerg				"%s: output filter died (retcode=%d termsig=%d)",
67315648Sjoerg				printer, status.w_retcode, status.w_termsig);
6741553Srgrimes			return(REPRINT);
6751553Srgrimes		}
6761553Srgrimes		stopped++;
6771553Srgrimes	}
6781553Srgrimesstart:
6791553Srgrimes	if ((child = dofork(DORETURN)) == 0) {	/* child */
6801553Srgrimes		dup2(fi, 0);
6811553Srgrimes		dup2(fo, 1);
6821553Srgrimes		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
6831553Srgrimes		if (n >= 0)
6841553Srgrimes			dup2(n, 2);
6858094Sjkh		closelog();
68618569Sbde		for (n = 3, dtablesize = getdtablesize(); n < dtablesize; n++)
6871553Srgrimes			(void) close(n);
6881553Srgrimes		execv(prog, av);
6891553Srgrimes		syslog(LOG_ERR, "cannot execv %s", prog);
6901553Srgrimes		exit(2);
6911553Srgrimes	}
6921553Srgrimes	(void) close(fi);
6931553Srgrimes	if (child < 0)
6941553Srgrimes		status.w_retcode = 100;
6951553Srgrimes	else
6961553Srgrimes		while ((pid = wait((int *)&status)) > 0 && pid != child)
6971553Srgrimes			;
6981553Srgrimes	child = 0;
6991553Srgrimes	prchild = 0;
7001553Srgrimes	if (stopped) {		/* restart output filter */
7011553Srgrimes		if (kill(ofilter, SIGCONT) < 0) {
7021553Srgrimes			syslog(LOG_ERR, "cannot restart output filter");
7031553Srgrimes			exit(1);
7041553Srgrimes		}
7051553Srgrimes	}
7061553Srgrimes	tof = 0;
7071553Srgrimes
7081553Srgrimes	/* Copy filter output to "lf" logfile */
7091553Srgrimes	if (fp = fopen(tempfile, "r")) {
7101553Srgrimes		while (fgets(buf, sizeof(buf), fp))
7111553Srgrimes			fputs(buf, stderr);
7121553Srgrimes		fclose(fp);
7131553Srgrimes	}
7141553Srgrimes
7151553Srgrimes	if (!WIFEXITED(status)) {
71615648Sjoerg		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
7171553Srgrimes			printer, format, status.w_termsig);
7181553Srgrimes		return(ERROR);
7191553Srgrimes	}
7201553Srgrimes	switch (status.w_retcode) {
7211553Srgrimes	case 0:
7221553Srgrimes		tof = 1;
7231553Srgrimes		return(OK);
7241553Srgrimes	case 1:
7251553Srgrimes		return(REPRINT);
72615648Sjoerg	case 2:
72715648Sjoerg		return(ERROR);
7281553Srgrimes	default:
72915648Sjoerg		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
7301553Srgrimes			printer, format, status.w_retcode);
73115648Sjoerg		return(FILTERERR);
7321553Srgrimes	}
7331553Srgrimes}
7341553Srgrimes
7351553Srgrimes/*
7361553Srgrimes * Send the daemon control file (cf) and any data files.
7371553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
7381553Srgrimes * 0 if all is well.
7391553Srgrimes */
7401553Srgrimesstatic int
7411553Srgrimessendit(file)
7421553Srgrimes	char *file;
7431553Srgrimes{
7441553Srgrimes	register int i, err = OK;
7451553Srgrimes	char *cp, last[BUFSIZ];
7461553Srgrimes
7471553Srgrimes	/*
7481553Srgrimes	 * open control file
7491553Srgrimes	 */
7501553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
7511553Srgrimes		return(OK);
7521553Srgrimes	/*
7531553Srgrimes	 *      read the control file for work to do
7541553Srgrimes	 *
7551553Srgrimes	 *      file format -- first character in the line is a command
7561553Srgrimes	 *      rest of the line is the argument.
7571553Srgrimes	 *      commands of interest are:
7581553Srgrimes	 *
7591553Srgrimes	 *            a-z -- "file name" name of file to print
7601553Srgrimes	 *              U -- "unlink" name of file to remove
7611553Srgrimes	 *                    (after we print it. (Pass 2 only)).
7621553Srgrimes	 */
7631553Srgrimes
7641553Srgrimes	/*
7651553Srgrimes	 * pass 1
7661553Srgrimes	 */
7671553Srgrimes	while (getline(cfp)) {
7681553Srgrimes	again:
7691553Srgrimes		if (line[0] == 'S') {
7701553Srgrimes			cp = line+1;
7711553Srgrimes			i = 0;
7721553Srgrimes			while (*cp >= '0' && *cp <= '9')
7731553Srgrimes				i = i * 10 + (*cp++ - '0');
7741553Srgrimes			fdev = i;
7751553Srgrimes			cp++;
7761553Srgrimes			i = 0;
7771553Srgrimes			while (*cp >= '0' && *cp <= '9')
7781553Srgrimes				i = i * 10 + (*cp++ - '0');
7791553Srgrimes			fino = i;
7801553Srgrimes			continue;
7811553Srgrimes		}
7821553Srgrimes		if (line[0] >= 'a' && line[0] <= 'z') {
7831553Srgrimes			strcpy(last, line);
7841553Srgrimes			while (i = getline(cfp))
7851553Srgrimes				if (strcmp(last, line))
7861553Srgrimes					break;
7871553Srgrimes			switch (sendfile('\3', last+1)) {
7881553Srgrimes			case OK:
7891553Srgrimes				if (i)
7901553Srgrimes					goto again;
7911553Srgrimes				break;
7921553Srgrimes			case REPRINT:
7931553Srgrimes				(void) fclose(cfp);
7941553Srgrimes				return(REPRINT);
7951553Srgrimes			case ACCESS:
7961553Srgrimes				sendmail(logname, ACCESS);
7971553Srgrimes			case ERROR:
7981553Srgrimes				err = ERROR;
7991553Srgrimes			}
8001553Srgrimes			break;
8011553Srgrimes		}
8021553Srgrimes	}
8031553Srgrimes	if (err == OK && sendfile('\2', file) > 0) {
8041553Srgrimes		(void) fclose(cfp);
8051553Srgrimes		return(REPRINT);
8061553Srgrimes	}
8071553Srgrimes	/*
8081553Srgrimes	 * pass 2
8091553Srgrimes	 */
8101553Srgrimes	fseek(cfp, 0L, 0);
8111553Srgrimes	while (getline(cfp))
8121553Srgrimes		if (line[0] == 'U')
8131553Srgrimes			(void) unlink(line+1);
8141553Srgrimes	/*
8151553Srgrimes	 * clean-up in case another control file exists
8161553Srgrimes	 */
8171553Srgrimes	(void) fclose(cfp);
8181553Srgrimes	(void) unlink(file);
8191553Srgrimes	return(err);
8201553Srgrimes}
8211553Srgrimes
8221553Srgrimes/*
8231553Srgrimes * Send a data file to the remote machine and spool it.
8241553Srgrimes * Return positive if we should try resending.
8251553Srgrimes */
8261553Srgrimesstatic int
8271553Srgrimessendfile(type, file)
8281553Srgrimes	int type;
8291553Srgrimes	char *file;
8301553Srgrimes{
8311553Srgrimes	register int f, i, amt;
8321553Srgrimes	struct stat stb;
8331553Srgrimes	char buf[BUFSIZ];
8341553Srgrimes	int sizerr, resp;
8351553Srgrimes
8361553Srgrimes	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
8371553Srgrimes		return(ERROR);
8381553Srgrimes	/*
8391553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
8401553Srgrimes	 * still point to the same file or someone is trying to print something
8411553Srgrimes	 * he shouldn't.
8421553Srgrimes	 */
8431553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
8441553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
8451553Srgrimes		return(ACCESS);
8461553Srgrimes	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
8471553Srgrimes	amt = strlen(buf);
8481553Srgrimes	for (i = 0;  ; i++) {
8491553Srgrimes		if (write(pfd, buf, amt) != amt ||
8501553Srgrimes		    (resp = response()) < 0 || resp == '\1') {
8511553Srgrimes			(void) close(f);
8521553Srgrimes			return(REPRINT);
8531553Srgrimes		} else if (resp == '\0')
8541553Srgrimes			break;
8551553Srgrimes		if (i == 0)
8561553Srgrimes			pstatus("no space on remote; waiting for queue to drain");
8571553Srgrimes		if (i == 10)
8581553Srgrimes			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
8591553Srgrimes				printer, RM);
8601553Srgrimes		sleep(5 * 60);
8611553Srgrimes	}
8621553Srgrimes	if (i)
8631553Srgrimes		pstatus("sending to %s", RM);
8641553Srgrimes	sizerr = 0;
8651553Srgrimes	for (i = 0; i < stb.st_size; i += BUFSIZ) {
8661553Srgrimes		amt = BUFSIZ;
8671553Srgrimes		if (i + amt > stb.st_size)
8681553Srgrimes			amt = stb.st_size - i;
8691553Srgrimes		if (sizerr == 0 && read(f, buf, amt) != amt)
8701553Srgrimes			sizerr = 1;
8711553Srgrimes		if (write(pfd, buf, amt) != amt) {
8721553Srgrimes			(void) close(f);
8731553Srgrimes			return(REPRINT);
8741553Srgrimes		}
8751553Srgrimes	}
8761553Srgrimes
8771553Srgrimes
8781553Srgrimes
8791553Srgrimes
8801553Srgrimes	(void) close(f);
8811553Srgrimes	if (sizerr) {
8821553Srgrimes		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
8831553Srgrimes		/* tell recvjob to ignore this file */
8841553Srgrimes		(void) write(pfd, "\1", 1);
8851553Srgrimes		return(ERROR);
8861553Srgrimes	}
8871553Srgrimes	if (write(pfd, "", 1) != 1 || response())
8881553Srgrimes		return(REPRINT);
8891553Srgrimes	return(OK);
8901553Srgrimes}
8911553Srgrimes
8921553Srgrimes/*
8931553Srgrimes * Check to make sure there have been no errors and that both programs
8941553Srgrimes * are in sync with eachother.
8951553Srgrimes * Return non-zero if the connection was lost.
8961553Srgrimes */
8971553Srgrimesstatic char
8981553Srgrimesresponse()
8991553Srgrimes{
9001553Srgrimes	char resp;
9011553Srgrimes
9021553Srgrimes	if (read(pfd, &resp, 1) != 1) {
9031553Srgrimes		syslog(LOG_INFO, "%s: lost connection", printer);
9041553Srgrimes		return(-1);
9051553Srgrimes	}
9061553Srgrimes	return(resp);
9071553Srgrimes}
9081553Srgrimes
9091553Srgrimes/*
9101553Srgrimes * Banner printing stuff
9111553Srgrimes */
9121553Srgrimesstatic void
9131553Srgrimesbanner(name1, name2)
9141553Srgrimes	char *name1, *name2;
9151553Srgrimes{
9161553Srgrimes	time_t tvec;
9171553Srgrimes
9181553Srgrimes	time(&tvec);
9191553Srgrimes	if (!SF && !tof)
9201553Srgrimes		(void) write(ofd, FF, strlen(FF));
9211553Srgrimes	if (SB) {	/* short banner only */
9221553Srgrimes		if (class[0]) {
9231553Srgrimes			(void) write(ofd, class, strlen(class));
9241553Srgrimes			(void) write(ofd, ":", 1);
9251553Srgrimes		}
9261553Srgrimes		(void) write(ofd, name1, strlen(name1));
9271553Srgrimes		(void) write(ofd, "  Job: ", 7);
9281553Srgrimes		(void) write(ofd, name2, strlen(name2));
9291553Srgrimes		(void) write(ofd, "  Date: ", 8);
9301553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
9311553Srgrimes		(void) write(ofd, "\n", 1);
9321553Srgrimes	} else {	/* normal banner */
9331553Srgrimes		(void) write(ofd, "\n\n\n", 3);
9341553Srgrimes		scan_out(ofd, name1, '\0');
9351553Srgrimes		(void) write(ofd, "\n\n", 2);
9361553Srgrimes		scan_out(ofd, name2, '\0');
9371553Srgrimes		if (class[0]) {
9381553Srgrimes			(void) write(ofd,"\n\n\n",3);
9391553Srgrimes			scan_out(ofd, class, '\0');
9401553Srgrimes		}
9411553Srgrimes		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
9421553Srgrimes		(void) write(ofd, name2, strlen(name2));
9431553Srgrimes		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
9441553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
9451553Srgrimes		(void) write(ofd, "\n", 1);
9461553Srgrimes	}
9471553Srgrimes	if (!SF)
9481553Srgrimes		(void) write(ofd, FF, strlen(FF));
9491553Srgrimes	tof = 1;
9501553Srgrimes}
9511553Srgrimes
9521553Srgrimesstatic char *
9531553Srgrimesscnline(key, p, c)
9541553Srgrimes	register int key;
9551553Srgrimes	register char *p;
9561553Srgrimes	int c;
9571553Srgrimes{
9581553Srgrimes	register scnwidth;
9591553Srgrimes
9601553Srgrimes	for (scnwidth = WIDTH; --scnwidth;) {
9611553Srgrimes		key <<= 1;
9621553Srgrimes		*p++ = key & 0200 ? c : BACKGND;
9631553Srgrimes	}
9641553Srgrimes	return (p);
9651553Srgrimes}
9661553Srgrimes
9671553Srgrimes#define TRC(q)	(((q)-' ')&0177)
9681553Srgrimes
9691553Srgrimesstatic void
9701553Srgrimesscan_out(scfd, scsp, dlm)
9711553Srgrimes	int scfd, dlm;
9721553Srgrimes	char *scsp;
9731553Srgrimes{
9741553Srgrimes	register char *strp;
9751553Srgrimes	register nchrs, j;
9761553Srgrimes	char outbuf[LINELEN+1], *sp, c, cc;
9771553Srgrimes	int d, scnhgt;
9781553Srgrimes
9791553Srgrimes	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
9801553Srgrimes		strp = &outbuf[0];
9811553Srgrimes		sp = scsp;
9821553Srgrimes		for (nchrs = 0; ; ) {
9831553Srgrimes			d = dropit(c = TRC(cc = *sp++));
9841553Srgrimes			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
9851553Srgrimes				for (j = WIDTH; --j;)
9861553Srgrimes					*strp++ = BACKGND;
9871553Srgrimes			else
9881553Srgrimes				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
9891553Srgrimes			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
9901553Srgrimes				break;
9911553Srgrimes			*strp++ = BACKGND;
9921553Srgrimes			*strp++ = BACKGND;
9931553Srgrimes		}
9941553Srgrimes		while (*--strp == BACKGND && strp >= outbuf)
9951553Srgrimes			;
9961553Srgrimes		strp++;
9978857Srgrimes		*strp++ = '\n';
9981553Srgrimes		(void) write(scfd, outbuf, strp-outbuf);
9991553Srgrimes	}
10001553Srgrimes}
10011553Srgrimes
10021553Srgrimesstatic int
10031553Srgrimesdropit(c)
10041553Srgrimes	int c;
10051553Srgrimes{
10061553Srgrimes	switch(c) {
10071553Srgrimes
10081553Srgrimes	case TRC('_'):
10091553Srgrimes	case TRC(';'):
10101553Srgrimes	case TRC(','):
10111553Srgrimes	case TRC('g'):
10121553Srgrimes	case TRC('j'):
10131553Srgrimes	case TRC('p'):
10141553Srgrimes	case TRC('q'):
10151553Srgrimes	case TRC('y'):
10161553Srgrimes		return (DROP);
10171553Srgrimes
10181553Srgrimes	default:
10191553Srgrimes		return (0);
10201553Srgrimes	}
10211553Srgrimes}
10221553Srgrimes
10231553Srgrimes/*
10241553Srgrimes * sendmail ---
10251553Srgrimes *   tell people about job completion
10261553Srgrimes */
10271553Srgrimesstatic void
10281553Srgrimessendmail(user, bombed)
10291553Srgrimes	char *user;
10301553Srgrimes	int bombed;
10311553Srgrimes{
10321553Srgrimes	register int i;
103318569Sbde	int dtablesize;
10341553Srgrimes	int p[2], s;
10351553Srgrimes	register char *cp;
10361553Srgrimes	char buf[100];
10371553Srgrimes	struct stat stb;
10381553Srgrimes	FILE *fp;
10391553Srgrimes
10401553Srgrimes	pipe(p);
10411553Srgrimes	if ((s = dofork(DORETURN)) == 0) {		/* child */
10421553Srgrimes		dup2(p[0], 0);
10438094Sjkh		closelog();
104418569Sbde		for (i = 3, dtablesize = getdtablesize(); i < dtablesize; i++)
10451553Srgrimes			(void) close(i);
10461553Srgrimes		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
10471553Srgrimes			cp++;
10481553Srgrimes	else
10491553Srgrimes			cp = _PATH_SENDMAIL;
10501553Srgrimes		sprintf(buf, "%s@%s", user, fromhost);
10511553Srgrimes		execl(_PATH_SENDMAIL, cp, buf, 0);
10521553Srgrimes		exit(0);
10531553Srgrimes	} else if (s > 0) {				/* parent */
10541553Srgrimes		dup2(p[1], 1);
10551553Srgrimes		printf("To: %s@%s\n", user, fromhost);
105615648Sjoerg		printf("Subject: %s printer job \"%s\"\n", printer,
105715648Sjoerg			*jobname ? jobname : "<unknown>");
105815648Sjoerg		printf("Reply-To: root@%s\n\n", host);
10591553Srgrimes		printf("Your printer job ");
10601553Srgrimes		if (*jobname)
10611553Srgrimes			printf("(%s) ", jobname);
10621553Srgrimes		switch (bombed) {
10631553Srgrimes		case OK:
10641553Srgrimes			printf("\ncompleted successfully\n");
106515648Sjoerg			cp = "OK";
10661553Srgrimes			break;
10671553Srgrimes		default:
10681553Srgrimes		case FATALERR:
10691553Srgrimes			printf("\ncould not be printed\n");
107015648Sjoerg			cp = "FATALERR";
10711553Srgrimes			break;
10721553Srgrimes		case NOACCT:
10731553Srgrimes			printf("\ncould not be printed without an account on %s\n", host);
107415648Sjoerg			cp = "NOACCT";
10751553Srgrimes			break;
10761553Srgrimes		case FILTERERR:
10771553Srgrimes			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
10781553Srgrimes			    (fp = fopen(tempfile, "r")) == NULL) {
107915648Sjoerg				printf("\nhad some errors and may not have printed\n");
10801553Srgrimes				break;
10811553Srgrimes			}
108215648Sjoerg			printf("\nhad the following errors and may not have printed:\n");
10831553Srgrimes			while ((i = getc(fp)) != EOF)
10841553Srgrimes				putchar(i);
10851553Srgrimes			(void) fclose(fp);
108615648Sjoerg			cp = "FILTERERR";
10871553Srgrimes			break;
10881553Srgrimes		case ACCESS:
10891553Srgrimes			printf("\nwas not printed because it was not linked to the original file\n");
109015648Sjoerg			cp = "ACCESS";
10911553Srgrimes		}
10921553Srgrimes		fflush(stdout);
10931553Srgrimes		(void) close(1);
10941553Srgrimes	}
10951553Srgrimes	(void) close(p[0]);
10961553Srgrimes	(void) close(p[1]);
109715648Sjoerg	wait(NULL);
109815648Sjoerg	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
109915648Sjoerg		user, *jobname ? jobname : "<unknown>", printer, cp);
11001553Srgrimes}
11011553Srgrimes
11021553Srgrimes/*
11031553Srgrimes * dofork - fork with retries on failure
11041553Srgrimes */
11051553Srgrimesstatic int
11061553Srgrimesdofork(action)
11071553Srgrimes	int action;
11081553Srgrimes{
11091553Srgrimes	register int i, pid;
11101553Srgrimes
11111553Srgrimes	for (i = 0; i < 20; i++) {
11121553Srgrimes		if ((pid = fork()) < 0) {
11131553Srgrimes			sleep((unsigned)(i*i));
11141553Srgrimes			continue;
11151553Srgrimes		}
11161553Srgrimes		/*
11171553Srgrimes		 * Child should run as daemon instead of root
11181553Srgrimes		 */
111915648Sjoerg		if (pid == 0)
11201553Srgrimes			setuid(DU);
11211553Srgrimes		return(pid);
11221553Srgrimes	}
11231553Srgrimes	syslog(LOG_ERR, "can't fork");
11241553Srgrimes
11251553Srgrimes	switch (action) {
11261553Srgrimes	case DORETURN:
11271553Srgrimes		return (-1);
11281553Srgrimes	default:
11291553Srgrimes		syslog(LOG_ERR, "bad action (%d) to dofork", action);
11301553Srgrimes		/*FALL THRU*/
11311553Srgrimes	case DOABORT:
11321553Srgrimes		exit(1);
11331553Srgrimes	}
11341553Srgrimes	/*NOTREACHED*/
11351553Srgrimes}
11361553Srgrimes
11371553Srgrimes/*
11381553Srgrimes * Kill child processes to abort current job.
11391553Srgrimes */
11401553Srgrimesstatic void
11411553Srgrimesabortpr(signo)
11421553Srgrimes	int signo;
11431553Srgrimes{
11441553Srgrimes	(void) unlink(tempfile);
11451553Srgrimes	kill(0, SIGINT);
11461553Srgrimes	if (ofilter > 0)
11471553Srgrimes		kill(ofilter, SIGCONT);
11481553Srgrimes	while (wait(NULL) > 0)
11491553Srgrimes		;
11501553Srgrimes	exit(0);
11511553Srgrimes}
11521553Srgrimes
11531553Srgrimesstatic void
11541553Srgrimesinit()
11551553Srgrimes{
11561553Srgrimes	int status;
11571553Srgrimes	char *s;
11581553Srgrimes
11591553Srgrimes	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
11601553Srgrimes		syslog(LOG_ERR, "can't open printer description file");
11611553Srgrimes		exit(1);
11621553Srgrimes	} else if (status == -1) {
11631553Srgrimes		syslog(LOG_ERR, "unknown printer: %s", printer);
11641553Srgrimes		exit(1);
11651553Srgrimes	} else if (status == -3)
11661553Srgrimes		fatal("potential reference loop detected in printcap file");
11671553Srgrimes
11681553Srgrimes	if (cgetstr(bp, "lp", &LP) == -1)
11691553Srgrimes		LP = _PATH_DEFDEVLP;
11701553Srgrimes	if (cgetstr(bp, "rp", &RP) == -1)
11711553Srgrimes		RP = DEFLP;
11721553Srgrimes	if (cgetstr(bp, "lo", &LO) == -1)
11731553Srgrimes		LO = DEFLOCK;
11741553Srgrimes	if (cgetstr(bp, "st", &ST) == -1)
11751553Srgrimes		ST = DEFSTAT;
11761553Srgrimes	if (cgetstr(bp, "lf", &LF) == -1)
11771553Srgrimes		LF = _PATH_CONSOLE;
11781553Srgrimes	if (cgetstr(bp, "sd", &SD) == -1)
11791553Srgrimes		SD = _PATH_DEFSPOOL;
11801553Srgrimes	if (cgetnum(bp, "du", &DU) < 0)
11811553Srgrimes		DU = DEFUID;
11821553Srgrimes	if (cgetstr(bp,"ff", &FF) == -1)
11831553Srgrimes		FF = DEFFF;
11841553Srgrimes	if (cgetnum(bp, "pw", &PW) < 0)
11851553Srgrimes		PW = DEFWIDTH;
11861553Srgrimes	sprintf(&width[2], "%d", PW);
11871553Srgrimes	if (cgetnum(bp, "pl", &PL) < 0)
11881553Srgrimes		PL = DEFLENGTH;
11891553Srgrimes	sprintf(&length[2], "%d", PL);
11901553Srgrimes	if (cgetnum(bp,"px", &PX) < 0)
11911553Srgrimes		PX = 0;
11921553Srgrimes	sprintf(&pxwidth[2], "%d", PX);
11931553Srgrimes	if (cgetnum(bp, "py", &PY) < 0)
11941553Srgrimes		PY = 0;
11951553Srgrimes	sprintf(&pxlength[2], "%d", PY);
11961553Srgrimes	cgetstr(bp, "rm", &RM);
11971553Srgrimes	if (s = checkremote())
11981553Srgrimes		syslog(LOG_WARNING, s);
11991553Srgrimes
12001553Srgrimes	cgetstr(bp, "af", &AF);
12011553Srgrimes	cgetstr(bp, "of", &OF);
12021553Srgrimes	cgetstr(bp, "if", &IF);
12031553Srgrimes	cgetstr(bp, "rf", &RF);
12041553Srgrimes	cgetstr(bp, "tf", &TF);
12051553Srgrimes	cgetstr(bp, "nf", &NF);
12061553Srgrimes	cgetstr(bp, "df", &DF);
12071553Srgrimes	cgetstr(bp, "gf", &GF);
12081553Srgrimes	cgetstr(bp, "vf", &VF);
12091553Srgrimes	cgetstr(bp, "cf", &CF);
12101553Srgrimes	cgetstr(bp, "tr", &TR);
121115032Ssef	cgetstr(bp, "ms", &MS);
12121553Srgrimes
12131553Srgrimes	RS = (cgetcap(bp, "rs", ':') != NULL);
12141553Srgrimes	SF = (cgetcap(bp, "sf", ':') != NULL);
12151553Srgrimes	SH = (cgetcap(bp, "sh", ':') != NULL);
12161553Srgrimes	SB = (cgetcap(bp, "sb", ':') != NULL);
12171553Srgrimes	HL = (cgetcap(bp, "hl", ':') != NULL);
12181553Srgrimes	RW = (cgetcap(bp, "rw", ':') != NULL);
12191553Srgrimes
12201553Srgrimes	cgetnum(bp, "br", &BR);
12211553Srgrimes
12221553Srgrimes	tof = (cgetcap(bp, "fo", ':') == NULL);
12231553Srgrimes}
12241553Srgrimes
12251553Srgrimes/*
12261553Srgrimes * Acquire line printer or remote connection.
12271553Srgrimes */
12281553Srgrimesstatic void
12291553Srgrimesopenpr()
12301553Srgrimes{
123115648Sjoerg	register int i;
123218569Sbde	int dtablesize;
123315648Sjoerg	char *cp;
12341553Srgrimes
123515648Sjoerg	if (!remote && *LP) {
123615648Sjoerg		if (cp = index(LP, '@'))
123715648Sjoerg			opennet(cp);
123815648Sjoerg		else
123915648Sjoerg			opentty();
124015648Sjoerg	} else if (remote) {
124115648Sjoerg		openrem();
12421553Srgrimes	} else {
12431553Srgrimes		syslog(LOG_ERR, "%s: no line printer device or host name",
12441553Srgrimes			printer);
12451553Srgrimes		exit(1);
12461553Srgrimes	}
124715648Sjoerg
12481553Srgrimes	/*
12491553Srgrimes	 * Start up an output filter, if needed.
12501553Srgrimes	 */
12511553Srgrimes	if (!remote && OF) {
12521553Srgrimes		int p[2];
12531553Srgrimes
12541553Srgrimes		pipe(p);
12551553Srgrimes		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
12561553Srgrimes			dup2(p[0], 0);		/* pipe is std in */
12571553Srgrimes			dup2(pfd, 1);		/* printer is std out */
12588094Sjkh			closelog();
125918569Sbde			for (i = 3, dtablesize = getdtablesize();
126018569Sbde			     i < dtablesize; i++)
12611553Srgrimes				(void) close(i);
12621553Srgrimes			if ((cp = rindex(OF, '/')) == NULL)
12631553Srgrimes				cp = OF;
12641553Srgrimes			else
12651553Srgrimes				cp++;
12661553Srgrimes			execl(OF, cp, width, length, 0);
12671553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
12681553Srgrimes			exit(1);
12691553Srgrimes		}
12701553Srgrimes		(void) close(p[0]);		/* close input side */
12711553Srgrimes		ofd = p[1];			/* use pipe for output */
12721553Srgrimes	} else {
12731553Srgrimes		ofd = pfd;
12741553Srgrimes		ofilter = 0;
12751553Srgrimes	}
12761553Srgrimes}
12771553Srgrimes
127815648Sjoerg/*
127915648Sjoerg * Printer connected directly to the network
128015648Sjoerg * or to a terminal server on the net
128115648Sjoerg */
128215648Sjoergstatic void
128315648Sjoergopennet(cp)
128415648Sjoerg	char *cp;
128515648Sjoerg{
128615648Sjoerg	register int i;
128715648Sjoerg	int resp, port;
128815648Sjoerg	char save_ch;
128915648Sjoerg
129015648Sjoerg	save_ch = *cp;
129115648Sjoerg	*cp = '\0';
129215648Sjoerg	port = atoi(LP);
129315648Sjoerg	if (port <= 0) {
129415648Sjoerg		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
129515648Sjoerg		exit(1);
129615648Sjoerg	}
129715648Sjoerg	*cp++ = save_ch;
129815648Sjoerg
129915648Sjoerg	for (i = 1; ; i = i < 256 ? i << 1 : i) {
130015648Sjoerg		resp = -1;
130115648Sjoerg		pfd = getport(cp, port);
130215648Sjoerg		if (pfd < 0 && errno == ECONNREFUSED)
130315648Sjoerg			resp = 1;
130415648Sjoerg		else if (pfd >= 0) {
130515648Sjoerg			/*
130615648Sjoerg			 * need to delay a bit for rs232 lines
130715648Sjoerg			 * to stabilize in case printer is
130815648Sjoerg			 * connected via a terminal server
130915648Sjoerg			 */
131015648Sjoerg			delay(500);
131115648Sjoerg			break;
131215648Sjoerg		}
131315648Sjoerg		if (i == 1) {
131415648Sjoerg		   if (resp < 0)
131515648Sjoerg			pstatus("waiting for %s to come up", LP);
131615648Sjoerg		   else
131715648Sjoerg			pstatus("waiting for access to printer on %s", LP);
131815648Sjoerg		}
131915648Sjoerg		sleep(i);
132015648Sjoerg	}
132115648Sjoerg	pstatus("sending to %s port %d", cp, port);
132215648Sjoerg}
132315648Sjoerg
132415648Sjoerg/*
132515648Sjoerg * Printer is connected to an RS232 port on this host
132615648Sjoerg */
132715648Sjoergstatic void
132815648Sjoergopentty()
132915648Sjoerg{
133015648Sjoerg	register int i;
133115648Sjoerg	int resp, port;
133215648Sjoerg
133315648Sjoerg	for (i = 1; ; i = i < 32 ? i << 1 : i) {
133415648Sjoerg		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
133515648Sjoerg		if (pfd >= 0) {
133615648Sjoerg			delay(500);
133715648Sjoerg			break;
133815648Sjoerg		}
133915648Sjoerg		if (errno == ENOENT) {
134015648Sjoerg			syslog(LOG_ERR, "%s: %m", LP);
134115648Sjoerg			exit(1);
134215648Sjoerg		}
134315648Sjoerg		if (i == 1)
134415648Sjoerg			pstatus("waiting for %s to become ready (offline ?)",
134515648Sjoerg				printer);
134615648Sjoerg		sleep(i);
134715648Sjoerg	}
134815648Sjoerg	if (isatty(pfd))
134915648Sjoerg		setty();
135015648Sjoerg	pstatus("%s is ready and printing", printer);
135115648Sjoerg}
135215648Sjoerg
135315648Sjoerg/*
135415648Sjoerg * Printer is on a remote host
135515648Sjoerg */
135615648Sjoergstatic void
135715648Sjoergopenrem()
135815648Sjoerg{
135915648Sjoerg	register int i, n;
136015648Sjoerg	int resp, port;
136115648Sjoerg
136215648Sjoerg	for (i = 1; ; i = i < 256 ? i << 1 : i) {
136315648Sjoerg		resp = -1;
136415648Sjoerg		pfd = getport(RM, 0);
136515648Sjoerg		if (pfd >= 0) {
136615648Sjoerg			(void) sprintf(line, "\2%s\n", RP);
136715648Sjoerg			n = strlen(line);
136815648Sjoerg			if (write(pfd, line, n) == n &&
136915648Sjoerg			    (resp = response()) == '\0')
137015648Sjoerg				break;
137115648Sjoerg			(void) close(pfd);
137215648Sjoerg		}
137315648Sjoerg		if (i == 1) {
137415648Sjoerg			if (resp < 0)
137515648Sjoerg				pstatus("waiting for %s to come up", RM);
137615648Sjoerg			else {
137715648Sjoerg				pstatus("waiting for queue to be enabled on %s",
137815648Sjoerg					RM);
137915648Sjoerg				i = 256;
138015648Sjoerg			}
138115648Sjoerg		}
138215648Sjoerg		sleep(i);
138315648Sjoerg	}
138415648Sjoerg	pstatus("sending to %s", RM);
138515648Sjoerg}
138615648Sjoerg
13871553Srgrimesstruct bauds {
13881553Srgrimes	int	baud;
13891553Srgrimes	int	speed;
13901553Srgrimes} bauds[] = {
13911553Srgrimes	50,	B50,
13921553Srgrimes	75,	B75,
13931553Srgrimes	110,	B110,
13941553Srgrimes	134,	B134,
13951553Srgrimes	150,	B150,
13961553Srgrimes	200,	B200,
13971553Srgrimes	300,	B300,
13981553Srgrimes	600,	B600,
13991553Srgrimes	1200,	B1200,
14001553Srgrimes	1800,	B1800,
14011553Srgrimes	2400,	B2400,
14021553Srgrimes	4800,	B4800,
14031553Srgrimes	9600,	B9600,
14041553Srgrimes	19200,	EXTA,
14051553Srgrimes	38400,	EXTB,
14069821Swpaul	57600,	B57600,
14079821Swpaul	115200,	B115200,
14081553Srgrimes	0,	0
14091553Srgrimes};
14101553Srgrimes
14111553Srgrimes/*
14121553Srgrimes * setup tty lines.
14131553Srgrimes */
14141553Srgrimesstatic void
14151553Srgrimessetty()
14161553Srgrimes{
141715032Ssef	struct termios ttybuf;
141815032Ssef	struct bauds *bp;
14191553Srgrimes
14201553Srgrimes	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
14211553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
14221553Srgrimes		exit(1);
14231553Srgrimes	}
142415032Ssef	if (tcgetattr(pfd, &ttybuf) < 0) {
142515032Ssef		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
14261553Srgrimes		exit(1);
14271553Srgrimes	}
14281553Srgrimes	if (BR > 0) {
14291553Srgrimes		for (bp = bauds; bp->baud; bp++)
14301553Srgrimes			if (BR == bp->baud)
14311553Srgrimes				break;
14321553Srgrimes		if (!bp->baud) {
14331553Srgrimes			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
14341553Srgrimes			exit(1);
14351553Srgrimes		}
143615032Ssef		cfsetspeed(&ttybuf, bp->speed);
14371553Srgrimes	}
143815032Ssef	if (MS) {
143915032Ssef		char *s = strdup(MS), *tmp;
144015032Ssef
144115032Ssef		while (tmp = strsep (&s, ",")) {
144215032Ssef			msearch(tmp, &ttybuf);
14431553Srgrimes		}
14441553Srgrimes	}
144515032Ssef	if (MS || (BR > 0)) {
144615032Ssef		if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
144715032Ssef			syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
14481553Srgrimes		}
14491553Srgrimes	}
14501553Srgrimes}
14511553Srgrimes
14521553Srgrimes#if __STDC__
14531553Srgrimes#include <stdarg.h>
14541553Srgrimes#else
14551553Srgrimes#include <varargs.h>
14561553Srgrimes#endif
14571553Srgrimes
145815648Sjoergstatic void
14591553Srgrimes#if __STDC__
14601553Srgrimespstatus(const char *msg, ...)
14611553Srgrimes#else
14621553Srgrimespstatus(msg, va_alist)
14631553Srgrimes	char *msg;
14641553Srgrimes        va_dcl
14651553Srgrimes#endif
14661553Srgrimes{
14671553Srgrimes	register int fd;
14681553Srgrimes	char buf[BUFSIZ];
14691553Srgrimes	va_list ap;
14701553Srgrimes#if __STDC__
14711553Srgrimes	va_start(ap, msg);
14721553Srgrimes#else
14731553Srgrimes	va_start(ap);
14741553Srgrimes#endif
14751553Srgrimes
14761553Srgrimes	umask(0);
14771553Srgrimes	fd = open(ST, O_WRONLY|O_CREAT, 0664);
14781553Srgrimes	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
14791553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
14801553Srgrimes		exit(1);
14811553Srgrimes	}
14821553Srgrimes	ftruncate(fd, 0);
14831553Srgrimes	(void)vsnprintf(buf, sizeof(buf), msg, ap);
14841553Srgrimes	va_end(ap);
14851553Srgrimes	strcat(buf, "\n");
14861553Srgrimes	(void) write(fd, buf, strlen(buf));
14871553Srgrimes	(void) close(fd);
14881553Srgrimes}
1489