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