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