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