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