printjob.c revision 18569
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) unlink(tempfile);
292		exit(0);
293	}
294	goto again;
295}
296
297char	fonts[4][50];	/* fonts for troff */
298
299char ifonts[4][40] = {
300	_PATH_VFONTR,
301	_PATH_VFONTI,
302	_PATH_VFONTB,
303	_PATH_VFONTS,
304};
305
306/*
307 * The remaining part is the reading of the control file (cf)
308 * and performing the various actions.
309 */
310static int
311printit(file)
312	char *file;
313{
314	register int i;
315	char *cp;
316	int bombed = OK;
317
318	/*
319	 * open control file; ignore if no longer there.
320	 */
321	if ((cfp = fopen(file, "r")) == NULL) {
322		syslog(LOG_INFO, "%s: %s: %m", printer, file);
323		return(OK);
324	}
325	/*
326	 * Reset troff fonts.
327	 */
328	for (i = 0; i < 4; i++)
329		strcpy(fonts[i], ifonts[i]);
330	sprintf(&width[2], "%d", PW);
331	strcpy(indent+2, "0");
332
333	/*
334	 *      read the control file for work to do
335	 *
336	 *      file format -- first character in the line is a command
337	 *      rest of the line is the argument.
338	 *      valid commands are:
339	 *
340	 *		S -- "stat info" for symbolic link protection
341	 *		J -- "job name" on banner page
342	 *		C -- "class name" on banner page
343	 *              L -- "literal" user's name to print on banner
344	 *		T -- "title" for pr
345	 *		H -- "host name" of machine where lpr was done
346	 *              P -- "person" user's login name
347	 *              I -- "indent" amount to indent output
348	 *		R -- laser dpi "resolution"
349	 *              f -- "file name" name of text file to print
350	 *		l -- "file name" text file with control chars
351	 *		p -- "file name" text file to print with pr(1)
352	 *		t -- "file name" troff(1) file to print
353	 *		n -- "file name" ditroff(1) file to print
354	 *		d -- "file name" dvi file to print
355	 *		g -- "file name" plot(1G) file to print
356	 *		v -- "file name" plain raster file to print
357	 *		c -- "file name" cifplot file to print
358	 *		1 -- "R font file" for troff
359	 *		2 -- "I font file" for troff
360	 *		3 -- "B font file" for troff
361	 *		4 -- "S font file" for troff
362	 *		N -- "name" of file (used by lpq)
363	 *              U -- "unlink" name of file to remove
364	 *                    (after we print it. (Pass 2 only)).
365	 *		M -- "mail" to user when done printing
366	 *
367	 *      getline reads a line and expands tabs to blanks
368	 */
369
370	/* pass 1 */
371
372	while (getline(cfp))
373		switch (line[0]) {
374		case 'H':
375			strcpy(fromhost, line+1);
376			if (class[0] == '\0')
377				strncpy(class, line+1, sizeof(class)-1);
378			continue;
379
380		case 'P':
381			strncpy(logname, line+1, sizeof(logname)-1);
382			if (RS) {			/* restricted */
383				if (getpwnam(logname) == NULL) {
384					bombed = NOACCT;
385					sendmail(line+1, bombed);
386					goto pass2;
387				}
388			}
389			continue;
390
391		case 'S':
392			cp = line+1;
393			i = 0;
394			while (*cp >= '0' && *cp <= '9')
395				i = i * 10 + (*cp++ - '0');
396			fdev = i;
397			cp++;
398			i = 0;
399			while (*cp >= '0' && *cp <= '9')
400				i = i * 10 + (*cp++ - '0');
401			fino = i;
402			continue;
403
404		case 'J':
405			if (line[1] != '\0')
406				strncpy(jobname, line+1, sizeof(jobname)-1);
407			else
408				strcpy(jobname, " ");
409			continue;
410
411		case 'C':
412			if (line[1] != '\0')
413				strncpy(class, line+1, sizeof(class)-1);
414			else if (class[0] == '\0')
415				gethostname(class, sizeof(class));
416			continue;
417
418		case 'T':	/* header title for pr */
419			strncpy(title, line+1, sizeof(title)-1);
420			continue;
421
422		case 'L':	/* identification line */
423			if (!SH && !HL)
424				banner(line+1, jobname);
425			continue;
426
427		case '1':	/* troff fonts */
428		case '2':
429		case '3':
430		case '4':
431			if (line[1] != '\0')
432				strcpy(fonts[line[0]-'1'], line+1);
433			continue;
434
435		case 'W':	/* page width */
436			strncpy(width+2, line+1, sizeof(width)-3);
437			continue;
438
439		case 'I':	/* indent amount */
440			strncpy(indent+2, line+1, sizeof(indent)-3);
441			continue;
442
443		default:	/* some file to print */
444			switch (i = print(line[0], line+1)) {
445			case ERROR:
446				if (bombed == OK)
447					bombed = FATALERR;
448				break;
449			case REPRINT:
450				(void) fclose(cfp);
451				return(REPRINT);
452			case FILTERERR:
453			case ACCESS:
454				bombed = i;
455				sendmail(logname, bombed);
456			}
457			title[0] = '\0';
458			continue;
459
460		case 'N':
461		case 'U':
462		case 'M':
463		case 'R':
464			continue;
465		}
466
467	/* pass 2 */
468
469pass2:
470	fseek(cfp, 0L, 0);
471	while (getline(cfp))
472		switch (line[0]) {
473		case 'L':	/* identification line */
474			if (!SH && HL)
475				banner(line+1, jobname);
476			continue;
477
478		case 'M':
479			if (bombed < NOACCT)	/* already sent if >= NOACCT */
480				sendmail(line+1, bombed);
481			continue;
482
483		case 'U':
484			(void) unlink(line+1);
485		}
486	/*
487	 * clean-up in case another control file exists
488	 */
489	(void) fclose(cfp);
490	(void) unlink(file);
491	return(bombed == OK ? OK : ERROR);
492}
493
494/*
495 * Print a file.
496 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
497 * Return -1 if a non-recoverable error occured,
498 * 2 if the filter detected some errors (but printed the job anyway),
499 * 1 if we should try to reprint this job and
500 * 0 if all is well.
501 * Note: all filters take stdin as the file, stdout as the printer,
502 * stderr as the log file, and must not ignore SIGINT.
503 */
504static int
505print(format, file)
506	int format;
507	char *file;
508{
509	register int n;
510	register char *prog;
511	int dtablesize, fi, fo;
512	FILE *fp;
513	char *av[15], buf[BUFSIZ];
514	int pid, p[2], stopped = 0;
515	union wait status;
516	struct stat stb;
517
518	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
519		return(ERROR);
520	/*
521	 * Check to see if data file is a symbolic link. If so, it should
522	 * still point to the same file or someone is trying to print
523	 * something he shouldn't.
524	 */
525	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
526	    (stb.st_dev != fdev || stb.st_ino != fino))
527		return(ACCESS);
528	if (!SF && !tof) {		/* start on a fresh page */
529		(void) write(ofd, FF, strlen(FF));
530		tof = 1;
531	}
532	if (IF == NULL && (format == 'f' || format == 'l')) {
533		tof = 0;
534		while ((n = read(fi, buf, BUFSIZ)) > 0)
535			if (write(ofd, buf, n) != n) {
536				(void) close(fi);
537				return(REPRINT);
538			}
539		(void) close(fi);
540		return(OK);
541	}
542	switch (format) {
543	case 'p':	/* print file using 'pr' */
544		if (IF == NULL) {	/* use output filter */
545			prog = _PATH_PR;
546			av[0] = "pr";
547			av[1] = width;
548			av[2] = length;
549			av[3] = "-h";
550			av[4] = *title ? title : " ";
551			av[5] = "-F";
552			av[6] = 0;
553			fo = ofd;
554			goto start;
555		}
556		pipe(p);
557		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
558			dup2(fi, 0);		/* file is stdin */
559			dup2(p[1], 1);		/* pipe is stdout */
560			closelog();
561			for (n = 3, dtablesize = getdtablesize();
562			     n < dtablesize; n++)
563				(void) close(n);
564			execl(_PATH_PR, "pr", width, length,
565			    "-h", *title ? title : " ", "-F", 0);
566			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
567			exit(2);
568		}
569		(void) close(p[1]);		/* close output side */
570		(void) close(fi);
571		if (prchild < 0) {
572			prchild = 0;
573			(void) close(p[0]);
574			return(ERROR);
575		}
576		fi = p[0];			/* use pipe for input */
577	case 'f':	/* print plain text file */
578		prog = IF;
579		av[1] = width;
580		av[2] = length;
581		av[3] = indent;
582		n = 4;
583		break;
584	case 'l':	/* like 'f' but pass control characters */
585		prog = IF;
586		av[1] = "-c";
587		av[2] = width;
588		av[3] = length;
589		av[4] = indent;
590		n = 5;
591		break;
592	case 'r':	/* print a fortran text file */
593		prog = RF;
594		av[1] = width;
595		av[2] = length;
596		n = 3;
597		break;
598	case 't':	/* print troff output */
599	case 'n':	/* print ditroff output */
600	case 'd':	/* print tex output */
601		(void) unlink(".railmag");
602		if ((fo = creat(".railmag", FILMOD)) < 0) {
603			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
604			(void) unlink(".railmag");
605		} else {
606			for (n = 0; n < 4; n++) {
607				if (fonts[n][0] != '/')
608					(void) write(fo, _PATH_VFONT,
609					    sizeof(_PATH_VFONT) - 1);
610				(void) write(fo, fonts[n], strlen(fonts[n]));
611				(void) write(fo, "\n", 1);
612			}
613			(void) close(fo);
614		}
615		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
616		av[1] = pxwidth;
617		av[2] = pxlength;
618		n = 3;
619		break;
620	case 'c':	/* print cifplot output */
621		prog = CF;
622		av[1] = pxwidth;
623		av[2] = pxlength;
624		n = 3;
625		break;
626	case 'g':	/* print plot(1G) output */
627		prog = GF;
628		av[1] = pxwidth;
629		av[2] = pxlength;
630		n = 3;
631		break;
632	case 'v':	/* print raster output */
633		prog = VF;
634		av[1] = pxwidth;
635		av[2] = pxlength;
636		n = 3;
637		break;
638	default:
639		(void) close(fi);
640		syslog(LOG_ERR, "%s: illegal format character '%c'",
641			printer, format);
642		return(ERROR);
643	}
644	if (prog == NULL) {
645		(void) close(fi);
646		syslog(LOG_ERR,
647		   "%s: no filter found in printcap for format character '%c'",
648		   printer, format);
649		return(ERROR);
650	}
651	if ((av[0] = rindex(prog, '/')) != NULL)
652		av[0]++;
653	else
654		av[0] = prog;
655	av[n++] = "-n";
656	av[n++] = logname;
657	av[n++] = "-h";
658	av[n++] = fromhost;
659	av[n++] = AF;
660	av[n] = 0;
661	fo = pfd;
662	if (ofilter > 0) {		/* stop output filter */
663		write(ofd, "\031\1", 2);
664		while ((pid =
665		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
666			;
667		if (status.w_stopval != WSTOPPED) {
668			(void) close(fi);
669			syslog(LOG_WARNING,
670				"%s: output filter died (retcode=%d termsig=%d)",
671				printer, status.w_retcode, status.w_termsig);
672			return(REPRINT);
673		}
674		stopped++;
675	}
676start:
677	if ((child = dofork(DORETURN)) == 0) {	/* child */
678		dup2(fi, 0);
679		dup2(fo, 1);
680		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
681		if (n >= 0)
682			dup2(n, 2);
683		closelog();
684		for (n = 3, dtablesize = getdtablesize(); n < dtablesize; n++)
685			(void) close(n);
686		execv(prog, av);
687		syslog(LOG_ERR, "cannot execv %s", prog);
688		exit(2);
689	}
690	(void) close(fi);
691	if (child < 0)
692		status.w_retcode = 100;
693	else
694		while ((pid = wait((int *)&status)) > 0 && pid != child)
695			;
696	child = 0;
697	prchild = 0;
698	if (stopped) {		/* restart output filter */
699		if (kill(ofilter, SIGCONT) < 0) {
700			syslog(LOG_ERR, "cannot restart output filter");
701			exit(1);
702		}
703	}
704	tof = 0;
705
706	/* Copy filter output to "lf" logfile */
707	if (fp = fopen(tempfile, "r")) {
708		while (fgets(buf, sizeof(buf), fp))
709			fputs(buf, stderr);
710		fclose(fp);
711	}
712
713	if (!WIFEXITED(status)) {
714		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
715			printer, format, status.w_termsig);
716		return(ERROR);
717	}
718	switch (status.w_retcode) {
719	case 0:
720		tof = 1;
721		return(OK);
722	case 1:
723		return(REPRINT);
724	case 2:
725		return(ERROR);
726	default:
727		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
728			printer, format, status.w_retcode);
729		return(FILTERERR);
730	}
731}
732
733/*
734 * Send the daemon control file (cf) and any data files.
735 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
736 * 0 if all is well.
737 */
738static int
739sendit(file)
740	char *file;
741{
742	register int i, err = OK;
743	char *cp, last[BUFSIZ];
744
745	/*
746	 * open control file
747	 */
748	if ((cfp = fopen(file, "r")) == NULL)
749		return(OK);
750	/*
751	 *      read the control file for work to do
752	 *
753	 *      file format -- first character in the line is a command
754	 *      rest of the line is the argument.
755	 *      commands of interest are:
756	 *
757	 *            a-z -- "file name" name of file to print
758	 *              U -- "unlink" name of file to remove
759	 *                    (after we print it. (Pass 2 only)).
760	 */
761
762	/*
763	 * pass 1
764	 */
765	while (getline(cfp)) {
766	again:
767		if (line[0] == 'S') {
768			cp = line+1;
769			i = 0;
770			while (*cp >= '0' && *cp <= '9')
771				i = i * 10 + (*cp++ - '0');
772			fdev = i;
773			cp++;
774			i = 0;
775			while (*cp >= '0' && *cp <= '9')
776				i = i * 10 + (*cp++ - '0');
777			fino = i;
778			continue;
779		}
780		if (line[0] >= 'a' && line[0] <= 'z') {
781			strcpy(last, line);
782			while (i = getline(cfp))
783				if (strcmp(last, line))
784					break;
785			switch (sendfile('\3', last+1)) {
786			case OK:
787				if (i)
788					goto again;
789				break;
790			case REPRINT:
791				(void) fclose(cfp);
792				return(REPRINT);
793			case ACCESS:
794				sendmail(logname, ACCESS);
795			case ERROR:
796				err = ERROR;
797			}
798			break;
799		}
800	}
801	if (err == OK && sendfile('\2', file) > 0) {
802		(void) fclose(cfp);
803		return(REPRINT);
804	}
805	/*
806	 * pass 2
807	 */
808	fseek(cfp, 0L, 0);
809	while (getline(cfp))
810		if (line[0] == 'U')
811			(void) unlink(line+1);
812	/*
813	 * clean-up in case another control file exists
814	 */
815	(void) fclose(cfp);
816	(void) unlink(file);
817	return(err);
818}
819
820/*
821 * Send a data file to the remote machine and spool it.
822 * Return positive if we should try resending.
823 */
824static int
825sendfile(type, file)
826	int type;
827	char *file;
828{
829	register int f, i, amt;
830	struct stat stb;
831	char buf[BUFSIZ];
832	int sizerr, resp;
833
834	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
835		return(ERROR);
836	/*
837	 * Check to see if data file is a symbolic link. If so, it should
838	 * still point to the same file or someone is trying to print something
839	 * he shouldn't.
840	 */
841	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
842	    (stb.st_dev != fdev || stb.st_ino != fino))
843		return(ACCESS);
844	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
845	amt = strlen(buf);
846	for (i = 0;  ; i++) {
847		if (write(pfd, buf, amt) != amt ||
848		    (resp = response()) < 0 || resp == '\1') {
849			(void) close(f);
850			return(REPRINT);
851		} else if (resp == '\0')
852			break;
853		if (i == 0)
854			pstatus("no space on remote; waiting for queue to drain");
855		if (i == 10)
856			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
857				printer, RM);
858		sleep(5 * 60);
859	}
860	if (i)
861		pstatus("sending to %s", RM);
862	sizerr = 0;
863	for (i = 0; i < stb.st_size; i += BUFSIZ) {
864		amt = BUFSIZ;
865		if (i + amt > stb.st_size)
866			amt = stb.st_size - i;
867		if (sizerr == 0 && read(f, buf, amt) != amt)
868			sizerr = 1;
869		if (write(pfd, buf, amt) != amt) {
870			(void) close(f);
871			return(REPRINT);
872		}
873	}
874
875
876
877
878	(void) close(f);
879	if (sizerr) {
880		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
881		/* tell recvjob to ignore this file */
882		(void) write(pfd, "\1", 1);
883		return(ERROR);
884	}
885	if (write(pfd, "", 1) != 1 || response())
886		return(REPRINT);
887	return(OK);
888}
889
890/*
891 * Check to make sure there have been no errors and that both programs
892 * are in sync with eachother.
893 * Return non-zero if the connection was lost.
894 */
895static char
896response()
897{
898	char resp;
899
900	if (read(pfd, &resp, 1) != 1) {
901		syslog(LOG_INFO, "%s: lost connection", printer);
902		return(-1);
903	}
904	return(resp);
905}
906
907/*
908 * Banner printing stuff
909 */
910static void
911banner(name1, name2)
912	char *name1, *name2;
913{
914	time_t tvec;
915
916	time(&tvec);
917	if (!SF && !tof)
918		(void) write(ofd, FF, strlen(FF));
919	if (SB) {	/* short banner only */
920		if (class[0]) {
921			(void) write(ofd, class, strlen(class));
922			(void) write(ofd, ":", 1);
923		}
924		(void) write(ofd, name1, strlen(name1));
925		(void) write(ofd, "  Job: ", 7);
926		(void) write(ofd, name2, strlen(name2));
927		(void) write(ofd, "  Date: ", 8);
928		(void) write(ofd, ctime(&tvec), 24);
929		(void) write(ofd, "\n", 1);
930	} else {	/* normal banner */
931		(void) write(ofd, "\n\n\n", 3);
932		scan_out(ofd, name1, '\0');
933		(void) write(ofd, "\n\n", 2);
934		scan_out(ofd, name2, '\0');
935		if (class[0]) {
936			(void) write(ofd,"\n\n\n",3);
937			scan_out(ofd, class, '\0');
938		}
939		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
940		(void) write(ofd, name2, strlen(name2));
941		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
942		(void) write(ofd, ctime(&tvec), 24);
943		(void) write(ofd, "\n", 1);
944	}
945	if (!SF)
946		(void) write(ofd, FF, strlen(FF));
947	tof = 1;
948}
949
950static char *
951scnline(key, p, c)
952	register int key;
953	register char *p;
954	int c;
955{
956	register scnwidth;
957
958	for (scnwidth = WIDTH; --scnwidth;) {
959		key <<= 1;
960		*p++ = key & 0200 ? c : BACKGND;
961	}
962	return (p);
963}
964
965#define TRC(q)	(((q)-' ')&0177)
966
967static void
968scan_out(scfd, scsp, dlm)
969	int scfd, dlm;
970	char *scsp;
971{
972	register char *strp;
973	register nchrs, j;
974	char outbuf[LINELEN+1], *sp, c, cc;
975	int d, scnhgt;
976
977	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
978		strp = &outbuf[0];
979		sp = scsp;
980		for (nchrs = 0; ; ) {
981			d = dropit(c = TRC(cc = *sp++));
982			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
983				for (j = WIDTH; --j;)
984					*strp++ = BACKGND;
985			else
986				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
987			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
988				break;
989			*strp++ = BACKGND;
990			*strp++ = BACKGND;
991		}
992		while (*--strp == BACKGND && strp >= outbuf)
993			;
994		strp++;
995		*strp++ = '\n';
996		(void) write(scfd, outbuf, strp-outbuf);
997	}
998}
999
1000static int
1001dropit(c)
1002	int c;
1003{
1004	switch(c) {
1005
1006	case TRC('_'):
1007	case TRC(';'):
1008	case TRC(','):
1009	case TRC('g'):
1010	case TRC('j'):
1011	case TRC('p'):
1012	case TRC('q'):
1013	case TRC('y'):
1014		return (DROP);
1015
1016	default:
1017		return (0);
1018	}
1019}
1020
1021/*
1022 * sendmail ---
1023 *   tell people about job completion
1024 */
1025static void
1026sendmail(user, bombed)
1027	char *user;
1028	int bombed;
1029{
1030	register int i;
1031	int dtablesize;
1032	int p[2], s;
1033	register char *cp;
1034	char buf[100];
1035	struct stat stb;
1036	FILE *fp;
1037
1038	pipe(p);
1039	if ((s = dofork(DORETURN)) == 0) {		/* child */
1040		dup2(p[0], 0);
1041		closelog();
1042		for (i = 3, dtablesize = getdtablesize(); i < dtablesize; i++)
1043			(void) close(i);
1044		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1045			cp++;
1046	else
1047			cp = _PATH_SENDMAIL;
1048		sprintf(buf, "%s@%s", user, fromhost);
1049		execl(_PATH_SENDMAIL, cp, buf, 0);
1050		exit(0);
1051	} else if (s > 0) {				/* parent */
1052		dup2(p[1], 1);
1053		printf("To: %s@%s\n", user, fromhost);
1054		printf("Subject: %s printer job \"%s\"\n", printer,
1055			*jobname ? jobname : "<unknown>");
1056		printf("Reply-To: root@%s\n\n", host);
1057		printf("Your printer job ");
1058		if (*jobname)
1059			printf("(%s) ", jobname);
1060		switch (bombed) {
1061		case OK:
1062			printf("\ncompleted successfully\n");
1063			cp = "OK";
1064			break;
1065		default:
1066		case FATALERR:
1067			printf("\ncould not be printed\n");
1068			cp = "FATALERR";
1069			break;
1070		case NOACCT:
1071			printf("\ncould not be printed without an account on %s\n", host);
1072			cp = "NOACCT";
1073			break;
1074		case FILTERERR:
1075			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1076			    (fp = fopen(tempfile, "r")) == NULL) {
1077				printf("\nhad some errors and may not have printed\n");
1078				break;
1079			}
1080			printf("\nhad the following errors and may not have printed:\n");
1081			while ((i = getc(fp)) != EOF)
1082				putchar(i);
1083			(void) fclose(fp);
1084			cp = "FILTERERR";
1085			break;
1086		case ACCESS:
1087			printf("\nwas not printed because it was not linked to the original file\n");
1088			cp = "ACCESS";
1089		}
1090		fflush(stdout);
1091		(void) close(1);
1092	}
1093	(void) close(p[0]);
1094	(void) close(p[1]);
1095	wait(NULL);
1096	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
1097		user, *jobname ? jobname : "<unknown>", printer, cp);
1098}
1099
1100/*
1101 * dofork - fork with retries on failure
1102 */
1103static int
1104dofork(action)
1105	int action;
1106{
1107	register int i, pid;
1108
1109	for (i = 0; i < 20; i++) {
1110		if ((pid = fork()) < 0) {
1111			sleep((unsigned)(i*i));
1112			continue;
1113		}
1114		/*
1115		 * Child should run as daemon instead of root
1116		 */
1117		if (pid == 0)
1118			setuid(DU);
1119		return(pid);
1120	}
1121	syslog(LOG_ERR, "can't fork");
1122
1123	switch (action) {
1124	case DORETURN:
1125		return (-1);
1126	default:
1127		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1128		/*FALL THRU*/
1129	case DOABORT:
1130		exit(1);
1131	}
1132	/*NOTREACHED*/
1133}
1134
1135/*
1136 * Kill child processes to abort current job.
1137 */
1138static void
1139abortpr(signo)
1140	int signo;
1141{
1142	(void) unlink(tempfile);
1143	kill(0, SIGINT);
1144	if (ofilter > 0)
1145		kill(ofilter, SIGCONT);
1146	while (wait(NULL) > 0)
1147		;
1148	exit(0);
1149}
1150
1151static void
1152init()
1153{
1154	int status;
1155	char *s;
1156
1157	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1158		syslog(LOG_ERR, "can't open printer description file");
1159		exit(1);
1160	} else if (status == -1) {
1161		syslog(LOG_ERR, "unknown printer: %s", printer);
1162		exit(1);
1163	} else if (status == -3)
1164		fatal("potential reference loop detected in printcap file");
1165
1166	if (cgetstr(bp, "lp", &LP) == -1)
1167		LP = _PATH_DEFDEVLP;
1168	if (cgetstr(bp, "rp", &RP) == -1)
1169		RP = DEFLP;
1170	if (cgetstr(bp, "lo", &LO) == -1)
1171		LO = DEFLOCK;
1172	if (cgetstr(bp, "st", &ST) == -1)
1173		ST = DEFSTAT;
1174	if (cgetstr(bp, "lf", &LF) == -1)
1175		LF = _PATH_CONSOLE;
1176	if (cgetstr(bp, "sd", &SD) == -1)
1177		SD = _PATH_DEFSPOOL;
1178	if (cgetnum(bp, "du", &DU) < 0)
1179		DU = DEFUID;
1180	if (cgetstr(bp,"ff", &FF) == -1)
1181		FF = DEFFF;
1182	if (cgetnum(bp, "pw", &PW) < 0)
1183		PW = DEFWIDTH;
1184	sprintf(&width[2], "%d", PW);
1185	if (cgetnum(bp, "pl", &PL) < 0)
1186		PL = DEFLENGTH;
1187	sprintf(&length[2], "%d", PL);
1188	if (cgetnum(bp,"px", &PX) < 0)
1189		PX = 0;
1190	sprintf(&pxwidth[2], "%d", PX);
1191	if (cgetnum(bp, "py", &PY) < 0)
1192		PY = 0;
1193	sprintf(&pxlength[2], "%d", PY);
1194	cgetstr(bp, "rm", &RM);
1195	if (s = checkremote())
1196		syslog(LOG_WARNING, s);
1197
1198	cgetstr(bp, "af", &AF);
1199	cgetstr(bp, "of", &OF);
1200	cgetstr(bp, "if", &IF);
1201	cgetstr(bp, "rf", &RF);
1202	cgetstr(bp, "tf", &TF);
1203	cgetstr(bp, "nf", &NF);
1204	cgetstr(bp, "df", &DF);
1205	cgetstr(bp, "gf", &GF);
1206	cgetstr(bp, "vf", &VF);
1207	cgetstr(bp, "cf", &CF);
1208	cgetstr(bp, "tr", &TR);
1209	cgetstr(bp, "ms", &MS);
1210
1211	RS = (cgetcap(bp, "rs", ':') != NULL);
1212	SF = (cgetcap(bp, "sf", ':') != NULL);
1213	SH = (cgetcap(bp, "sh", ':') != NULL);
1214	SB = (cgetcap(bp, "sb", ':') != NULL);
1215	HL = (cgetcap(bp, "hl", ':') != NULL);
1216	RW = (cgetcap(bp, "rw", ':') != NULL);
1217
1218	cgetnum(bp, "br", &BR);
1219
1220	tof = (cgetcap(bp, "fo", ':') == NULL);
1221}
1222
1223/*
1224 * Acquire line printer or remote connection.
1225 */
1226static void
1227openpr()
1228{
1229	register int i;
1230	int dtablesize;
1231	char *cp;
1232
1233	if (!remote && *LP) {
1234		if (cp = index(LP, '@'))
1235			opennet(cp);
1236		else
1237			opentty();
1238	} else if (remote) {
1239		openrem();
1240	} else {
1241		syslog(LOG_ERR, "%s: no line printer device or host name",
1242			printer);
1243		exit(1);
1244	}
1245
1246	/*
1247	 * Start up an output filter, if needed.
1248	 */
1249	if (!remote && OF) {
1250		int p[2];
1251
1252		pipe(p);
1253		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1254			dup2(p[0], 0);		/* pipe is std in */
1255			dup2(pfd, 1);		/* printer is std out */
1256			closelog();
1257			for (i = 3, dtablesize = getdtablesize();
1258			     i < dtablesize; i++)
1259				(void) close(i);
1260			if ((cp = rindex(OF, '/')) == NULL)
1261				cp = OF;
1262			else
1263				cp++;
1264			execl(OF, cp, width, length, 0);
1265			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1266			exit(1);
1267		}
1268		(void) close(p[0]);		/* close input side */
1269		ofd = p[1];			/* use pipe for output */
1270	} else {
1271		ofd = pfd;
1272		ofilter = 0;
1273	}
1274}
1275
1276/*
1277 * Printer connected directly to the network
1278 * or to a terminal server on the net
1279 */
1280static void
1281opennet(cp)
1282	char *cp;
1283{
1284	register int i;
1285	int resp, port;
1286	char save_ch;
1287
1288	save_ch = *cp;
1289	*cp = '\0';
1290	port = atoi(LP);
1291	if (port <= 0) {
1292		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
1293		exit(1);
1294	}
1295	*cp++ = save_ch;
1296
1297	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1298		resp = -1;
1299		pfd = getport(cp, port);
1300		if (pfd < 0 && errno == ECONNREFUSED)
1301			resp = 1;
1302		else if (pfd >= 0) {
1303			/*
1304			 * need to delay a bit for rs232 lines
1305			 * to stabilize in case printer is
1306			 * connected via a terminal server
1307			 */
1308			delay(500);
1309			break;
1310		}
1311		if (i == 1) {
1312		   if (resp < 0)
1313			pstatus("waiting for %s to come up", LP);
1314		   else
1315			pstatus("waiting for access to printer on %s", LP);
1316		}
1317		sleep(i);
1318	}
1319	pstatus("sending to %s port %d", cp, port);
1320}
1321
1322/*
1323 * Printer is connected to an RS232 port on this host
1324 */
1325static void
1326opentty()
1327{
1328	register int i;
1329	int resp, port;
1330
1331	for (i = 1; ; i = i < 32 ? i << 1 : i) {
1332		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1333		if (pfd >= 0) {
1334			delay(500);
1335			break;
1336		}
1337		if (errno == ENOENT) {
1338			syslog(LOG_ERR, "%s: %m", LP);
1339			exit(1);
1340		}
1341		if (i == 1)
1342			pstatus("waiting for %s to become ready (offline ?)",
1343				printer);
1344		sleep(i);
1345	}
1346	if (isatty(pfd))
1347		setty();
1348	pstatus("%s is ready and printing", printer);
1349}
1350
1351/*
1352 * Printer is on a remote host
1353 */
1354static void
1355openrem()
1356{
1357	register int i, n;
1358	int resp, port;
1359
1360	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1361		resp = -1;
1362		pfd = getport(RM, 0);
1363		if (pfd >= 0) {
1364			(void) sprintf(line, "\2%s\n", RP);
1365			n = strlen(line);
1366			if (write(pfd, line, n) == n &&
1367			    (resp = response()) == '\0')
1368				break;
1369			(void) close(pfd);
1370		}
1371		if (i == 1) {
1372			if (resp < 0)
1373				pstatus("waiting for %s to come up", RM);
1374			else {
1375				pstatus("waiting for queue to be enabled on %s",
1376					RM);
1377				i = 256;
1378			}
1379		}
1380		sleep(i);
1381	}
1382	pstatus("sending to %s", RM);
1383}
1384
1385struct bauds {
1386	int	baud;
1387	int	speed;
1388} bauds[] = {
1389	50,	B50,
1390	75,	B75,
1391	110,	B110,
1392	134,	B134,
1393	150,	B150,
1394	200,	B200,
1395	300,	B300,
1396	600,	B600,
1397	1200,	B1200,
1398	1800,	B1800,
1399	2400,	B2400,
1400	4800,	B4800,
1401	9600,	B9600,
1402	19200,	EXTA,
1403	38400,	EXTB,
1404	57600,	B57600,
1405	115200,	B115200,
1406	0,	0
1407};
1408
1409/*
1410 * setup tty lines.
1411 */
1412static void
1413setty()
1414{
1415	struct termios ttybuf;
1416	struct bauds *bp;
1417
1418	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1419		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1420		exit(1);
1421	}
1422	if (tcgetattr(pfd, &ttybuf) < 0) {
1423		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
1424		exit(1);
1425	}
1426	if (BR > 0) {
1427		for (bp = bauds; bp->baud; bp++)
1428			if (BR == bp->baud)
1429				break;
1430		if (!bp->baud) {
1431			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1432			exit(1);
1433		}
1434		cfsetspeed(&ttybuf, bp->speed);
1435	}
1436	if (MS) {
1437		char *s = strdup(MS), *tmp;
1438
1439		while (tmp = strsep (&s, ",")) {
1440			msearch(tmp, &ttybuf);
1441		}
1442	}
1443	if (MS || (BR > 0)) {
1444		if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
1445			syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
1446		}
1447	}
1448}
1449
1450#if __STDC__
1451#include <stdarg.h>
1452#else
1453#include <varargs.h>
1454#endif
1455
1456static void
1457#if __STDC__
1458pstatus(const char *msg, ...)
1459#else
1460pstatus(msg, va_alist)
1461	char *msg;
1462        va_dcl
1463#endif
1464{
1465	register int fd;
1466	char buf[BUFSIZ];
1467	va_list ap;
1468#if __STDC__
1469	va_start(ap, msg);
1470#else
1471	va_start(ap);
1472#endif
1473
1474	umask(0);
1475	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1476	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1477		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1478		exit(1);
1479	}
1480	ftruncate(fd, 0);
1481	(void)vsnprintf(buf, sizeof(buf), msg, ap);
1482	va_end(ap);
1483	strcat(buf, "\n");
1484	(void) write(fd, buf, strlen(buf));
1485	(void) close(fd);
1486}
1487