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