printjob.c revision 19202
1/*
2 * Copyright (c) 1983, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by the University of
17 *	California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36static char copyright[] =
37"@(#) Copyright (c) 1983, 1993\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42static char sccsid[] = "@(#)printjob.c	8.7 (Berkeley) 5/10/95";
43#endif /* not lint */
44
45
46/*
47 * printjob -- print jobs in the queue.
48 *
49 *	NOTE: the lock file is used to pass information to lpq and lprm.
50 *	it does not need to be removed because file locks are dynamic.
51 */
52
53#include <sys/param.h>
54#include <sys/wait.h>
55#include <sys/stat.h>
56#include <sys/types.h>
57
58#include <pwd.h>
59#include <unistd.h>
60#include <signal.h>
61#include <syslog.h>
62#include <fcntl.h>
63#include <dirent.h>
64#include <errno.h>
65#include <stdio.h>
66#include <string.h>
67#include <stdlib.h>
68#include <sys/ioctl.h>
69#include <termios.h>
70#include <time.h>
71#include "lp.h"
72#include "lp.local.h"
73#include "pathnames.h"
74#include "extern.h"
75
76#define DORETURN	0	/* absorb fork error */
77#define DOABORT		1	/* abort if dofork fails */
78
79/*
80 * Error tokens
81 */
82#define REPRINT		-2
83#define ERROR		-1
84#define	OK		0
85#define	FATALERR	1
86#define	NOACCT		2
87#define	FILTERERR	3
88#define	ACCESS		4
89
90static dev_t	 fdev;		/* device of file pointed to by symlink */
91static ino_t	 fino;		/* inode of file pointed to by symlink */
92static FILE	*cfp;		/* control file */
93static int	 child;		/* id of any filters */
94static int	 lfd;		/* lock file descriptor */
95static int	 ofd;		/* output filter file descriptor */
96static int	 ofilter;	/* id of output filter, if any */
97static int	 pfd;		/* prstatic inter file descriptor */
98static int	 pid;		/* pid of lpd process */
99static int	 prchild;	/* id of pr process */
100static char	 title[80];	/* ``pr'' title */
101static int	 tof;		/* true if at top of form */
102
103static char	class[32];		/* classification field */
104static char	fromhost[32];		/* user's host machine */
105				/* indentation size in static characters */
106static char	indent[10] = "-i0";
107static char	jobname[100];		/* job or file name */
108static char	length[10] = "-l";	/* page length in lines */
109static char	logname[32];		/* user's login name */
110static char	pxlength[10] = "-y";	/* page length in pixels */
111static char	pxwidth[10] = "-x";	/* page width in pixels */
112static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
113static char	width[10] = "-w";	/* page width in static characters */
114
115static void       abortpr __P((int));
116static void       banner __P((char *, char *));
117static int        dofork __P((int));
118static int        dropit __P((int));
119static void       init __P((void));
120static void       openpr __P((void));
121static void       opennet __P((char *));
122static void       opentty __P((void));
123static void       openrem __P((void));
124static int        print __P((int, char *));
125static int        printit __P((char *));
126static void       pstatus __P((const char *, ...));
127static char       response __P((void));
128static void       scan_out __P((int, char *, int));
129static char      *scnline __P((int, char *, int));
130static int        sendfile __P((int, char *));
131static int        sendit __P((char *));
132static void       sendmail __P((char *, int));
133static void       setty __P((void));
134
135void
136printjob()
137{
138	struct stat stb;
139	register struct queue *q, **qp;
140	struct queue **queue;
141	register int i, nitems;
142	off_t pidoff;
143	int errcnt, count = 0;
144
145	init();					/* set up capabilities */
146	(void) write(1, "", 1);			/* ack that daemon is started */
147	(void) close(2);			/* set up log file */
148	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
149		syslog(LOG_ERR, "%s: %m", LF);
150		(void) open(_PATH_DEVNULL, O_WRONLY);
151	}
152	setgid(getegid());
153	pid = getpid();				/* for use with lprm */
154	setpgrp(0, pid);
155	signal(SIGHUP, abortpr);
156	signal(SIGINT, abortpr);
157	signal(SIGQUIT, abortpr);
158	signal(SIGTERM, abortpr);
159
160	(void) mktemp(tempfile);
161
162	/*
163	 * uses short form file names
164	 */
165	if (chdir(SD) < 0) {
166		syslog(LOG_ERR, "%s: %m", SD);
167		exit(1);
168	}
169	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
170		exit(0);		/* printing disabled */
171	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
172	if (lfd < 0) {
173		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
174		exit(1);
175	}
176	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
177		if (errno == EWOULDBLOCK)	/* active deamon present */
178			exit(0);
179		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
180		exit(1);
181	}
182	ftruncate(lfd, 0);
183	/*
184	 * write process id for others to know
185	 */
186	sprintf(line, "%u\n", pid);
187	pidoff = i = strlen(line);
188	if (write(lfd, line, i) != i) {
189		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
190		exit(1);
191	}
192	/*
193	 * search the spool directory for work and sort by queue order.
194	 */
195	if ((nitems = getq(&queue)) < 0) {
196		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
197		exit(1);
198	}
199	if (nitems == 0)		/* no work to do */
200		exit(0);
201	if (stb.st_mode & 01) {		/* reset queue flag */
202		if (fchmod(lfd, stb.st_mode & 0776) < 0)
203			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
204	}
205	openpr();			/* open printer or remote */
206again:
207	/*
208	 * we found something to do now do it --
209	 *    write the name of the current control file into the lock file
210	 *    so the spool queue program can tell what we're working on
211	 */
212	for (qp = queue; nitems--; free((char *) q)) {
213		q = *qp++;
214		if (stat(q->q_name, &stb) < 0)
215			continue;
216		errcnt = 0;
217	restart:
218		(void) lseek(lfd, pidoff, 0);
219		(void) sprintf(line, "%s\n", q->q_name);
220		i = strlen(line);
221		if (write(lfd, line, i) != i)
222			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
223		if (!remote)
224			i = printit(q->q_name);
225		else
226			i = sendit(q->q_name);
227		/*
228		 * Check to see if we are supposed to stop printing or
229		 * if we are to rebuild the queue.
230		 */
231		if (fstat(lfd, &stb) == 0) {
232			/* stop printing before starting next job? */
233			if (stb.st_mode & 0100)
234				goto done;
235			/* rebuild queue (after lpc topq) */
236			if (stb.st_mode & 01) {
237				for (free((char *) q); nitems--; free((char *) q))
238					q = *qp++;
239				if (fchmod(lfd, stb.st_mode & 0776) < 0)
240					syslog(LOG_WARNING, "%s: %s: %m",
241						printer, LO);
242				break;
243			}
244		}
245		if (i == OK)		/* file ok and printed */
246			count++;
247		else if (i == REPRINT && ++errcnt < 5) {
248			/* try reprinting the job */
249			syslog(LOG_INFO, "restarting %s", printer);
250			if (ofilter > 0) {
251				kill(ofilter, SIGCONT);	/* to be sure */
252				(void) close(ofd);
253				while ((i = wait(NULL)) > 0 && i != ofilter)
254					;
255				ofilter = 0;
256			}
257			(void) close(pfd);	/* close printer */
258			if (ftruncate(lfd, pidoff) < 0)
259				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
260			openpr();		/* try to reopen printer */
261			goto restart;
262		} else {
263			syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
264				remote ? "sent to remote host" : "printed", q->q_name);
265			if (i == REPRINT) {
266				/* insure we don't attempt this job again */
267				(void) unlink(q->q_name);
268				q->q_name[0] = 'd';
269				(void) unlink(q->q_name);
270				if (logname[0])
271					sendmail(logname, FATALERR);
272			}
273		}
274	}
275	free((char *) queue);
276	/*
277	 * search the spool directory for more work.
278	 */
279	if ((nitems = getq(&queue)) < 0) {
280		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
281		exit(1);
282	}
283	if (nitems == 0) {		/* no more work to do */
284	done:
285		if (count > 0) {	/* Files actually printed */
286			if (!SF && !tof)
287				(void) write(ofd, FF, strlen(FF));
288			if (TR != NULL)		/* output trailer */
289				(void) write(ofd, TR, strlen(TR));
290		}
291		(void) close(ofd);
292		(void) wait(NULL);
293		(void) unlink(tempfile);
294		exit(0);
295	}
296	goto again;
297}
298
299char	fonts[4][50];	/* fonts for troff */
300
301char ifonts[4][40] = {
302	_PATH_VFONTR,
303	_PATH_VFONTI,
304	_PATH_VFONTB,
305	_PATH_VFONTS,
306};
307
308/*
309 * The remaining part is the reading of the control file (cf)
310 * and performing the various actions.
311 */
312static int
313printit(file)
314	char *file;
315{
316	register int i;
317	char *cp;
318	int bombed = OK;
319
320	/*
321	 * open control file; ignore if no longer there.
322	 */
323	if ((cfp = fopen(file, "r")) == NULL) {
324		syslog(LOG_INFO, "%s: %s: %m", printer, file);
325		return(OK);
326	}
327	/*
328	 * Reset troff fonts.
329	 */
330	for (i = 0; i < 4; i++)
331		strcpy(fonts[i], ifonts[i]);
332	sprintf(&width[2], "%d", PW);
333	strcpy(indent+2, "0");
334
335	/*
336	 *      read the control file for work to do
337	 *
338	 *      file format -- first character in the line is a command
339	 *      rest of the line is the argument.
340	 *      valid commands are:
341	 *
342	 *		S -- "stat info" for symbolic link protection
343	 *		J -- "job name" on banner page
344	 *		C -- "class name" on banner page
345	 *              L -- "literal" user's name to print on banner
346	 *		T -- "title" for pr
347	 *		H -- "host name" of machine where lpr was done
348	 *              P -- "person" user's login name
349	 *              I -- "indent" amount to indent output
350	 *		R -- laser dpi "resolution"
351	 *              f -- "file name" name of text file to print
352	 *		l -- "file name" text file with control chars
353	 *		p -- "file name" text file to print with pr(1)
354	 *		t -- "file name" troff(1) file to print
355	 *		n -- "file name" ditroff(1) file to print
356	 *		d -- "file name" dvi file to print
357	 *		g -- "file name" plot(1G) file to print
358	 *		v -- "file name" plain raster file to print
359	 *		c -- "file name" cifplot file to print
360	 *		1 -- "R font file" for troff
361	 *		2 -- "I font file" for troff
362	 *		3 -- "B font file" for troff
363	 *		4 -- "S font file" for troff
364	 *		N -- "name" of file (used by lpq)
365	 *              U -- "unlink" name of file to remove
366	 *                    (after we print it. (Pass 2 only)).
367	 *		M -- "mail" to user when done printing
368	 *
369	 *      getline reads a line and expands tabs to blanks
370	 */
371
372	/* pass 1 */
373
374	while (getline(cfp))
375		switch (line[0]) {
376		case 'H':
377			strcpy(fromhost, line+1);
378			if (class[0] == '\0')
379				strncpy(class, line+1, sizeof(class)-1);
380			continue;
381
382		case 'P':
383			strncpy(logname, line+1, sizeof(logname)-1);
384			if (RS) {			/* restricted */
385				if (getpwnam(logname) == NULL) {
386					bombed = NOACCT;
387					sendmail(line+1, bombed);
388					goto pass2;
389				}
390			}
391			continue;
392
393		case 'S':
394			cp = line+1;
395			i = 0;
396			while (*cp >= '0' && *cp <= '9')
397				i = i * 10 + (*cp++ - '0');
398			fdev = i;
399			cp++;
400			i = 0;
401			while (*cp >= '0' && *cp <= '9')
402				i = i * 10 + (*cp++ - '0');
403			fino = i;
404			continue;
405
406		case 'J':
407			if (line[1] != '\0')
408				strncpy(jobname, line+1, sizeof(jobname)-1);
409			else
410				strcpy(jobname, " ");
411			continue;
412
413		case 'C':
414			if (line[1] != '\0')
415				strncpy(class, line+1, sizeof(class)-1);
416			else if (class[0] == '\0')
417				gethostname(class, sizeof(class));
418			continue;
419
420		case 'T':	/* header title for pr */
421			strncpy(title, line+1, sizeof(title)-1);
422			continue;
423
424		case 'L':	/* identification line */
425			if (!SH && !HL)
426				banner(line+1, jobname);
427			continue;
428
429		case '1':	/* troff fonts */
430		case '2':
431		case '3':
432		case '4':
433			if (line[1] != '\0')
434				strcpy(fonts[line[0]-'1'], line+1);
435			continue;
436
437		case 'W':	/* page width */
438			strncpy(width+2, line+1, sizeof(width)-3);
439			continue;
440
441		case 'I':	/* indent amount */
442			strncpy(indent+2, line+1, sizeof(indent)-3);
443			continue;
444
445		default:	/* some file to print */
446			switch (i = print(line[0], line+1)) {
447			case ERROR:
448				if (bombed == OK)
449					bombed = FATALERR;
450				break;
451			case REPRINT:
452				(void) fclose(cfp);
453				return(REPRINT);
454			case FILTERERR:
455			case ACCESS:
456				bombed = i;
457				sendmail(logname, bombed);
458			}
459			title[0] = '\0';
460			continue;
461
462		case 'N':
463		case 'U':
464		case 'M':
465		case 'R':
466			continue;
467		}
468
469	/* pass 2 */
470
471pass2:
472	fseek(cfp, 0L, 0);
473	while (getline(cfp))
474		switch (line[0]) {
475		case 'L':	/* identification line */
476			if (!SH && HL)
477				banner(line+1, jobname);
478			continue;
479
480		case 'M':
481			if (bombed < NOACCT)	/* already sent if >= NOACCT */
482				sendmail(line+1, bombed);
483			continue;
484
485		case 'U':
486			(void) unlink(line+1);
487		}
488	/*
489	 * clean-up in case another control file exists
490	 */
491	(void) fclose(cfp);
492	(void) unlink(file);
493	return(bombed == OK ? OK : ERROR);
494}
495
496/*
497 * Print a file.
498 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
499 * Return -1 if a non-recoverable error occured,
500 * 2 if the filter detected some errors (but printed the job anyway),
501 * 1 if we should try to reprint this job and
502 * 0 if all is well.
503 * Note: all filters take stdin as the file, stdout as the printer,
504 * stderr as the log file, and must not ignore SIGINT.
505 */
506static int
507print(format, file)
508	int format;
509	char *file;
510{
511	register int n;
512	register char *prog;
513	int dtablesize, fi, fo;
514	FILE *fp;
515	char *av[15], buf[BUFSIZ];
516	int pid, p[2], stopped = 0;
517	union wait status;
518	struct stat stb;
519
520	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
521		return(ERROR);
522	/*
523	 * Check to see if data file is a symbolic link. If so, it should
524	 * still point to the same file or someone is trying to print
525	 * something he shouldn't.
526	 */
527	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
528	    (stb.st_dev != fdev || stb.st_ino != fino))
529		return(ACCESS);
530	if (!SF && !tof) {		/* start on a fresh page */
531		(void) write(ofd, FF, strlen(FF));
532		tof = 1;
533	}
534	if (IF == NULL && (format == 'f' || format == 'l')) {
535		tof = 0;
536		while ((n = read(fi, buf, BUFSIZ)) > 0)
537			if (write(ofd, buf, n) != n) {
538				(void) close(fi);
539				return(REPRINT);
540			}
541		(void) close(fi);
542		return(OK);
543	}
544	switch (format) {
545	case 'p':	/* print file using 'pr' */
546		if (IF == NULL) {	/* use output filter */
547			prog = _PATH_PR;
548			av[0] = "pr";
549			av[1] = width;
550			av[2] = length;
551			av[3] = "-h";
552			av[4] = *title ? title : " ";
553			av[5] = "-F";
554			av[6] = 0;
555			fo = ofd;
556			goto start;
557		}
558		pipe(p);
559		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
560			dup2(fi, 0);		/* file is stdin */
561			dup2(p[1], 1);		/* pipe is stdout */
562			closelog();
563			for (n = 3, dtablesize = getdtablesize();
564			     n < dtablesize; n++)
565				(void) close(n);
566			execl(_PATH_PR, "pr", width, length,
567			    "-h", *title ? title : " ", "-F", 0);
568			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
569			exit(2);
570		}
571		(void) close(p[1]);		/* close output side */
572		(void) close(fi);
573		if (prchild < 0) {
574			prchild = 0;
575			(void) close(p[0]);
576			return(ERROR);
577		}
578		fi = p[0];			/* use pipe for input */
579	case 'f':	/* print plain text file */
580		prog = IF;
581		av[1] = width;
582		av[2] = length;
583		av[3] = indent;
584		n = 4;
585		break;
586	case 'l':	/* like 'f' but pass control characters */
587		prog = IF;
588		av[1] = "-c";
589		av[2] = width;
590		av[3] = length;
591		av[4] = indent;
592		n = 5;
593		break;
594	case 'r':	/* print a fortran text file */
595		prog = RF;
596		av[1] = width;
597		av[2] = length;
598		n = 3;
599		break;
600	case 't':	/* print troff output */
601	case 'n':	/* print ditroff output */
602	case 'd':	/* print tex output */
603		(void) unlink(".railmag");
604		if ((fo = creat(".railmag", FILMOD)) < 0) {
605			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
606			(void) unlink(".railmag");
607		} else {
608			for (n = 0; n < 4; n++) {
609				if (fonts[n][0] != '/')
610					(void) write(fo, _PATH_VFONT,
611					    sizeof(_PATH_VFONT) - 1);
612				(void) write(fo, fonts[n], strlen(fonts[n]));
613				(void) write(fo, "\n", 1);
614			}
615			(void) close(fo);
616		}
617		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
618		av[1] = pxwidth;
619		av[2] = pxlength;
620		n = 3;
621		break;
622	case 'c':	/* print cifplot output */
623		prog = CF;
624		av[1] = pxwidth;
625		av[2] = pxlength;
626		n = 3;
627		break;
628	case 'g':	/* print plot(1G) output */
629		prog = GF;
630		av[1] = pxwidth;
631		av[2] = pxlength;
632		n = 3;
633		break;
634	case 'v':	/* print raster output */
635		prog = VF;
636		av[1] = pxwidth;
637		av[2] = pxlength;
638		n = 3;
639		break;
640	default:
641		(void) close(fi);
642		syslog(LOG_ERR, "%s: illegal format character '%c'",
643			printer, format);
644		return(ERROR);
645	}
646	if (prog == NULL) {
647		(void) close(fi);
648		syslog(LOG_ERR,
649		   "%s: no filter found in printcap for format character '%c'",
650		   printer, format);
651		return(ERROR);
652	}
653	if ((av[0] = rindex(prog, '/')) != NULL)
654		av[0]++;
655	else
656		av[0] = prog;
657	av[n++] = "-n";
658	av[n++] = logname;
659	av[n++] = "-h";
660	av[n++] = fromhost;
661	av[n++] = AF;
662	av[n] = 0;
663	fo = pfd;
664	if (ofilter > 0) {		/* stop output filter */
665		write(ofd, "\031\1", 2);
666		while ((pid =
667		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
668			;
669		if (status.w_stopval != WSTOPPED) {
670			(void) close(fi);
671			syslog(LOG_WARNING,
672				"%s: output filter died (retcode=%d termsig=%d)",
673				printer, status.w_retcode, status.w_termsig);
674			return(REPRINT);
675		}
676		stopped++;
677	}
678start:
679	if ((child = dofork(DORETURN)) == 0) {	/* child */
680		dup2(fi, 0);
681		dup2(fo, 1);
682		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
683		if (n >= 0)
684			dup2(n, 2);
685		closelog();
686		for (n = 3, dtablesize = getdtablesize(); n < dtablesize; n++)
687			(void) close(n);
688		execv(prog, av);
689		syslog(LOG_ERR, "cannot execv %s", prog);
690		exit(2);
691	}
692	(void) close(fi);
693	if (child < 0)
694		status.w_retcode = 100;
695	else
696		while ((pid = wait((int *)&status)) > 0 && pid != child)
697			;
698	child = 0;
699	prchild = 0;
700	if (stopped) {		/* restart output filter */
701		if (kill(ofilter, SIGCONT) < 0) {
702			syslog(LOG_ERR, "cannot restart output filter");
703			exit(1);
704		}
705	}
706	tof = 0;
707
708	/* Copy filter output to "lf" logfile */
709	if (fp = fopen(tempfile, "r")) {
710		while (fgets(buf, sizeof(buf), fp))
711			fputs(buf, stderr);
712		fclose(fp);
713	}
714
715	if (!WIFEXITED(status)) {
716		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
717			printer, format, status.w_termsig);
718		return(ERROR);
719	}
720	switch (status.w_retcode) {
721	case 0:
722		tof = 1;
723		return(OK);
724	case 1:
725		return(REPRINT);
726	case 2:
727		return(ERROR);
728	default:
729		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
730			printer, format, status.w_retcode);
731		return(FILTERERR);
732	}
733}
734
735/*
736 * Send the daemon control file (cf) and any data files.
737 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
738 * 0 if all is well.
739 */
740static int
741sendit(file)
742	char *file;
743{
744	register int i, err = OK;
745	char *cp, last[BUFSIZ];
746
747	/*
748	 * open control file
749	 */
750	if ((cfp = fopen(file, "r")) == NULL)
751		return(OK);
752	/*
753	 *      read the control file for work to do
754	 *
755	 *      file format -- first character in the line is a command
756	 *      rest of the line is the argument.
757	 *      commands of interest are:
758	 *
759	 *            a-z -- "file name" name of file to print
760	 *              U -- "unlink" name of file to remove
761	 *                    (after we print it. (Pass 2 only)).
762	 */
763
764	/*
765	 * pass 1
766	 */
767	while (getline(cfp)) {
768	again:
769		if (line[0] == 'S') {
770			cp = line+1;
771			i = 0;
772			while (*cp >= '0' && *cp <= '9')
773				i = i * 10 + (*cp++ - '0');
774			fdev = i;
775			cp++;
776			i = 0;
777			while (*cp >= '0' && *cp <= '9')
778				i = i * 10 + (*cp++ - '0');
779			fino = i;
780			continue;
781		}
782		if (line[0] >= 'a' && line[0] <= 'z') {
783			strcpy(last, line);
784			while (i = getline(cfp))
785				if (strcmp(last, line))
786					break;
787			switch (sendfile('\3', last+1)) {
788			case OK:
789				if (i)
790					goto again;
791				break;
792			case REPRINT:
793				(void) fclose(cfp);
794				return(REPRINT);
795			case ACCESS:
796				sendmail(logname, ACCESS);
797			case ERROR:
798				err = ERROR;
799			}
800			break;
801		}
802	}
803	if (err == OK && sendfile('\2', file) > 0) {
804		(void) fclose(cfp);
805		return(REPRINT);
806	}
807	/*
808	 * pass 2
809	 */
810	fseek(cfp, 0L, 0);
811	while (getline(cfp))
812		if (line[0] == 'U')
813			(void) unlink(line+1);
814	/*
815	 * clean-up in case another control file exists
816	 */
817	(void) fclose(cfp);
818	(void) unlink(file);
819	return(err);
820}
821
822/*
823 * Send a data file to the remote machine and spool it.
824 * Return positive if we should try resending.
825 */
826static int
827sendfile(type, file)
828	int type;
829	char *file;
830{
831	register int f, i, amt;
832	struct stat stb;
833	char buf[BUFSIZ];
834	int sizerr, resp;
835
836	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
837		return(ERROR);
838	/*
839	 * Check to see if data file is a symbolic link. If so, it should
840	 * still point to the same file or someone is trying to print something
841	 * he shouldn't.
842	 */
843	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
844	    (stb.st_dev != fdev || stb.st_ino != fino))
845		return(ACCESS);
846	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
847	amt = strlen(buf);
848	for (i = 0;  ; i++) {
849		if (write(pfd, buf, amt) != amt ||
850		    (resp = response()) < 0 || resp == '\1') {
851			(void) close(f);
852			return(REPRINT);
853		} else if (resp == '\0')
854			break;
855		if (i == 0)
856			pstatus("no space on remote; waiting for queue to drain");
857		if (i == 10)
858			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
859				printer, RM);
860		sleep(5 * 60);
861	}
862	if (i)
863		pstatus("sending to %s", RM);
864	sizerr = 0;
865	for (i = 0; i < stb.st_size; i += BUFSIZ) {
866		amt = BUFSIZ;
867		if (i + amt > stb.st_size)
868			amt = stb.st_size - i;
869		if (sizerr == 0 && read(f, buf, amt) != amt)
870			sizerr = 1;
871		if (write(pfd, buf, amt) != amt) {
872			(void) close(f);
873			return(REPRINT);
874		}
875	}
876
877
878
879
880	(void) close(f);
881	if (sizerr) {
882		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
883		/* tell recvjob to ignore this file */
884		(void) write(pfd, "\1", 1);
885		return(ERROR);
886	}
887	if (write(pfd, "", 1) != 1 || response())
888		return(REPRINT);
889	return(OK);
890}
891
892/*
893 * Check to make sure there have been no errors and that both programs
894 * are in sync with eachother.
895 * Return non-zero if the connection was lost.
896 */
897static char
898response()
899{
900	char resp;
901
902	if (read(pfd, &resp, 1) != 1) {
903		syslog(LOG_INFO, "%s: lost connection", printer);
904		return(-1);
905	}
906	return(resp);
907}
908
909/*
910 * Banner printing stuff
911 */
912static void
913banner(name1, name2)
914	char *name1, *name2;
915{
916	time_t tvec;
917
918	time(&tvec);
919	if (!SF && !tof)
920		(void) write(ofd, FF, strlen(FF));
921	if (SB) {	/* short banner only */
922		if (class[0]) {
923			(void) write(ofd, class, strlen(class));
924			(void) write(ofd, ":", 1);
925		}
926		(void) write(ofd, name1, strlen(name1));
927		(void) write(ofd, "  Job: ", 7);
928		(void) write(ofd, name2, strlen(name2));
929		(void) write(ofd, "  Date: ", 8);
930		(void) write(ofd, ctime(&tvec), 24);
931		(void) write(ofd, "\n", 1);
932	} else {	/* normal banner */
933		(void) write(ofd, "\n\n\n", 3);
934		scan_out(ofd, name1, '\0');
935		(void) write(ofd, "\n\n", 2);
936		scan_out(ofd, name2, '\0');
937		if (class[0]) {
938			(void) write(ofd,"\n\n\n",3);
939			scan_out(ofd, class, '\0');
940		}
941		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
942		(void) write(ofd, name2, strlen(name2));
943		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
944		(void) write(ofd, ctime(&tvec), 24);
945		(void) write(ofd, "\n", 1);
946	}
947	if (!SF)
948		(void) write(ofd, FF, strlen(FF));
949	tof = 1;
950}
951
952static char *
953scnline(key, p, c)
954	register int key;
955	register char *p;
956	int c;
957{
958	register scnwidth;
959
960	for (scnwidth = WIDTH; --scnwidth;) {
961		key <<= 1;
962		*p++ = key & 0200 ? c : BACKGND;
963	}
964	return (p);
965}
966
967#define TRC(q)	(((q)-' ')&0177)
968
969static void
970scan_out(scfd, scsp, dlm)
971	int scfd, dlm;
972	char *scsp;
973{
974	register char *strp;
975	register nchrs, j;
976	char outbuf[LINELEN+1], *sp, c, cc;
977	int d, scnhgt;
978
979	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
980		strp = &outbuf[0];
981		sp = scsp;
982		for (nchrs = 0; ; ) {
983			d = dropit(c = TRC(cc = *sp++));
984			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
985				for (j = WIDTH; --j;)
986					*strp++ = BACKGND;
987			else
988				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
989			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
990				break;
991			*strp++ = BACKGND;
992			*strp++ = BACKGND;
993		}
994		while (*--strp == BACKGND && strp >= outbuf)
995			;
996		strp++;
997		*strp++ = '\n';
998		(void) write(scfd, outbuf, strp-outbuf);
999	}
1000}
1001
1002static int
1003dropit(c)
1004	int c;
1005{
1006	switch(c) {
1007
1008	case TRC('_'):
1009	case TRC(';'):
1010	case TRC(','):
1011	case TRC('g'):
1012	case TRC('j'):
1013	case TRC('p'):
1014	case TRC('q'):
1015	case TRC('y'):
1016		return (DROP);
1017
1018	default:
1019		return (0);
1020	}
1021}
1022
1023/*
1024 * sendmail ---
1025 *   tell people about job completion
1026 */
1027static void
1028sendmail(user, bombed)
1029	char *user;
1030	int bombed;
1031{
1032	register int i;
1033	int dtablesize;
1034	int p[2], s;
1035	register char *cp;
1036	char buf[100];
1037	struct stat stb;
1038	FILE *fp;
1039
1040	pipe(p);
1041	if ((s = dofork(DORETURN)) == 0) {		/* child */
1042		dup2(p[0], 0);
1043		closelog();
1044		for (i = 3, dtablesize = getdtablesize(); i < dtablesize; i++)
1045			(void) close(i);
1046		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1047			cp++;
1048	else
1049			cp = _PATH_SENDMAIL;
1050		sprintf(buf, "%s@%s", user, fromhost);
1051		execl(_PATH_SENDMAIL, cp, buf, 0);
1052		exit(0);
1053	} else if (s > 0) {				/* parent */
1054		dup2(p[1], 1);
1055		printf("To: %s@%s\n", user, fromhost);
1056		printf("Subject: %s printer job \"%s\"\n", printer,
1057			*jobname ? jobname : "<unknown>");
1058		printf("Reply-To: root@%s\n\n", host);
1059		printf("Your printer job ");
1060		if (*jobname)
1061			printf("(%s) ", jobname);
1062		switch (bombed) {
1063		case OK:
1064			printf("\ncompleted successfully\n");
1065			cp = "OK";
1066			break;
1067		default:
1068		case FATALERR:
1069			printf("\ncould not be printed\n");
1070			cp = "FATALERR";
1071			break;
1072		case NOACCT:
1073			printf("\ncould not be printed without an account on %s\n", host);
1074			cp = "NOACCT";
1075			break;
1076		case FILTERERR:
1077			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1078			    (fp = fopen(tempfile, "r")) == NULL) {
1079				printf("\nhad some errors and may not have printed\n");
1080				break;
1081			}
1082			printf("\nhad the following errors and may not have printed:\n");
1083			while ((i = getc(fp)) != EOF)
1084				putchar(i);
1085			(void) fclose(fp);
1086			cp = "FILTERERR";
1087			break;
1088		case ACCESS:
1089			printf("\nwas not printed because it was not linked to the original file\n");
1090			cp = "ACCESS";
1091		}
1092		fflush(stdout);
1093		(void) close(1);
1094	}
1095	(void) close(p[0]);
1096	(void) close(p[1]);
1097	wait(NULL);
1098	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
1099		user, *jobname ? jobname : "<unknown>", printer, cp);
1100}
1101
1102/*
1103 * dofork - fork with retries on failure
1104 */
1105static int
1106dofork(action)
1107	int action;
1108{
1109	register int i, pid;
1110
1111	for (i = 0; i < 20; i++) {
1112		if ((pid = fork()) < 0) {
1113			sleep((unsigned)(i*i));
1114			continue;
1115		}
1116		/*
1117		 * Child should run as daemon instead of root
1118		 */
1119		if (pid == 0)
1120			setuid(DU);
1121		return(pid);
1122	}
1123	syslog(LOG_ERR, "can't fork");
1124
1125	switch (action) {
1126	case DORETURN:
1127		return (-1);
1128	default:
1129		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1130		/*FALL THRU*/
1131	case DOABORT:
1132		exit(1);
1133	}
1134	/*NOTREACHED*/
1135}
1136
1137/*
1138 * Kill child processes to abort current job.
1139 */
1140static void
1141abortpr(signo)
1142	int signo;
1143{
1144	(void) unlink(tempfile);
1145	kill(0, SIGINT);
1146	if (ofilter > 0)
1147		kill(ofilter, SIGCONT);
1148	while (wait(NULL) > 0)
1149		;
1150	exit(0);
1151}
1152
1153static void
1154init()
1155{
1156	int status;
1157	char *s;
1158
1159	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1160		syslog(LOG_ERR, "can't open printer description file");
1161		exit(1);
1162	} else if (status == -1) {
1163		syslog(LOG_ERR, "unknown printer: %s", printer);
1164		exit(1);
1165	} else if (status == -3)
1166		fatal("potential reference loop detected in printcap file");
1167
1168	if (cgetstr(bp, "lp", &LP) == -1)
1169		LP = _PATH_DEFDEVLP;
1170	if (cgetstr(bp, "rp", &RP) == -1)
1171		RP = DEFLP;
1172	if (cgetstr(bp, "lo", &LO) == -1)
1173		LO = DEFLOCK;
1174	if (cgetstr(bp, "st", &ST) == -1)
1175		ST = DEFSTAT;
1176	if (cgetstr(bp, "lf", &LF) == -1)
1177		LF = _PATH_CONSOLE;
1178	if (cgetstr(bp, "sd", &SD) == -1)
1179		SD = _PATH_DEFSPOOL;
1180	if (cgetnum(bp, "du", &DU) < 0)
1181		DU = DEFUID;
1182	if (cgetstr(bp,"ff", &FF) == -1)
1183		FF = DEFFF;
1184	if (cgetnum(bp, "pw", &PW) < 0)
1185		PW = DEFWIDTH;
1186	sprintf(&width[2], "%d", PW);
1187	if (cgetnum(bp, "pl", &PL) < 0)
1188		PL = DEFLENGTH;
1189	sprintf(&length[2], "%d", PL);
1190	if (cgetnum(bp,"px", &PX) < 0)
1191		PX = 0;
1192	sprintf(&pxwidth[2], "%d", PX);
1193	if (cgetnum(bp, "py", &PY) < 0)
1194		PY = 0;
1195	sprintf(&pxlength[2], "%d", PY);
1196	cgetstr(bp, "rm", &RM);
1197	if (s = checkremote())
1198		syslog(LOG_WARNING, s);
1199
1200	cgetstr(bp, "af", &AF);
1201	cgetstr(bp, "of", &OF);
1202	cgetstr(bp, "if", &IF);
1203	cgetstr(bp, "rf", &RF);
1204	cgetstr(bp, "tf", &TF);
1205	cgetstr(bp, "nf", &NF);
1206	cgetstr(bp, "df", &DF);
1207	cgetstr(bp, "gf", &GF);
1208	cgetstr(bp, "vf", &VF);
1209	cgetstr(bp, "cf", &CF);
1210	cgetstr(bp, "tr", &TR);
1211	cgetstr(bp, "ms", &MS);
1212
1213	RS = (cgetcap(bp, "rs", ':') != NULL);
1214	SF = (cgetcap(bp, "sf", ':') != NULL);
1215	SH = (cgetcap(bp, "sh", ':') != NULL);
1216	SB = (cgetcap(bp, "sb", ':') != NULL);
1217	HL = (cgetcap(bp, "hl", ':') != NULL);
1218	RW = (cgetcap(bp, "rw", ':') != NULL);
1219
1220	cgetnum(bp, "br", &BR);
1221
1222	tof = (cgetcap(bp, "fo", ':') == NULL);
1223}
1224
1225/*
1226 * Acquire line printer or remote connection.
1227 */
1228static void
1229openpr()
1230{
1231	register int i;
1232	int dtablesize;
1233	char *cp;
1234
1235	if (!remote && *LP) {
1236		if (cp = index(LP, '@'))
1237			opennet(cp);
1238		else
1239			opentty();
1240	} else if (remote) {
1241		openrem();
1242	} else {
1243		syslog(LOG_ERR, "%s: no line printer device or host name",
1244			printer);
1245		exit(1);
1246	}
1247
1248	/*
1249	 * Start up an output filter, if needed.
1250	 */
1251	if (!remote && OF) {
1252		int p[2];
1253
1254		pipe(p);
1255		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1256			dup2(p[0], 0);		/* pipe is std in */
1257			dup2(pfd, 1);		/* printer is std out */
1258			closelog();
1259			for (i = 3, dtablesize = getdtablesize();
1260			     i < dtablesize; i++)
1261				(void) close(i);
1262			if ((cp = rindex(OF, '/')) == NULL)
1263				cp = OF;
1264			else
1265				cp++;
1266			execl(OF, cp, width, length, 0);
1267			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1268			exit(1);
1269		}
1270		(void) close(p[0]);		/* close input side */
1271		ofd = p[1];			/* use pipe for output */
1272	} else {
1273		ofd = pfd;
1274		ofilter = 0;
1275	}
1276}
1277
1278/*
1279 * Printer connected directly to the network
1280 * or to a terminal server on the net
1281 */
1282static void
1283opennet(cp)
1284	char *cp;
1285{
1286	register int i;
1287	int resp, port;
1288	char save_ch;
1289
1290	save_ch = *cp;
1291	*cp = '\0';
1292	port = atoi(LP);
1293	if (port <= 0) {
1294		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
1295		exit(1);
1296	}
1297	*cp++ = save_ch;
1298
1299	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1300		resp = -1;
1301		pfd = getport(cp, port);
1302		if (pfd < 0 && errno == ECONNREFUSED)
1303			resp = 1;
1304		else if (pfd >= 0) {
1305			/*
1306			 * need to delay a bit for rs232 lines
1307			 * to stabilize in case printer is
1308			 * connected via a terminal server
1309			 */
1310			delay(500);
1311			break;
1312		}
1313		if (i == 1) {
1314		   if (resp < 0)
1315			pstatus("waiting for %s to come up", LP);
1316		   else
1317			pstatus("waiting for access to printer on %s", LP);
1318		}
1319		sleep(i);
1320	}
1321	pstatus("sending to %s port %d", cp, port);
1322}
1323
1324/*
1325 * Printer is connected to an RS232 port on this host
1326 */
1327static void
1328opentty()
1329{
1330	register int i;
1331	int resp, port;
1332
1333	for (i = 1; ; i = i < 32 ? i << 1 : i) {
1334		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1335		if (pfd >= 0) {
1336			delay(500);
1337			break;
1338		}
1339		if (errno == ENOENT) {
1340			syslog(LOG_ERR, "%s: %m", LP);
1341			exit(1);
1342		}
1343		if (i == 1)
1344			pstatus("waiting for %s to become ready (offline ?)",
1345				printer);
1346		sleep(i);
1347	}
1348	if (isatty(pfd))
1349		setty();
1350	pstatus("%s is ready and printing", printer);
1351}
1352
1353/*
1354 * Printer is on a remote host
1355 */
1356static void
1357openrem()
1358{
1359	register int i, n;
1360	int resp, port;
1361
1362	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1363		resp = -1;
1364		pfd = getport(RM, 0);
1365		if (pfd >= 0) {
1366			(void) sprintf(line, "\2%s\n", RP);
1367			n = strlen(line);
1368			if (write(pfd, line, n) == n &&
1369			    (resp = response()) == '\0')
1370				break;
1371			(void) close(pfd);
1372		}
1373		if (i == 1) {
1374			if (resp < 0)
1375				pstatus("waiting for %s to come up", RM);
1376			else {
1377				pstatus("waiting for queue to be enabled on %s",
1378					RM);
1379				i = 256;
1380			}
1381		}
1382		sleep(i);
1383	}
1384	pstatus("sending to %s", RM);
1385}
1386
1387struct bauds {
1388	int	baud;
1389	int	speed;
1390} bauds[] = {
1391	50,	B50,
1392	75,	B75,
1393	110,	B110,
1394	134,	B134,
1395	150,	B150,
1396	200,	B200,
1397	300,	B300,
1398	600,	B600,
1399	1200,	B1200,
1400	1800,	B1800,
1401	2400,	B2400,
1402	4800,	B4800,
1403	9600,	B9600,
1404	19200,	EXTA,
1405	38400,	EXTB,
1406	57600,	B57600,
1407	115200,	B115200,
1408	0,	0
1409};
1410
1411/*
1412 * setup tty lines.
1413 */
1414static void
1415setty()
1416{
1417	struct termios ttybuf;
1418	struct bauds *bp;
1419
1420	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1421		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1422		exit(1);
1423	}
1424	if (tcgetattr(pfd, &ttybuf) < 0) {
1425		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
1426		exit(1);
1427	}
1428	if (BR > 0) {
1429		for (bp = bauds; bp->baud; bp++)
1430			if (BR == bp->baud)
1431				break;
1432		if (!bp->baud) {
1433			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1434			exit(1);
1435		}
1436		cfsetspeed(&ttybuf, bp->speed);
1437	}
1438	if (MS) {
1439		char *s = strdup(MS), *tmp;
1440
1441		while (tmp = strsep (&s, ",")) {
1442			msearch(tmp, &ttybuf);
1443		}
1444	}
1445	if (MS || (BR > 0)) {
1446		if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
1447			syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
1448		}
1449	}
1450}
1451
1452#if __STDC__
1453#include <stdarg.h>
1454#else
1455#include <varargs.h>
1456#endif
1457
1458static void
1459#if __STDC__
1460pstatus(const char *msg, ...)
1461#else
1462pstatus(msg, va_alist)
1463	char *msg;
1464        va_dcl
1465#endif
1466{
1467	register int fd;
1468	char buf[BUFSIZ];
1469	va_list ap;
1470#if __STDC__
1471	va_start(ap, msg);
1472#else
1473	va_start(ap);
1474#endif
1475
1476	umask(0);
1477	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1478	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1479		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1480		exit(1);
1481	}
1482	ftruncate(fd, 0);
1483	(void)vsnprintf(buf, sizeof(buf), msg, ap);
1484	va_end(ap);
1485	strcat(buf, "\n");
1486	(void) write(fd, buf, strlen(buf));
1487	(void) close(fd);
1488}
1489