printjob.c revision 8857
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] = "-F";
533			av[6] = 0;
534			fo = ofd;
535			goto start;
536		}
537		pipe(p);
538		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
539			dup2(fi, 0);		/* file is stdin */
540			dup2(p[1], 1);		/* pipe is stdout */
541			closelog();
542			for (n = 3; n < NOFILE; n++)
543				(void) close(n);
544			execl(_PATH_PR, "pr", width, length,
545			    "-h", *title ? title : " ", "-F", 0);
546			openlog("lpd", LOG_PID, LOG_LPR);
547			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
548			exit(2);
549		}
550		(void) close(p[1]);		/* close output side */
551		(void) close(fi);
552		if (prchild < 0) {
553			prchild = 0;
554			(void) close(p[0]);
555			return(ERROR);
556		}
557		fi = p[0];			/* use pipe for input */
558	case 'f':	/* print plain text file */
559		prog = IF;
560		av[1] = width;
561		av[2] = length;
562		av[3] = indent;
563		n = 4;
564		break;
565	case 'l':	/* like 'f' but pass control characters */
566		prog = IF;
567		av[1] = "-c";
568		av[2] = width;
569		av[3] = length;
570		av[4] = indent;
571		n = 5;
572		break;
573	case 'r':	/* print a fortran text file */
574		prog = RF;
575		av[1] = width;
576		av[2] = length;
577		n = 3;
578		break;
579	case 't':	/* print troff output */
580	case 'n':	/* print ditroff output */
581	case 'd':	/* print tex output */
582		(void) unlink(".railmag");
583		if ((fo = creat(".railmag", FILMOD)) < 0) {
584			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
585			(void) unlink(".railmag");
586		} else {
587			for (n = 0; n < 4; n++) {
588				if (fonts[n][0] != '/')
589					(void) write(fo, _PATH_VFONT,
590					    sizeof(_PATH_VFONT) - 1);
591				(void) write(fo, fonts[n], strlen(fonts[n]));
592				(void) write(fo, "\n", 1);
593			}
594			(void) close(fo);
595		}
596		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
597		av[1] = pxwidth;
598		av[2] = pxlength;
599		n = 3;
600		break;
601	case 'c':	/* print cifplot output */
602		prog = CF;
603		av[1] = pxwidth;
604		av[2] = pxlength;
605		n = 3;
606		break;
607	case 'g':	/* print plot(1G) output */
608		prog = GF;
609		av[1] = pxwidth;
610		av[2] = pxlength;
611		n = 3;
612		break;
613	case 'v':	/* print raster output */
614		prog = VF;
615		av[1] = pxwidth;
616		av[2] = pxlength;
617		n = 3;
618		break;
619	default:
620		(void) close(fi);
621		syslog(LOG_ERR, "%s: illegal format character '%c'",
622			printer, format);
623		return(ERROR);
624	}
625	if ((av[0] = rindex(prog, '/')) != NULL)
626		av[0]++;
627	else
628		av[0] = prog;
629	av[n++] = "-n";
630	av[n++] = logname;
631	av[n++] = "-h";
632	av[n++] = fromhost;
633	av[n++] = AF;
634	av[n] = 0;
635	fo = pfd;
636	if (ofilter > 0) {		/* stop output filter */
637		write(ofd, "\031\1", 2);
638		while ((pid =
639		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
640			;
641		if (status.w_stopval != WSTOPPED) {
642			(void) close(fi);
643			syslog(LOG_WARNING, "%s: output filter died (%d)",
644				printer, status.w_retcode);
645			return(REPRINT);
646		}
647		stopped++;
648	}
649start:
650	if ((child = dofork(DORETURN)) == 0) {	/* child */
651		dup2(fi, 0);
652		dup2(fo, 1);
653		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
654		if (n >= 0)
655			dup2(n, 2);
656		closelog();
657		for (n = 3; n < NOFILE; n++)
658			(void) close(n);
659		execv(prog, av);
660		openlog("lpd", LOG_PID, LOG_LPR);
661		syslog(LOG_ERR, "cannot execv %s", prog);
662		exit(2);
663	}
664	(void) close(fi);
665	if (child < 0)
666		status.w_retcode = 100;
667	else
668		while ((pid = wait((int *)&status)) > 0 && pid != child)
669			;
670	child = 0;
671	prchild = 0;
672	if (stopped) {		/* restart output filter */
673		if (kill(ofilter, SIGCONT) < 0) {
674			syslog(LOG_ERR, "cannot restart output filter");
675			exit(1);
676		}
677	}
678	tof = 0;
679
680	/* Copy filter output to "lf" logfile */
681	if (fp = fopen(tempfile, "r")) {
682		while (fgets(buf, sizeof(buf), fp))
683			fputs(buf, stderr);
684		fclose(fp);
685	}
686
687	if (!WIFEXITED(status)) {
688		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
689			printer, format, status.w_termsig);
690		return(ERROR);
691	}
692	switch (status.w_retcode) {
693	case 0:
694		tof = 1;
695		return(OK);
696	case 1:
697		return(REPRINT);
698	default:
699		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
700			printer, format, status.w_retcode);
701	case 2:
702		return(ERROR);
703	}
704}
705
706/*
707 * Send the daemon control file (cf) and any data files.
708 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
709 * 0 if all is well.
710 */
711static int
712sendit(file)
713	char *file;
714{
715	register int i, err = OK;
716	char *cp, last[BUFSIZ];
717
718	/*
719	 * open control file
720	 */
721	if ((cfp = fopen(file, "r")) == NULL)
722		return(OK);
723	/*
724	 *      read the control file for work to do
725	 *
726	 *      file format -- first character in the line is a command
727	 *      rest of the line is the argument.
728	 *      commands of interest are:
729	 *
730	 *            a-z -- "file name" name of file to print
731	 *              U -- "unlink" name of file to remove
732	 *                    (after we print it. (Pass 2 only)).
733	 */
734
735	/*
736	 * pass 1
737	 */
738	while (getline(cfp)) {
739	again:
740		if (line[0] == 'S') {
741			cp = line+1;
742			i = 0;
743			while (*cp >= '0' && *cp <= '9')
744				i = i * 10 + (*cp++ - '0');
745			fdev = i;
746			cp++;
747			i = 0;
748			while (*cp >= '0' && *cp <= '9')
749				i = i * 10 + (*cp++ - '0');
750			fino = i;
751			continue;
752		}
753		if (line[0] >= 'a' && line[0] <= 'z') {
754			strcpy(last, line);
755			while (i = getline(cfp))
756				if (strcmp(last, line))
757					break;
758			switch (sendfile('\3', last+1)) {
759			case OK:
760				if (i)
761					goto again;
762				break;
763			case REPRINT:
764				(void) fclose(cfp);
765				return(REPRINT);
766			case ACCESS:
767				sendmail(logname, ACCESS);
768			case ERROR:
769				err = ERROR;
770			}
771			break;
772		}
773	}
774	if (err == OK && sendfile('\2', file) > 0) {
775		(void) fclose(cfp);
776		return(REPRINT);
777	}
778	/*
779	 * pass 2
780	 */
781	fseek(cfp, 0L, 0);
782	while (getline(cfp))
783		if (line[0] == 'U')
784			(void) unlink(line+1);
785	/*
786	 * clean-up in case another control file exists
787	 */
788	(void) fclose(cfp);
789	(void) unlink(file);
790	return(err);
791}
792
793/*
794 * Send a data file to the remote machine and spool it.
795 * Return positive if we should try resending.
796 */
797static int
798sendfile(type, file)
799	int type;
800	char *file;
801{
802	register int f, i, amt;
803	struct stat stb;
804	char buf[BUFSIZ];
805	int sizerr, resp;
806
807	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
808		return(ERROR);
809	/*
810	 * Check to see if data file is a symbolic link. If so, it should
811	 * still point to the same file or someone is trying to print something
812	 * he shouldn't.
813	 */
814	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
815	    (stb.st_dev != fdev || stb.st_ino != fino))
816		return(ACCESS);
817	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
818	amt = strlen(buf);
819	for (i = 0;  ; i++) {
820		if (write(pfd, buf, amt) != amt ||
821		    (resp = response()) < 0 || resp == '\1') {
822			(void) close(f);
823			return(REPRINT);
824		} else if (resp == '\0')
825			break;
826		if (i == 0)
827			pstatus("no space on remote; waiting for queue to drain");
828		if (i == 10)
829			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
830				printer, RM);
831		sleep(5 * 60);
832	}
833	if (i)
834		pstatus("sending to %s", RM);
835	sizerr = 0;
836	for (i = 0; i < stb.st_size; i += BUFSIZ) {
837		amt = BUFSIZ;
838		if (i + amt > stb.st_size)
839			amt = stb.st_size - i;
840		if (sizerr == 0 && read(f, buf, amt) != amt)
841			sizerr = 1;
842		if (write(pfd, buf, amt) != amt) {
843			(void) close(f);
844			return(REPRINT);
845		}
846	}
847
848
849
850
851	(void) close(f);
852	if (sizerr) {
853		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
854		/* tell recvjob to ignore this file */
855		(void) write(pfd, "\1", 1);
856		return(ERROR);
857	}
858	if (write(pfd, "", 1) != 1 || response())
859		return(REPRINT);
860	return(OK);
861}
862
863/*
864 * Check to make sure there have been no errors and that both programs
865 * are in sync with eachother.
866 * Return non-zero if the connection was lost.
867 */
868static char
869response()
870{
871	char resp;
872
873	if (read(pfd, &resp, 1) != 1) {
874		syslog(LOG_INFO, "%s: lost connection", printer);
875		return(-1);
876	}
877	return(resp);
878}
879
880/*
881 * Banner printing stuff
882 */
883static void
884banner(name1, name2)
885	char *name1, *name2;
886{
887	time_t tvec;
888	extern char *ctime();
889
890	time(&tvec);
891	if (!SF && !tof)
892		(void) write(ofd, FF, strlen(FF));
893	if (SB) {	/* short banner only */
894		if (class[0]) {
895			(void) write(ofd, class, strlen(class));
896			(void) write(ofd, ":", 1);
897		}
898		(void) write(ofd, name1, strlen(name1));
899		(void) write(ofd, "  Job: ", 7);
900		(void) write(ofd, name2, strlen(name2));
901		(void) write(ofd, "  Date: ", 8);
902		(void) write(ofd, ctime(&tvec), 24);
903		(void) write(ofd, "\n", 1);
904	} else {	/* normal banner */
905		(void) write(ofd, "\n\n\n", 3);
906		scan_out(ofd, name1, '\0');
907		(void) write(ofd, "\n\n", 2);
908		scan_out(ofd, name2, '\0');
909		if (class[0]) {
910			(void) write(ofd,"\n\n\n",3);
911			scan_out(ofd, class, '\0');
912		}
913		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
914		(void) write(ofd, name2, strlen(name2));
915		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
916		(void) write(ofd, ctime(&tvec), 24);
917		(void) write(ofd, "\n", 1);
918	}
919	if (!SF)
920		(void) write(ofd, FF, strlen(FF));
921	tof = 1;
922}
923
924static char *
925scnline(key, p, c)
926	register int key;
927	register char *p;
928	int c;
929{
930	register scnwidth;
931
932	for (scnwidth = WIDTH; --scnwidth;) {
933		key <<= 1;
934		*p++ = key & 0200 ? c : BACKGND;
935	}
936	return (p);
937}
938
939#define TRC(q)	(((q)-' ')&0177)
940
941static void
942scan_out(scfd, scsp, dlm)
943	int scfd, dlm;
944	char *scsp;
945{
946	register char *strp;
947	register nchrs, j;
948	char outbuf[LINELEN+1], *sp, c, cc;
949	int d, scnhgt;
950	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
951
952	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
953		strp = &outbuf[0];
954		sp = scsp;
955		for (nchrs = 0; ; ) {
956			d = dropit(c = TRC(cc = *sp++));
957			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
958				for (j = WIDTH; --j;)
959					*strp++ = BACKGND;
960			else
961				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
962			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
963				break;
964			*strp++ = BACKGND;
965			*strp++ = BACKGND;
966		}
967		while (*--strp == BACKGND && strp >= outbuf)
968			;
969		strp++;
970		*strp++ = '\n';
971		(void) write(scfd, outbuf, strp-outbuf);
972	}
973}
974
975static int
976dropit(c)
977	int c;
978{
979	switch(c) {
980
981	case TRC('_'):
982	case TRC(';'):
983	case TRC(','):
984	case TRC('g'):
985	case TRC('j'):
986	case TRC('p'):
987	case TRC('q'):
988	case TRC('y'):
989		return (DROP);
990
991	default:
992		return (0);
993	}
994}
995
996/*
997 * sendmail ---
998 *   tell people about job completion
999 */
1000static void
1001sendmail(user, bombed)
1002	char *user;
1003	int bombed;
1004{
1005	register int i;
1006	int p[2], s;
1007	register char *cp;
1008	char buf[100];
1009	struct stat stb;
1010	FILE *fp;
1011
1012	pipe(p);
1013	if ((s = dofork(DORETURN)) == 0) {		/* child */
1014		dup2(p[0], 0);
1015		closelog();
1016		for (i = 3; i < NOFILE; i++)
1017			(void) close(i);
1018		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1019			cp++;
1020	else
1021			cp = _PATH_SENDMAIL;
1022		sprintf(buf, "%s@%s", user, fromhost);
1023		execl(_PATH_SENDMAIL, cp, buf, 0);
1024		openlog("lpd", LOG_PID, LOG_LPR);
1025		syslog(LOG_ERR, "cannot execl %s", _PATH_SENDMAIL);
1026		exit(0);
1027	} else if (s > 0) {				/* parent */
1028		dup2(p[1], 1);
1029		printf("To: %s@%s\n", user, fromhost);
1030		printf("Subject: printer job\n\n");
1031		printf("Your printer job ");
1032		if (*jobname)
1033			printf("(%s) ", jobname);
1034		switch (bombed) {
1035		case OK:
1036			printf("\ncompleted successfully\n");
1037			break;
1038		default:
1039		case FATALERR:
1040			printf("\ncould not be printed\n");
1041			break;
1042		case NOACCT:
1043			printf("\ncould not be printed without an account on %s\n", host);
1044			break;
1045		case FILTERERR:
1046			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1047			    (fp = fopen(tempfile, "r")) == NULL) {
1048				printf("\nwas printed but had some errors\n");
1049				break;
1050			}
1051			printf("\nwas printed but had the following errors:\n");
1052			while ((i = getc(fp)) != EOF)
1053				putchar(i);
1054			(void) fclose(fp);
1055			break;
1056		case ACCESS:
1057			printf("\nwas not printed because it was not linked to the original file\n");
1058		}
1059		fflush(stdout);
1060		(void) close(1);
1061	}
1062	(void) close(p[0]);
1063	(void) close(p[1]);
1064	wait(&s);
1065}
1066
1067/*
1068 * dofork - fork with retries on failure
1069 */
1070static int
1071dofork(action)
1072	int action;
1073{
1074	register int i, pid;
1075
1076	for (i = 0; i < 20; i++) {
1077		if ((pid = fork()) < 0) {
1078			sleep((unsigned)(i*i));
1079			continue;
1080		}
1081		/*
1082		 * Child should run as daemon instead of root
1083		 */
1084		if (pid == 0)
1085			setuid(DU);
1086		return(pid);
1087	}
1088	syslog(LOG_ERR, "can't fork");
1089
1090	switch (action) {
1091	case DORETURN:
1092		return (-1);
1093	default:
1094		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1095		/*FALL THRU*/
1096	case DOABORT:
1097		exit(1);
1098	}
1099	/*NOTREACHED*/
1100}
1101
1102/*
1103 * Kill child processes to abort current job.
1104 */
1105static void
1106abortpr(signo)
1107	int signo;
1108{
1109	(void) unlink(tempfile);
1110	kill(0, SIGINT);
1111	if (ofilter > 0)
1112		kill(ofilter, SIGCONT);
1113	while (wait(NULL) > 0)
1114		;
1115	exit(0);
1116}
1117
1118static void
1119init()
1120{
1121	int status;
1122	char *s;
1123
1124	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1125		syslog(LOG_ERR, "can't open printer description file");
1126		exit(1);
1127	} else if (status == -1) {
1128		syslog(LOG_ERR, "unknown printer: %s", printer);
1129		exit(1);
1130	} else if (status == -3)
1131		fatal("potential reference loop detected in printcap file");
1132
1133	if (cgetstr(bp, "lp", &LP) == -1)
1134		LP = _PATH_DEFDEVLP;
1135	if (cgetstr(bp, "rp", &RP) == -1)
1136		RP = DEFLP;
1137	if (cgetstr(bp, "lo", &LO) == -1)
1138		LO = DEFLOCK;
1139	if (cgetstr(bp, "st", &ST) == -1)
1140		ST = DEFSTAT;
1141	if (cgetstr(bp, "lf", &LF) == -1)
1142		LF = _PATH_CONSOLE;
1143	if (cgetstr(bp, "sd", &SD) == -1)
1144		SD = _PATH_DEFSPOOL;
1145	if (cgetnum(bp, "du", &DU) < 0)
1146		DU = DEFUID;
1147	if (cgetstr(bp,"ff", &FF) == -1)
1148		FF = DEFFF;
1149	if (cgetnum(bp, "pw", &PW) < 0)
1150		PW = DEFWIDTH;
1151	sprintf(&width[2], "%d", PW);
1152	if (cgetnum(bp, "pl", &PL) < 0)
1153		PL = DEFLENGTH;
1154	sprintf(&length[2], "%d", PL);
1155	if (cgetnum(bp,"px", &PX) < 0)
1156		PX = 0;
1157	sprintf(&pxwidth[2], "%d", PX);
1158	if (cgetnum(bp, "py", &PY) < 0)
1159		PY = 0;
1160	sprintf(&pxlength[2], "%d", PY);
1161	cgetstr(bp, "rm", &RM);
1162	if (s = checkremote())
1163		syslog(LOG_WARNING, s);
1164
1165	cgetstr(bp, "af", &AF);
1166	cgetstr(bp, "of", &OF);
1167	cgetstr(bp, "if", &IF);
1168	cgetstr(bp, "rf", &RF);
1169	cgetstr(bp, "tf", &TF);
1170	cgetstr(bp, "nf", &NF);
1171	cgetstr(bp, "df", &DF);
1172	cgetstr(bp, "gf", &GF);
1173	cgetstr(bp, "vf", &VF);
1174	cgetstr(bp, "cf", &CF);
1175	cgetstr(bp, "tr", &TR);
1176
1177	RS = (cgetcap(bp, "rs", ':') != NULL);
1178	SF = (cgetcap(bp, "sf", ':') != NULL);
1179	SH = (cgetcap(bp, "sh", ':') != NULL);
1180	SB = (cgetcap(bp, "sb", ':') != NULL);
1181	HL = (cgetcap(bp, "hl", ':') != NULL);
1182	RW = (cgetcap(bp, "rw", ':') != NULL);
1183
1184	cgetnum(bp, "br", &BR);
1185	if (cgetnum(bp, "fc", &FC) < 0)
1186		FC = 0;
1187	if (cgetnum(bp, "fs", &FS) < 0)
1188		FS = 0;
1189	if (cgetnum(bp, "xc", &XC) < 0)
1190		XC = 0;
1191	if (cgetnum(bp, "xs", &XS) < 0)
1192		XS = 0;
1193
1194	tof = (cgetcap(bp, "fo", ':') == NULL);
1195}
1196
1197/*
1198 * Acquire line printer or remote connection.
1199 */
1200static void
1201openpr()
1202{
1203	register int i, n;
1204	int resp;
1205
1206	if (!sendtorem && *LP) {
1207		for (i = 1; ; i = i < 32 ? i << 1 : i) {
1208			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1209			if (pfd >= 0)
1210				break;
1211			if (errno == ENOENT) {
1212				syslog(LOG_ERR, "%s: %m", LP);
1213				exit(1);
1214			}
1215			if (i == 1)
1216				pstatus("waiting for %s to become ready (offline ?)", printer);
1217			sleep(i);
1218		}
1219		if (isatty(pfd))
1220			setty();
1221		pstatus("%s is ready and printing", printer);
1222	} else if (RM != NULL) {
1223		for (i = 1; ; i = i < 256 ? i << 1 : i) {
1224			resp = -1;
1225			pfd = getport(RM);
1226			if (pfd >= 0) {
1227				(void) sprintf(line, "\2%s\n", RP);
1228				n = strlen(line);
1229				if (write(pfd, line, n) == n &&
1230				    (resp = response()) == '\0')
1231					break;
1232				(void) close(pfd);
1233			}
1234			if (i == 1) {
1235				if (resp < 0)
1236					pstatus("waiting for %s to come up", RM);
1237				else {
1238					pstatus("waiting for queue to be enabled on %s", RM);
1239					i = 256;
1240				}
1241			}
1242			sleep(i);
1243		}
1244		pstatus("sending to %s", RM);
1245		remote = 1;
1246	} else {
1247		syslog(LOG_ERR, "%s: no line printer device or host name",
1248			printer);
1249		exit(1);
1250	}
1251	/*
1252	 * Start up an output filter, if needed.
1253	 */
1254	if (!remote && OF) {
1255		int p[2];
1256		char *cp;
1257
1258		pipe(p);
1259		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1260			dup2(p[0], 0);		/* pipe is std in */
1261			dup2(pfd, 1);		/* printer is std out */
1262			closelog();
1263			for (i = 3; i < NOFILE; i++)
1264				(void) close(i);
1265			if ((cp = rindex(OF, '/')) == NULL)
1266				cp = OF;
1267			else
1268				cp++;
1269			execl(OF, cp, width, length, 0);
1270			openlog("lpd", LOG_PID, LOG_LPR);
1271			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1272			exit(1);
1273		}
1274		(void) close(p[0]);		/* close input side */
1275		ofd = p[1];			/* use pipe for output */
1276	} else {
1277		ofd = pfd;
1278		ofilter = 0;
1279	}
1280}
1281
1282struct bauds {
1283	int	baud;
1284	int	speed;
1285} bauds[] = {
1286	50,	B50,
1287	75,	B75,
1288	110,	B110,
1289	134,	B134,
1290	150,	B150,
1291	200,	B200,
1292	300,	B300,
1293	600,	B600,
1294	1200,	B1200,
1295	1800,	B1800,
1296	2400,	B2400,
1297	4800,	B4800,
1298	9600,	B9600,
1299	19200,	EXTA,
1300	38400,	EXTB,
1301	0,	0
1302};
1303
1304/*
1305 * setup tty lines.
1306 */
1307static void
1308setty()
1309{
1310	struct sgttyb ttybuf;
1311	register struct bauds *bp;
1312
1313	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1314		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1315		exit(1);
1316	}
1317	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1318		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
1319		exit(1);
1320	}
1321	if (BR > 0) {
1322		for (bp = bauds; bp->baud; bp++)
1323			if (BR == bp->baud)
1324				break;
1325		if (!bp->baud) {
1326			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1327			exit(1);
1328		}
1329		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1330	}
1331	ttybuf.sg_flags &= ~FC;
1332	ttybuf.sg_flags |= FS;
1333	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1334		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
1335		exit(1);
1336	}
1337	if (XC) {
1338		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1339			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
1340			exit(1);
1341		}
1342	}
1343	if (XS) {
1344		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1345			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
1346			exit(1);
1347		}
1348	}
1349}
1350
1351#if __STDC__
1352#include <stdarg.h>
1353#else
1354#include <varargs.h>
1355#endif
1356
1357void
1358#if __STDC__
1359pstatus(const char *msg, ...)
1360#else
1361pstatus(msg, va_alist)
1362	char *msg;
1363        va_dcl
1364#endif
1365{
1366	register int fd;
1367	char buf[BUFSIZ];
1368	va_list ap;
1369#if __STDC__
1370	va_start(ap, msg);
1371#else
1372	va_start(ap);
1373#endif
1374
1375	umask(0);
1376	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1377	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1378		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1379		exit(1);
1380	}
1381	ftruncate(fd, 0);
1382	(void)vsnprintf(buf, sizeof(buf), msg, ap);
1383	va_end(ap);
1384	strcat(buf, "\n");
1385	(void) write(fd, buf, strlen(buf));
1386	(void) close(fd);
1387}
1388