printjob.c revision 15703
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		}
2911553Srgrimes		(void) unlink(tempfile);
2921553Srgrimes		exit(0);
2931553Srgrimes	}
2941553Srgrimes	goto again;
2951553Srgrimes}
2961553Srgrimes
2971553Srgrimeschar	fonts[4][50];	/* fonts for troff */
2981553Srgrimes
2991553Srgrimeschar ifonts[4][40] = {
3001553Srgrimes	_PATH_VFONTR,
3011553Srgrimes	_PATH_VFONTI,
3021553Srgrimes	_PATH_VFONTB,
3031553Srgrimes	_PATH_VFONTS,
3041553Srgrimes};
3051553Srgrimes
3061553Srgrimes/*
3071553Srgrimes * The remaining part is the reading of the control file (cf)
3081553Srgrimes * and performing the various actions.
3091553Srgrimes */
3101553Srgrimesstatic int
3111553Srgrimesprintit(file)
3121553Srgrimes	char *file;
3131553Srgrimes{
3141553Srgrimes	register int i;
3151553Srgrimes	char *cp;
3161553Srgrimes	int bombed = OK;
3171553Srgrimes
3181553Srgrimes	/*
3191553Srgrimes	 * open control file; ignore if no longer there.
3201553Srgrimes	 */
3211553Srgrimes	if ((cfp = fopen(file, "r")) == NULL) {
3221553Srgrimes		syslog(LOG_INFO, "%s: %s: %m", printer, file);
3231553Srgrimes		return(OK);
3241553Srgrimes	}
3251553Srgrimes	/*
3261553Srgrimes	 * Reset troff fonts.
3271553Srgrimes	 */
3281553Srgrimes	for (i = 0; i < 4; i++)
3291553Srgrimes		strcpy(fonts[i], ifonts[i]);
3301553Srgrimes	sprintf(&width[2], "%d", PW);
3311553Srgrimes	strcpy(indent+2, "0");
3321553Srgrimes
3331553Srgrimes	/*
3341553Srgrimes	 *      read the control file for work to do
3351553Srgrimes	 *
3361553Srgrimes	 *      file format -- first character in the line is a command
3371553Srgrimes	 *      rest of the line is the argument.
3381553Srgrimes	 *      valid commands are:
3391553Srgrimes	 *
3401553Srgrimes	 *		S -- "stat info" for symbolic link protection
3411553Srgrimes	 *		J -- "job name" on banner page
3421553Srgrimes	 *		C -- "class name" on banner page
3431553Srgrimes	 *              L -- "literal" user's name to print on banner
3441553Srgrimes	 *		T -- "title" for pr
3451553Srgrimes	 *		H -- "host name" of machine where lpr was done
3461553Srgrimes	 *              P -- "person" user's login name
3471553Srgrimes	 *              I -- "indent" amount to indent output
34815648Sjoerg	 *		R -- laser dpi "resolution"
3491553Srgrimes	 *              f -- "file name" name of text file to print
3501553Srgrimes	 *		l -- "file name" text file with control chars
3511553Srgrimes	 *		p -- "file name" text file to print with pr(1)
3521553Srgrimes	 *		t -- "file name" troff(1) file to print
3531553Srgrimes	 *		n -- "file name" ditroff(1) file to print
3541553Srgrimes	 *		d -- "file name" dvi file to print
3551553Srgrimes	 *		g -- "file name" plot(1G) file to print
3561553Srgrimes	 *		v -- "file name" plain raster file to print
3571553Srgrimes	 *		c -- "file name" cifplot file to print
3581553Srgrimes	 *		1 -- "R font file" for troff
3591553Srgrimes	 *		2 -- "I font file" for troff
3601553Srgrimes	 *		3 -- "B font file" for troff
3611553Srgrimes	 *		4 -- "S font file" for troff
3621553Srgrimes	 *		N -- "name" of file (used by lpq)
3631553Srgrimes	 *              U -- "unlink" name of file to remove
3641553Srgrimes	 *                    (after we print it. (Pass 2 only)).
3651553Srgrimes	 *		M -- "mail" to user when done printing
3661553Srgrimes	 *
3671553Srgrimes	 *      getline reads a line and expands tabs to blanks
3681553Srgrimes	 */
3691553Srgrimes
3701553Srgrimes	/* pass 1 */
3711553Srgrimes
3721553Srgrimes	while (getline(cfp))
3731553Srgrimes		switch (line[0]) {
3741553Srgrimes		case 'H':
3751553Srgrimes			strcpy(fromhost, line+1);
3761553Srgrimes			if (class[0] == '\0')
3771553Srgrimes				strncpy(class, line+1, sizeof(class)-1);
3781553Srgrimes			continue;
3791553Srgrimes
3801553Srgrimes		case 'P':
3811553Srgrimes			strncpy(logname, line+1, sizeof(logname)-1);
3821553Srgrimes			if (RS) {			/* restricted */
3831553Srgrimes				if (getpwnam(logname) == NULL) {
3841553Srgrimes					bombed = NOACCT;
3851553Srgrimes					sendmail(line+1, bombed);
3861553Srgrimes					goto pass2;
3871553Srgrimes				}
3881553Srgrimes			}
3891553Srgrimes			continue;
3901553Srgrimes
3911553Srgrimes		case 'S':
3921553Srgrimes			cp = line+1;
3931553Srgrimes			i = 0;
3941553Srgrimes			while (*cp >= '0' && *cp <= '9')
3951553Srgrimes				i = i * 10 + (*cp++ - '0');
3961553Srgrimes			fdev = i;
3971553Srgrimes			cp++;
3981553Srgrimes			i = 0;
3991553Srgrimes			while (*cp >= '0' && *cp <= '9')
4001553Srgrimes				i = i * 10 + (*cp++ - '0');
4011553Srgrimes			fino = i;
4021553Srgrimes			continue;
4031553Srgrimes
4041553Srgrimes		case 'J':
4051553Srgrimes			if (line[1] != '\0')
4061553Srgrimes				strncpy(jobname, line+1, sizeof(jobname)-1);
4071553Srgrimes			else
4081553Srgrimes				strcpy(jobname, " ");
4091553Srgrimes			continue;
4101553Srgrimes
4111553Srgrimes		case 'C':
4121553Srgrimes			if (line[1] != '\0')
4131553Srgrimes				strncpy(class, line+1, sizeof(class)-1);
4141553Srgrimes			else if (class[0] == '\0')
4151553Srgrimes				gethostname(class, sizeof(class));
4161553Srgrimes			continue;
4171553Srgrimes
4181553Srgrimes		case 'T':	/* header title for pr */
4191553Srgrimes			strncpy(title, line+1, sizeof(title)-1);
4201553Srgrimes			continue;
4211553Srgrimes
4221553Srgrimes		case 'L':	/* identification line */
4231553Srgrimes			if (!SH && !HL)
4241553Srgrimes				banner(line+1, jobname);
4251553Srgrimes			continue;
4261553Srgrimes
4271553Srgrimes		case '1':	/* troff fonts */
4281553Srgrimes		case '2':
4291553Srgrimes		case '3':
4301553Srgrimes		case '4':
4311553Srgrimes			if (line[1] != '\0')
4321553Srgrimes				strcpy(fonts[line[0]-'1'], line+1);
4331553Srgrimes			continue;
4341553Srgrimes
4351553Srgrimes		case 'W':	/* page width */
4361553Srgrimes			strncpy(width+2, line+1, sizeof(width)-3);
4371553Srgrimes			continue;
4381553Srgrimes
4391553Srgrimes		case 'I':	/* indent amount */
4401553Srgrimes			strncpy(indent+2, line+1, sizeof(indent)-3);
4411553Srgrimes			continue;
4421553Srgrimes
4431553Srgrimes		default:	/* some file to print */
4441553Srgrimes			switch (i = print(line[0], line+1)) {
4451553Srgrimes			case ERROR:
4461553Srgrimes				if (bombed == OK)
4471553Srgrimes					bombed = FATALERR;
4481553Srgrimes				break;
4491553Srgrimes			case REPRINT:
4501553Srgrimes				(void) fclose(cfp);
4511553Srgrimes				return(REPRINT);
4521553Srgrimes			case FILTERERR:
4531553Srgrimes			case ACCESS:
4541553Srgrimes				bombed = i;
4551553Srgrimes				sendmail(logname, bombed);
4561553Srgrimes			}
4571553Srgrimes			title[0] = '\0';
4581553Srgrimes			continue;
4591553Srgrimes
4601553Srgrimes		case 'N':
4611553Srgrimes		case 'U':
4621553Srgrimes		case 'M':
46315648Sjoerg		case 'R':
4641553Srgrimes			continue;
4651553Srgrimes		}
4661553Srgrimes
4671553Srgrimes	/* pass 2 */
4681553Srgrimes
4691553Srgrimespass2:
4701553Srgrimes	fseek(cfp, 0L, 0);
4711553Srgrimes	while (getline(cfp))
4721553Srgrimes		switch (line[0]) {
4731553Srgrimes		case 'L':	/* identification line */
4741553Srgrimes			if (!SH && HL)
4751553Srgrimes				banner(line+1, jobname);
4761553Srgrimes			continue;
4771553Srgrimes
4781553Srgrimes		case 'M':
4791553Srgrimes			if (bombed < NOACCT)	/* already sent if >= NOACCT */
4801553Srgrimes				sendmail(line+1, bombed);
4811553Srgrimes			continue;
4821553Srgrimes
4831553Srgrimes		case 'U':
4841553Srgrimes			(void) unlink(line+1);
4851553Srgrimes		}
4861553Srgrimes	/*
4871553Srgrimes	 * clean-up in case another control file exists
4881553Srgrimes	 */
4891553Srgrimes	(void) fclose(cfp);
4901553Srgrimes	(void) unlink(file);
4911553Srgrimes	return(bombed == OK ? OK : ERROR);
4921553Srgrimes}
4931553Srgrimes
4941553Srgrimes/*
4951553Srgrimes * Print a file.
4961553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
4971553Srgrimes * Return -1 if a non-recoverable error occured,
4981553Srgrimes * 2 if the filter detected some errors (but printed the job anyway),
4991553Srgrimes * 1 if we should try to reprint this job and
5001553Srgrimes * 0 if all is well.
5011553Srgrimes * Note: all filters take stdin as the file, stdout as the printer,
5021553Srgrimes * stderr as the log file, and must not ignore SIGINT.
5031553Srgrimes */
5041553Srgrimesstatic int
5051553Srgrimesprint(format, file)
5061553Srgrimes	int format;
5071553Srgrimes	char *file;
5081553Srgrimes{
5091553Srgrimes	register int n;
5101553Srgrimes	register char *prog;
5111553Srgrimes	int fi, fo;
5121553Srgrimes	FILE *fp;
5131553Srgrimes	char *av[15], buf[BUFSIZ];
5141553Srgrimes	int pid, p[2], stopped = 0;
5151553Srgrimes	union wait status;
5161553Srgrimes	struct stat stb;
5171553Srgrimes
5181553Srgrimes	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
5191553Srgrimes		return(ERROR);
5201553Srgrimes	/*
5211553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
5221553Srgrimes	 * still point to the same file or someone is trying to print
5231553Srgrimes	 * something he shouldn't.
5241553Srgrimes	 */
5251553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
5261553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
5271553Srgrimes		return(ACCESS);
5281553Srgrimes	if (!SF && !tof) {		/* start on a fresh page */
5291553Srgrimes		(void) write(ofd, FF, strlen(FF));
5301553Srgrimes		tof = 1;
5311553Srgrimes	}
5321553Srgrimes	if (IF == NULL && (format == 'f' || format == 'l')) {
5331553Srgrimes		tof = 0;
5341553Srgrimes		while ((n = read(fi, buf, BUFSIZ)) > 0)
5351553Srgrimes			if (write(ofd, buf, n) != n) {
5361553Srgrimes				(void) close(fi);
5371553Srgrimes				return(REPRINT);
5381553Srgrimes			}
5391553Srgrimes		(void) close(fi);
5401553Srgrimes		return(OK);
5411553Srgrimes	}
5421553Srgrimes	switch (format) {
5431553Srgrimes	case 'p':	/* print file using 'pr' */
5441553Srgrimes		if (IF == NULL) {	/* use output filter */
5451553Srgrimes			prog = _PATH_PR;
5461553Srgrimes			av[0] = "pr";
5471553Srgrimes			av[1] = width;
5481553Srgrimes			av[2] = length;
5491553Srgrimes			av[3] = "-h";
5501553Srgrimes			av[4] = *title ? title : " ";
5515445Sjoerg			av[5] = "-F";
5525445Sjoerg			av[6] = 0;
5531553Srgrimes			fo = ofd;
5541553Srgrimes			goto start;
5551553Srgrimes		}
5561553Srgrimes		pipe(p);
5571553Srgrimes		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
5581553Srgrimes			dup2(fi, 0);		/* file is stdin */
5591553Srgrimes			dup2(p[1], 1);		/* pipe is stdout */
5608094Sjkh			closelog();
5611553Srgrimes			for (n = 3; n < NOFILE; n++)
5621553Srgrimes				(void) close(n);
5631553Srgrimes			execl(_PATH_PR, "pr", width, length,
5645445Sjoerg			    "-h", *title ? title : " ", "-F", 0);
5651553Srgrimes			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
5661553Srgrimes			exit(2);
5671553Srgrimes		}
5681553Srgrimes		(void) close(p[1]);		/* close output side */
5691553Srgrimes		(void) close(fi);
5701553Srgrimes		if (prchild < 0) {
5711553Srgrimes			prchild = 0;
5721553Srgrimes			(void) close(p[0]);
5731553Srgrimes			return(ERROR);
5741553Srgrimes		}
5751553Srgrimes		fi = p[0];			/* use pipe for input */
5761553Srgrimes	case 'f':	/* print plain text file */
5771553Srgrimes		prog = IF;
5781553Srgrimes		av[1] = width;
5791553Srgrimes		av[2] = length;
5801553Srgrimes		av[3] = indent;
5811553Srgrimes		n = 4;
5821553Srgrimes		break;
5831553Srgrimes	case 'l':	/* like 'f' but pass control characters */
5841553Srgrimes		prog = IF;
5851553Srgrimes		av[1] = "-c";
5861553Srgrimes		av[2] = width;
5871553Srgrimes		av[3] = length;
5881553Srgrimes		av[4] = indent;
5891553Srgrimes		n = 5;
5901553Srgrimes		break;
5911553Srgrimes	case 'r':	/* print a fortran text file */
5921553Srgrimes		prog = RF;
5931553Srgrimes		av[1] = width;
5941553Srgrimes		av[2] = length;
5951553Srgrimes		n = 3;
5961553Srgrimes		break;
5971553Srgrimes	case 't':	/* print troff output */
5981553Srgrimes	case 'n':	/* print ditroff output */
5991553Srgrimes	case 'd':	/* print tex output */
6001553Srgrimes		(void) unlink(".railmag");
6011553Srgrimes		if ((fo = creat(".railmag", FILMOD)) < 0) {
6021553Srgrimes			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
6031553Srgrimes			(void) unlink(".railmag");
6041553Srgrimes		} else {
6051553Srgrimes			for (n = 0; n < 4; n++) {
6061553Srgrimes				if (fonts[n][0] != '/')
6071553Srgrimes					(void) write(fo, _PATH_VFONT,
6081553Srgrimes					    sizeof(_PATH_VFONT) - 1);
6091553Srgrimes				(void) write(fo, fonts[n], strlen(fonts[n]));
6101553Srgrimes				(void) write(fo, "\n", 1);
6111553Srgrimes			}
6121553Srgrimes			(void) close(fo);
6131553Srgrimes		}
6141553Srgrimes		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
6151553Srgrimes		av[1] = pxwidth;
6161553Srgrimes		av[2] = pxlength;
6171553Srgrimes		n = 3;
6181553Srgrimes		break;
6191553Srgrimes	case 'c':	/* print cifplot output */
6201553Srgrimes		prog = CF;
6211553Srgrimes		av[1] = pxwidth;
6221553Srgrimes		av[2] = pxlength;
6231553Srgrimes		n = 3;
6241553Srgrimes		break;
6251553Srgrimes	case 'g':	/* print plot(1G) output */
6261553Srgrimes		prog = GF;
6271553Srgrimes		av[1] = pxwidth;
6281553Srgrimes		av[2] = pxlength;
6291553Srgrimes		n = 3;
6301553Srgrimes		break;
6311553Srgrimes	case 'v':	/* print raster output */
6321553Srgrimes		prog = VF;
6331553Srgrimes		av[1] = pxwidth;
6341553Srgrimes		av[2] = pxlength;
6351553Srgrimes		n = 3;
6361553Srgrimes		break;
6371553Srgrimes	default:
6381553Srgrimes		(void) close(fi);
6391553Srgrimes		syslog(LOG_ERR, "%s: illegal format character '%c'",
6401553Srgrimes			printer, format);
6411553Srgrimes		return(ERROR);
6421553Srgrimes	}
64315648Sjoerg	if (prog == NULL) {
64415648Sjoerg		(void) close(fi);
64515648Sjoerg		syslog(LOG_ERR,
64615648Sjoerg		   "%s: no filter found in printcap for format character '%c'",
64715648Sjoerg		   printer, format);
64815648Sjoerg		return(ERROR);
64915648Sjoerg	}
6501553Srgrimes	if ((av[0] = rindex(prog, '/')) != NULL)
6511553Srgrimes		av[0]++;
6521553Srgrimes	else
6531553Srgrimes		av[0] = prog;
6541553Srgrimes	av[n++] = "-n";
6551553Srgrimes	av[n++] = logname;
6561553Srgrimes	av[n++] = "-h";
6571553Srgrimes	av[n++] = fromhost;
6581553Srgrimes	av[n++] = AF;
6591553Srgrimes	av[n] = 0;
6601553Srgrimes	fo = pfd;
6611553Srgrimes	if (ofilter > 0) {		/* stop output filter */
6621553Srgrimes		write(ofd, "\031\1", 2);
6631553Srgrimes		while ((pid =
6641553Srgrimes		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
6651553Srgrimes			;
6661553Srgrimes		if (status.w_stopval != WSTOPPED) {
6671553Srgrimes			(void) close(fi);
66815648Sjoerg			syslog(LOG_WARNING,
66915648Sjoerg				"%s: output filter died (retcode=%d termsig=%d)",
67015648Sjoerg				printer, status.w_retcode, status.w_termsig);
6711553Srgrimes			return(REPRINT);
6721553Srgrimes		}
6731553Srgrimes		stopped++;
6741553Srgrimes	}
6751553Srgrimesstart:
6761553Srgrimes	if ((child = dofork(DORETURN)) == 0) {	/* child */
6771553Srgrimes		dup2(fi, 0);
6781553Srgrimes		dup2(fo, 1);
6791553Srgrimes		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
6801553Srgrimes		if (n >= 0)
6811553Srgrimes			dup2(n, 2);
6828094Sjkh		closelog();
6831553Srgrimes		for (n = 3; n < NOFILE; n++)
6841553Srgrimes			(void) close(n);
6851553Srgrimes		execv(prog, av);
6861553Srgrimes		syslog(LOG_ERR, "cannot execv %s", prog);
6871553Srgrimes		exit(2);
6881553Srgrimes	}
6891553Srgrimes	(void) close(fi);
6901553Srgrimes	if (child < 0)
6911553Srgrimes		status.w_retcode = 100;
6921553Srgrimes	else
6931553Srgrimes		while ((pid = wait((int *)&status)) > 0 && pid != child)
6941553Srgrimes			;
6951553Srgrimes	child = 0;
6961553Srgrimes	prchild = 0;
6971553Srgrimes	if (stopped) {		/* restart output filter */
6981553Srgrimes		if (kill(ofilter, SIGCONT) < 0) {
6991553Srgrimes			syslog(LOG_ERR, "cannot restart output filter");
7001553Srgrimes			exit(1);
7011553Srgrimes		}
7021553Srgrimes	}
7031553Srgrimes	tof = 0;
7041553Srgrimes
7051553Srgrimes	/* Copy filter output to "lf" logfile */
7061553Srgrimes	if (fp = fopen(tempfile, "r")) {
7071553Srgrimes		while (fgets(buf, sizeof(buf), fp))
7081553Srgrimes			fputs(buf, stderr);
7091553Srgrimes		fclose(fp);
7101553Srgrimes	}
7111553Srgrimes
7121553Srgrimes	if (!WIFEXITED(status)) {
71315648Sjoerg		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
7141553Srgrimes			printer, format, status.w_termsig);
7151553Srgrimes		return(ERROR);
7161553Srgrimes	}
7171553Srgrimes	switch (status.w_retcode) {
7181553Srgrimes	case 0:
7191553Srgrimes		tof = 1;
7201553Srgrimes		return(OK);
7211553Srgrimes	case 1:
7221553Srgrimes		return(REPRINT);
72315648Sjoerg	case 2:
72415648Sjoerg		return(ERROR);
7251553Srgrimes	default:
72615648Sjoerg		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
7271553Srgrimes			printer, format, status.w_retcode);
72815648Sjoerg		return(FILTERERR);
7291553Srgrimes	}
7301553Srgrimes}
7311553Srgrimes
7321553Srgrimes/*
7331553Srgrimes * Send the daemon control file (cf) and any data files.
7341553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
7351553Srgrimes * 0 if all is well.
7361553Srgrimes */
7371553Srgrimesstatic int
7381553Srgrimessendit(file)
7391553Srgrimes	char *file;
7401553Srgrimes{
7411553Srgrimes	register int i, err = OK;
7421553Srgrimes	char *cp, last[BUFSIZ];
7431553Srgrimes
7441553Srgrimes	/*
7451553Srgrimes	 * open control file
7461553Srgrimes	 */
7471553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
7481553Srgrimes		return(OK);
7491553Srgrimes	/*
7501553Srgrimes	 *      read the control file for work to do
7511553Srgrimes	 *
7521553Srgrimes	 *      file format -- first character in the line is a command
7531553Srgrimes	 *      rest of the line is the argument.
7541553Srgrimes	 *      commands of interest are:
7551553Srgrimes	 *
7561553Srgrimes	 *            a-z -- "file name" name of file to print
7571553Srgrimes	 *              U -- "unlink" name of file to remove
7581553Srgrimes	 *                    (after we print it. (Pass 2 only)).
7591553Srgrimes	 */
7601553Srgrimes
7611553Srgrimes	/*
7621553Srgrimes	 * pass 1
7631553Srgrimes	 */
7641553Srgrimes	while (getline(cfp)) {
7651553Srgrimes	again:
7661553Srgrimes		if (line[0] == 'S') {
7671553Srgrimes			cp = line+1;
7681553Srgrimes			i = 0;
7691553Srgrimes			while (*cp >= '0' && *cp <= '9')
7701553Srgrimes				i = i * 10 + (*cp++ - '0');
7711553Srgrimes			fdev = i;
7721553Srgrimes			cp++;
7731553Srgrimes			i = 0;
7741553Srgrimes			while (*cp >= '0' && *cp <= '9')
7751553Srgrimes				i = i * 10 + (*cp++ - '0');
7761553Srgrimes			fino = i;
7771553Srgrimes			continue;
7781553Srgrimes		}
7791553Srgrimes		if (line[0] >= 'a' && line[0] <= 'z') {
7801553Srgrimes			strcpy(last, line);
7811553Srgrimes			while (i = getline(cfp))
7821553Srgrimes				if (strcmp(last, line))
7831553Srgrimes					break;
7841553Srgrimes			switch (sendfile('\3', last+1)) {
7851553Srgrimes			case OK:
7861553Srgrimes				if (i)
7871553Srgrimes					goto again;
7881553Srgrimes				break;
7891553Srgrimes			case REPRINT:
7901553Srgrimes				(void) fclose(cfp);
7911553Srgrimes				return(REPRINT);
7921553Srgrimes			case ACCESS:
7931553Srgrimes				sendmail(logname, ACCESS);
7941553Srgrimes			case ERROR:
7951553Srgrimes				err = ERROR;
7961553Srgrimes			}
7971553Srgrimes			break;
7981553Srgrimes		}
7991553Srgrimes	}
8001553Srgrimes	if (err == OK && sendfile('\2', file) > 0) {
8011553Srgrimes		(void) fclose(cfp);
8021553Srgrimes		return(REPRINT);
8031553Srgrimes	}
8041553Srgrimes	/*
8051553Srgrimes	 * pass 2
8061553Srgrimes	 */
8071553Srgrimes	fseek(cfp, 0L, 0);
8081553Srgrimes	while (getline(cfp))
8091553Srgrimes		if (line[0] == 'U')
8101553Srgrimes			(void) unlink(line+1);
8111553Srgrimes	/*
8121553Srgrimes	 * clean-up in case another control file exists
8131553Srgrimes	 */
8141553Srgrimes	(void) fclose(cfp);
8151553Srgrimes	(void) unlink(file);
8161553Srgrimes	return(err);
8171553Srgrimes}
8181553Srgrimes
8191553Srgrimes/*
8201553Srgrimes * Send a data file to the remote machine and spool it.
8211553Srgrimes * Return positive if we should try resending.
8221553Srgrimes */
8231553Srgrimesstatic int
8241553Srgrimessendfile(type, file)
8251553Srgrimes	int type;
8261553Srgrimes	char *file;
8271553Srgrimes{
8281553Srgrimes	register int f, i, amt;
8291553Srgrimes	struct stat stb;
8301553Srgrimes	char buf[BUFSIZ];
8311553Srgrimes	int sizerr, resp;
8321553Srgrimes
8331553Srgrimes	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
8341553Srgrimes		return(ERROR);
8351553Srgrimes	/*
8361553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
8371553Srgrimes	 * still point to the same file or someone is trying to print something
8381553Srgrimes	 * he shouldn't.
8391553Srgrimes	 */
8401553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
8411553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
8421553Srgrimes		return(ACCESS);
8431553Srgrimes	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
8441553Srgrimes	amt = strlen(buf);
8451553Srgrimes	for (i = 0;  ; i++) {
8461553Srgrimes		if (write(pfd, buf, amt) != amt ||
8471553Srgrimes		    (resp = response()) < 0 || resp == '\1') {
8481553Srgrimes			(void) close(f);
8491553Srgrimes			return(REPRINT);
8501553Srgrimes		} else if (resp == '\0')
8511553Srgrimes			break;
8521553Srgrimes		if (i == 0)
8531553Srgrimes			pstatus("no space on remote; waiting for queue to drain");
8541553Srgrimes		if (i == 10)
8551553Srgrimes			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
8561553Srgrimes				printer, RM);
8571553Srgrimes		sleep(5 * 60);
8581553Srgrimes	}
8591553Srgrimes	if (i)
8601553Srgrimes		pstatus("sending to %s", RM);
8611553Srgrimes	sizerr = 0;
8621553Srgrimes	for (i = 0; i < stb.st_size; i += BUFSIZ) {
8631553Srgrimes		amt = BUFSIZ;
8641553Srgrimes		if (i + amt > stb.st_size)
8651553Srgrimes			amt = stb.st_size - i;
8661553Srgrimes		if (sizerr == 0 && read(f, buf, amt) != amt)
8671553Srgrimes			sizerr = 1;
8681553Srgrimes		if (write(pfd, buf, amt) != amt) {
8691553Srgrimes			(void) close(f);
8701553Srgrimes			return(REPRINT);
8711553Srgrimes		}
8721553Srgrimes	}
8731553Srgrimes
8741553Srgrimes
8751553Srgrimes
8761553Srgrimes
8771553Srgrimes	(void) close(f);
8781553Srgrimes	if (sizerr) {
8791553Srgrimes		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
8801553Srgrimes		/* tell recvjob to ignore this file */
8811553Srgrimes		(void) write(pfd, "\1", 1);
8821553Srgrimes		return(ERROR);
8831553Srgrimes	}
8841553Srgrimes	if (write(pfd, "", 1) != 1 || response())
8851553Srgrimes		return(REPRINT);
8861553Srgrimes	return(OK);
8871553Srgrimes}
8881553Srgrimes
8891553Srgrimes/*
8901553Srgrimes * Check to make sure there have been no errors and that both programs
8911553Srgrimes * are in sync with eachother.
8921553Srgrimes * Return non-zero if the connection was lost.
8931553Srgrimes */
8941553Srgrimesstatic char
8951553Srgrimesresponse()
8961553Srgrimes{
8971553Srgrimes	char resp;
8981553Srgrimes
8991553Srgrimes	if (read(pfd, &resp, 1) != 1) {
9001553Srgrimes		syslog(LOG_INFO, "%s: lost connection", printer);
9011553Srgrimes		return(-1);
9021553Srgrimes	}
9031553Srgrimes	return(resp);
9041553Srgrimes}
9051553Srgrimes
9061553Srgrimes/*
9071553Srgrimes * Banner printing stuff
9081553Srgrimes */
9091553Srgrimesstatic void
9101553Srgrimesbanner(name1, name2)
9111553Srgrimes	char *name1, *name2;
9121553Srgrimes{
9131553Srgrimes	time_t tvec;
9141553Srgrimes
9151553Srgrimes	time(&tvec);
9161553Srgrimes	if (!SF && !tof)
9171553Srgrimes		(void) write(ofd, FF, strlen(FF));
9181553Srgrimes	if (SB) {	/* short banner only */
9191553Srgrimes		if (class[0]) {
9201553Srgrimes			(void) write(ofd, class, strlen(class));
9211553Srgrimes			(void) write(ofd, ":", 1);
9221553Srgrimes		}
9231553Srgrimes		(void) write(ofd, name1, strlen(name1));
9241553Srgrimes		(void) write(ofd, "  Job: ", 7);
9251553Srgrimes		(void) write(ofd, name2, strlen(name2));
9261553Srgrimes		(void) write(ofd, "  Date: ", 8);
9271553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
9281553Srgrimes		(void) write(ofd, "\n", 1);
9291553Srgrimes	} else {	/* normal banner */
9301553Srgrimes		(void) write(ofd, "\n\n\n", 3);
9311553Srgrimes		scan_out(ofd, name1, '\0');
9321553Srgrimes		(void) write(ofd, "\n\n", 2);
9331553Srgrimes		scan_out(ofd, name2, '\0');
9341553Srgrimes		if (class[0]) {
9351553Srgrimes			(void) write(ofd,"\n\n\n",3);
9361553Srgrimes			scan_out(ofd, class, '\0');
9371553Srgrimes		}
9381553Srgrimes		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
9391553Srgrimes		(void) write(ofd, name2, strlen(name2));
9401553Srgrimes		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
9411553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
9421553Srgrimes		(void) write(ofd, "\n", 1);
9431553Srgrimes	}
9441553Srgrimes	if (!SF)
9451553Srgrimes		(void) write(ofd, FF, strlen(FF));
9461553Srgrimes	tof = 1;
9471553Srgrimes}
9481553Srgrimes
9491553Srgrimesstatic char *
9501553Srgrimesscnline(key, p, c)
9511553Srgrimes	register int key;
9521553Srgrimes	register char *p;
9531553Srgrimes	int c;
9541553Srgrimes{
9551553Srgrimes	register scnwidth;
9561553Srgrimes
9571553Srgrimes	for (scnwidth = WIDTH; --scnwidth;) {
9581553Srgrimes		key <<= 1;
9591553Srgrimes		*p++ = key & 0200 ? c : BACKGND;
9601553Srgrimes	}
9611553Srgrimes	return (p);
9621553Srgrimes}
9631553Srgrimes
9641553Srgrimes#define TRC(q)	(((q)-' ')&0177)
9651553Srgrimes
9661553Srgrimesstatic void
9671553Srgrimesscan_out(scfd, scsp, dlm)
9681553Srgrimes	int scfd, dlm;
9691553Srgrimes	char *scsp;
9701553Srgrimes{
9711553Srgrimes	register char *strp;
9721553Srgrimes	register nchrs, j;
9731553Srgrimes	char outbuf[LINELEN+1], *sp, c, cc;
9741553Srgrimes	int d, scnhgt;
9751553Srgrimes
9761553Srgrimes	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
9771553Srgrimes		strp = &outbuf[0];
9781553Srgrimes		sp = scsp;
9791553Srgrimes		for (nchrs = 0; ; ) {
9801553Srgrimes			d = dropit(c = TRC(cc = *sp++));
9811553Srgrimes			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
9821553Srgrimes				for (j = WIDTH; --j;)
9831553Srgrimes					*strp++ = BACKGND;
9841553Srgrimes			else
9851553Srgrimes				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
9861553Srgrimes			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
9871553Srgrimes				break;
9881553Srgrimes			*strp++ = BACKGND;
9891553Srgrimes			*strp++ = BACKGND;
9901553Srgrimes		}
9911553Srgrimes		while (*--strp == BACKGND && strp >= outbuf)
9921553Srgrimes			;
9931553Srgrimes		strp++;
9948857Srgrimes		*strp++ = '\n';
9951553Srgrimes		(void) write(scfd, outbuf, strp-outbuf);
9961553Srgrimes	}
9971553Srgrimes}
9981553Srgrimes
9991553Srgrimesstatic int
10001553Srgrimesdropit(c)
10011553Srgrimes	int c;
10021553Srgrimes{
10031553Srgrimes	switch(c) {
10041553Srgrimes
10051553Srgrimes	case TRC('_'):
10061553Srgrimes	case TRC(';'):
10071553Srgrimes	case TRC(','):
10081553Srgrimes	case TRC('g'):
10091553Srgrimes	case TRC('j'):
10101553Srgrimes	case TRC('p'):
10111553Srgrimes	case TRC('q'):
10121553Srgrimes	case TRC('y'):
10131553Srgrimes		return (DROP);
10141553Srgrimes
10151553Srgrimes	default:
10161553Srgrimes		return (0);
10171553Srgrimes	}
10181553Srgrimes}
10191553Srgrimes
10201553Srgrimes/*
10211553Srgrimes * sendmail ---
10221553Srgrimes *   tell people about job completion
10231553Srgrimes */
10241553Srgrimesstatic void
10251553Srgrimessendmail(user, bombed)
10261553Srgrimes	char *user;
10271553Srgrimes	int bombed;
10281553Srgrimes{
10291553Srgrimes	register int i;
10301553Srgrimes	int p[2], s;
10311553Srgrimes	register char *cp;
10321553Srgrimes	char buf[100];
10331553Srgrimes	struct stat stb;
10341553Srgrimes	FILE *fp;
10351553Srgrimes
10361553Srgrimes	pipe(p);
10371553Srgrimes	if ((s = dofork(DORETURN)) == 0) {		/* child */
10381553Srgrimes		dup2(p[0], 0);
10398094Sjkh		closelog();
10401553Srgrimes		for (i = 3; i < NOFILE; i++)
10411553Srgrimes			(void) close(i);
10421553Srgrimes		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
10431553Srgrimes			cp++;
10441553Srgrimes	else
10451553Srgrimes			cp = _PATH_SENDMAIL;
10461553Srgrimes		sprintf(buf, "%s@%s", user, fromhost);
10471553Srgrimes		execl(_PATH_SENDMAIL, cp, buf, 0);
10481553Srgrimes		exit(0);
10491553Srgrimes	} else if (s > 0) {				/* parent */
10501553Srgrimes		dup2(p[1], 1);
10511553Srgrimes		printf("To: %s@%s\n", user, fromhost);
105215648Sjoerg		printf("Subject: %s printer job \"%s\"\n", printer,
105315648Sjoerg			*jobname ? jobname : "<unknown>");
105415648Sjoerg		printf("Reply-To: root@%s\n\n", host);
10551553Srgrimes		printf("Your printer job ");
10561553Srgrimes		if (*jobname)
10571553Srgrimes			printf("(%s) ", jobname);
10581553Srgrimes		switch (bombed) {
10591553Srgrimes		case OK:
10601553Srgrimes			printf("\ncompleted successfully\n");
106115648Sjoerg			cp = "OK";
10621553Srgrimes			break;
10631553Srgrimes		default:
10641553Srgrimes		case FATALERR:
10651553Srgrimes			printf("\ncould not be printed\n");
106615648Sjoerg			cp = "FATALERR";
10671553Srgrimes			break;
10681553Srgrimes		case NOACCT:
10691553Srgrimes			printf("\ncould not be printed without an account on %s\n", host);
107015648Sjoerg			cp = "NOACCT";
10711553Srgrimes			break;
10721553Srgrimes		case FILTERERR:
10731553Srgrimes			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
10741553Srgrimes			    (fp = fopen(tempfile, "r")) == NULL) {
107515648Sjoerg				printf("\nhad some errors and may not have printed\n");
10761553Srgrimes				break;
10771553Srgrimes			}
107815648Sjoerg			printf("\nhad the following errors and may not have printed:\n");
10791553Srgrimes			while ((i = getc(fp)) != EOF)
10801553Srgrimes				putchar(i);
10811553Srgrimes			(void) fclose(fp);
108215648Sjoerg			cp = "FILTERERR";
10831553Srgrimes			break;
10841553Srgrimes		case ACCESS:
10851553Srgrimes			printf("\nwas not printed because it was not linked to the original file\n");
108615648Sjoerg			cp = "ACCESS";
10871553Srgrimes		}
10881553Srgrimes		fflush(stdout);
10891553Srgrimes		(void) close(1);
10901553Srgrimes	}
10911553Srgrimes	(void) close(p[0]);
10921553Srgrimes	(void) close(p[1]);
109315648Sjoerg	wait(NULL);
109415648Sjoerg	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
109515648Sjoerg		user, *jobname ? jobname : "<unknown>", printer, cp);
10961553Srgrimes}
10971553Srgrimes
10981553Srgrimes/*
10991553Srgrimes * dofork - fork with retries on failure
11001553Srgrimes */
11011553Srgrimesstatic int
11021553Srgrimesdofork(action)
11031553Srgrimes	int action;
11041553Srgrimes{
11051553Srgrimes	register int i, pid;
11061553Srgrimes
11071553Srgrimes	for (i = 0; i < 20; i++) {
11081553Srgrimes		if ((pid = fork()) < 0) {
11091553Srgrimes			sleep((unsigned)(i*i));
11101553Srgrimes			continue;
11111553Srgrimes		}
11121553Srgrimes		/*
11131553Srgrimes		 * Child should run as daemon instead of root
11141553Srgrimes		 */
111515648Sjoerg		if (pid == 0)
11161553Srgrimes			setuid(DU);
11171553Srgrimes		return(pid);
11181553Srgrimes	}
11191553Srgrimes	syslog(LOG_ERR, "can't fork");
11201553Srgrimes
11211553Srgrimes	switch (action) {
11221553Srgrimes	case DORETURN:
11231553Srgrimes		return (-1);
11241553Srgrimes	default:
11251553Srgrimes		syslog(LOG_ERR, "bad action (%d) to dofork", action);
11261553Srgrimes		/*FALL THRU*/
11271553Srgrimes	case DOABORT:
11281553Srgrimes		exit(1);
11291553Srgrimes	}
11301553Srgrimes	/*NOTREACHED*/
11311553Srgrimes}
11321553Srgrimes
11331553Srgrimes/*
11341553Srgrimes * Kill child processes to abort current job.
11351553Srgrimes */
11361553Srgrimesstatic void
11371553Srgrimesabortpr(signo)
11381553Srgrimes	int signo;
11391553Srgrimes{
11401553Srgrimes	(void) unlink(tempfile);
11411553Srgrimes	kill(0, SIGINT);
11421553Srgrimes	if (ofilter > 0)
11431553Srgrimes		kill(ofilter, SIGCONT);
11441553Srgrimes	while (wait(NULL) > 0)
11451553Srgrimes		;
11461553Srgrimes	exit(0);
11471553Srgrimes}
11481553Srgrimes
11491553Srgrimesstatic void
11501553Srgrimesinit()
11511553Srgrimes{
11521553Srgrimes	int status;
11531553Srgrimes	char *s;
11541553Srgrimes
11551553Srgrimes	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
11561553Srgrimes		syslog(LOG_ERR, "can't open printer description file");
11571553Srgrimes		exit(1);
11581553Srgrimes	} else if (status == -1) {
11591553Srgrimes		syslog(LOG_ERR, "unknown printer: %s", printer);
11601553Srgrimes		exit(1);
11611553Srgrimes	} else if (status == -3)
11621553Srgrimes		fatal("potential reference loop detected in printcap file");
11631553Srgrimes
11641553Srgrimes	if (cgetstr(bp, "lp", &LP) == -1)
11651553Srgrimes		LP = _PATH_DEFDEVLP;
11661553Srgrimes	if (cgetstr(bp, "rp", &RP) == -1)
11671553Srgrimes		RP = DEFLP;
11681553Srgrimes	if (cgetstr(bp, "lo", &LO) == -1)
11691553Srgrimes		LO = DEFLOCK;
11701553Srgrimes	if (cgetstr(bp, "st", &ST) == -1)
11711553Srgrimes		ST = DEFSTAT;
11721553Srgrimes	if (cgetstr(bp, "lf", &LF) == -1)
11731553Srgrimes		LF = _PATH_CONSOLE;
11741553Srgrimes	if (cgetstr(bp, "sd", &SD) == -1)
11751553Srgrimes		SD = _PATH_DEFSPOOL;
11761553Srgrimes	if (cgetnum(bp, "du", &DU) < 0)
11771553Srgrimes		DU = DEFUID;
11781553Srgrimes	if (cgetstr(bp,"ff", &FF) == -1)
11791553Srgrimes		FF = DEFFF;
11801553Srgrimes	if (cgetnum(bp, "pw", &PW) < 0)
11811553Srgrimes		PW = DEFWIDTH;
11821553Srgrimes	sprintf(&width[2], "%d", PW);
11831553Srgrimes	if (cgetnum(bp, "pl", &PL) < 0)
11841553Srgrimes		PL = DEFLENGTH;
11851553Srgrimes	sprintf(&length[2], "%d", PL);
11861553Srgrimes	if (cgetnum(bp,"px", &PX) < 0)
11871553Srgrimes		PX = 0;
11881553Srgrimes	sprintf(&pxwidth[2], "%d", PX);
11891553Srgrimes	if (cgetnum(bp, "py", &PY) < 0)
11901553Srgrimes		PY = 0;
11911553Srgrimes	sprintf(&pxlength[2], "%d", PY);
11921553Srgrimes	cgetstr(bp, "rm", &RM);
11931553Srgrimes	if (s = checkremote())
11941553Srgrimes		syslog(LOG_WARNING, s);
11951553Srgrimes
11961553Srgrimes	cgetstr(bp, "af", &AF);
11971553Srgrimes	cgetstr(bp, "of", &OF);
11981553Srgrimes	cgetstr(bp, "if", &IF);
11991553Srgrimes	cgetstr(bp, "rf", &RF);
12001553Srgrimes	cgetstr(bp, "tf", &TF);
12011553Srgrimes	cgetstr(bp, "nf", &NF);
12021553Srgrimes	cgetstr(bp, "df", &DF);
12031553Srgrimes	cgetstr(bp, "gf", &GF);
12041553Srgrimes	cgetstr(bp, "vf", &VF);
12051553Srgrimes	cgetstr(bp, "cf", &CF);
12061553Srgrimes	cgetstr(bp, "tr", &TR);
120715032Ssef	cgetstr(bp, "ms", &MS);
12081553Srgrimes
12091553Srgrimes	RS = (cgetcap(bp, "rs", ':') != NULL);
12101553Srgrimes	SF = (cgetcap(bp, "sf", ':') != NULL);
12111553Srgrimes	SH = (cgetcap(bp, "sh", ':') != NULL);
12121553Srgrimes	SB = (cgetcap(bp, "sb", ':') != NULL);
12131553Srgrimes	HL = (cgetcap(bp, "hl", ':') != NULL);
12141553Srgrimes	RW = (cgetcap(bp, "rw", ':') != NULL);
12151553Srgrimes
12161553Srgrimes	cgetnum(bp, "br", &BR);
12171553Srgrimes
12181553Srgrimes	tof = (cgetcap(bp, "fo", ':') == NULL);
12191553Srgrimes}
12201553Srgrimes
12211553Srgrimes/*
12221553Srgrimes * Acquire line printer or remote connection.
12231553Srgrimes */
12241553Srgrimesstatic void
12251553Srgrimesopenpr()
12261553Srgrimes{
122715648Sjoerg	register int i;
122815648Sjoerg	char *cp;
12291553Srgrimes
123015648Sjoerg	if (!remote && *LP) {
123115648Sjoerg		if (cp = index(LP, '@'))
123215648Sjoerg			opennet(cp);
123315648Sjoerg		else
123415648Sjoerg			opentty();
123515648Sjoerg	} else if (remote) {
123615648Sjoerg		openrem();
12371553Srgrimes	} else {
12381553Srgrimes		syslog(LOG_ERR, "%s: no line printer device or host name",
12391553Srgrimes			printer);
12401553Srgrimes		exit(1);
12411553Srgrimes	}
124215648Sjoerg
12431553Srgrimes	/*
12441553Srgrimes	 * Start up an output filter, if needed.
12451553Srgrimes	 */
12461553Srgrimes	if (!remote && OF) {
12471553Srgrimes		int p[2];
12481553Srgrimes
12491553Srgrimes		pipe(p);
12501553Srgrimes		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
12511553Srgrimes			dup2(p[0], 0);		/* pipe is std in */
12521553Srgrimes			dup2(pfd, 1);		/* printer is std out */
12538094Sjkh			closelog();
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
127215648Sjoerg/*
127315648Sjoerg * Printer connected directly to the network
127415648Sjoerg * or to a terminal server on the net
127515648Sjoerg */
127615648Sjoergstatic void
127715648Sjoergopennet(cp)
127815648Sjoerg	char *cp;
127915648Sjoerg{
128015648Sjoerg	register int i;
128115648Sjoerg	int resp, port;
128215648Sjoerg	char save_ch;
128315648Sjoerg
128415648Sjoerg	save_ch = *cp;
128515648Sjoerg	*cp = '\0';
128615648Sjoerg	port = atoi(LP);
128715648Sjoerg	if (port <= 0) {
128815648Sjoerg		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
128915648Sjoerg		exit(1);
129015648Sjoerg	}
129115648Sjoerg	*cp++ = save_ch;
129215648Sjoerg
129315648Sjoerg	for (i = 1; ; i = i < 256 ? i << 1 : i) {
129415648Sjoerg		resp = -1;
129515648Sjoerg		pfd = getport(cp, port);
129615648Sjoerg		if (pfd < 0 && errno == ECONNREFUSED)
129715648Sjoerg			resp = 1;
129815648Sjoerg		else if (pfd >= 0) {
129915648Sjoerg			/*
130015648Sjoerg			 * need to delay a bit for rs232 lines
130115648Sjoerg			 * to stabilize in case printer is
130215648Sjoerg			 * connected via a terminal server
130315648Sjoerg			 */
130415648Sjoerg			delay(500);
130515648Sjoerg			break;
130615648Sjoerg		}
130715648Sjoerg		if (i == 1) {
130815648Sjoerg		   if (resp < 0)
130915648Sjoerg			pstatus("waiting for %s to come up", LP);
131015648Sjoerg		   else
131115648Sjoerg			pstatus("waiting for access to printer on %s", LP);
131215648Sjoerg		}
131315648Sjoerg		sleep(i);
131415648Sjoerg	}
131515648Sjoerg	pstatus("sending to %s port %d", cp, port);
131615648Sjoerg}
131715648Sjoerg
131815648Sjoerg/*
131915648Sjoerg * Printer is connected to an RS232 port on this host
132015648Sjoerg */
132115648Sjoergstatic void
132215648Sjoergopentty()
132315648Sjoerg{
132415648Sjoerg	register int i;
132515648Sjoerg	int resp, port;
132615648Sjoerg
132715648Sjoerg	for (i = 1; ; i = i < 32 ? i << 1 : i) {
132815648Sjoerg		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
132915648Sjoerg		if (pfd >= 0) {
133015648Sjoerg			delay(500);
133115648Sjoerg			break;
133215648Sjoerg		}
133315648Sjoerg		if (errno == ENOENT) {
133415648Sjoerg			syslog(LOG_ERR, "%s: %m", LP);
133515648Sjoerg			exit(1);
133615648Sjoerg		}
133715648Sjoerg		if (i == 1)
133815648Sjoerg			pstatus("waiting for %s to become ready (offline ?)",
133915648Sjoerg				printer);
134015648Sjoerg		sleep(i);
134115648Sjoerg	}
134215648Sjoerg	if (isatty(pfd))
134315648Sjoerg		setty();
134415648Sjoerg	pstatus("%s is ready and printing", printer);
134515648Sjoerg}
134615648Sjoerg
134715648Sjoerg/*
134815648Sjoerg * Printer is on a remote host
134915648Sjoerg */
135015648Sjoergstatic void
135115648Sjoergopenrem()
135215648Sjoerg{
135315648Sjoerg	register int i, n;
135415648Sjoerg	int resp, port;
135515648Sjoerg
135615648Sjoerg	for (i = 1; ; i = i < 256 ? i << 1 : i) {
135715648Sjoerg		resp = -1;
135815648Sjoerg		pfd = getport(RM, 0);
135915648Sjoerg		if (pfd >= 0) {
136015648Sjoerg			(void) sprintf(line, "\2%s\n", RP);
136115648Sjoerg			n = strlen(line);
136215648Sjoerg			if (write(pfd, line, n) == n &&
136315648Sjoerg			    (resp = response()) == '\0')
136415648Sjoerg				break;
136515648Sjoerg			(void) close(pfd);
136615648Sjoerg		}
136715648Sjoerg		if (i == 1) {
136815648Sjoerg			if (resp < 0)
136915648Sjoerg				pstatus("waiting for %s to come up", RM);
137015648Sjoerg			else {
137115648Sjoerg				pstatus("waiting for queue to be enabled on %s",
137215648Sjoerg					RM);
137315648Sjoerg				i = 256;
137415648Sjoerg			}
137515648Sjoerg		}
137615648Sjoerg		sleep(i);
137715648Sjoerg	}
137815648Sjoerg	pstatus("sending to %s", RM);
137915648Sjoerg}
138015648Sjoerg
13811553Srgrimesstruct bauds {
13821553Srgrimes	int	baud;
13831553Srgrimes	int	speed;
13841553Srgrimes} bauds[] = {
13851553Srgrimes	50,	B50,
13861553Srgrimes	75,	B75,
13871553Srgrimes	110,	B110,
13881553Srgrimes	134,	B134,
13891553Srgrimes	150,	B150,
13901553Srgrimes	200,	B200,
13911553Srgrimes	300,	B300,
13921553Srgrimes	600,	B600,
13931553Srgrimes	1200,	B1200,
13941553Srgrimes	1800,	B1800,
13951553Srgrimes	2400,	B2400,
13961553Srgrimes	4800,	B4800,
13971553Srgrimes	9600,	B9600,
13981553Srgrimes	19200,	EXTA,
13991553Srgrimes	38400,	EXTB,
14009821Swpaul	57600,	B57600,
14019821Swpaul	115200,	B115200,
14021553Srgrimes	0,	0
14031553Srgrimes};
14041553Srgrimes
14051553Srgrimes/*
14061553Srgrimes * setup tty lines.
14071553Srgrimes */
14081553Srgrimesstatic void
14091553Srgrimessetty()
14101553Srgrimes{
141115032Ssef	struct termios ttybuf;
141215032Ssef	struct bauds *bp;
14131553Srgrimes
14141553Srgrimes	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
14151553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
14161553Srgrimes		exit(1);
14171553Srgrimes	}
141815032Ssef	if (tcgetattr(pfd, &ttybuf) < 0) {
141915032Ssef		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
14201553Srgrimes		exit(1);
14211553Srgrimes	}
14221553Srgrimes	if (BR > 0) {
14231553Srgrimes		for (bp = bauds; bp->baud; bp++)
14241553Srgrimes			if (BR == bp->baud)
14251553Srgrimes				break;
14261553Srgrimes		if (!bp->baud) {
14271553Srgrimes			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
14281553Srgrimes			exit(1);
14291553Srgrimes		}
143015032Ssef		cfsetspeed(&ttybuf, bp->speed);
14311553Srgrimes	}
143215032Ssef	if (MS) {
143315032Ssef		char *s = strdup(MS), *tmp;
143415032Ssef
143515032Ssef		while (tmp = strsep (&s, ",")) {
143615032Ssef			msearch(tmp, &ttybuf);
14371553Srgrimes		}
14381553Srgrimes	}
143915032Ssef	if (MS || (BR > 0)) {
144015032Ssef		if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
144115032Ssef			syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
14421553Srgrimes		}
14431553Srgrimes	}
14441553Srgrimes}
14451553Srgrimes
14461553Srgrimes#if __STDC__
14471553Srgrimes#include <stdarg.h>
14481553Srgrimes#else
14491553Srgrimes#include <varargs.h>
14501553Srgrimes#endif
14511553Srgrimes
145215648Sjoergstatic void
14531553Srgrimes#if __STDC__
14541553Srgrimespstatus(const char *msg, ...)
14551553Srgrimes#else
14561553Srgrimespstatus(msg, va_alist)
14571553Srgrimes	char *msg;
14581553Srgrimes        va_dcl
14591553Srgrimes#endif
14601553Srgrimes{
14611553Srgrimes	register int fd;
14621553Srgrimes	char buf[BUFSIZ];
14631553Srgrimes	va_list ap;
14641553Srgrimes#if __STDC__
14651553Srgrimes	va_start(ap, msg);
14661553Srgrimes#else
14671553Srgrimes	va_start(ap);
14681553Srgrimes#endif
14691553Srgrimes
14701553Srgrimes	umask(0);
14711553Srgrimes	fd = open(ST, O_WRONLY|O_CREAT, 0664);
14721553Srgrimes	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
14731553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
14741553Srgrimes		exit(1);
14751553Srgrimes	}
14761553Srgrimes	ftruncate(fd, 0);
14771553Srgrimes	(void)vsnprintf(buf, sizeof(buf), msg, ap);
14781553Srgrimes	va_end(ap);
14791553Srgrimes	strcat(buf, "\n");
14801553Srgrimes	(void) write(fd, buf, strlen(buf));
14811553Srgrimes	(void) close(fd);
14821553Srgrimes}
1483