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