xref: /original-bsd/usr.sbin/lpr/lpd/printjob.c (revision 14054b48)
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.13 (Berkeley) 03/02/91";
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 	void 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 =
563 		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
564 			;
565 		if (status.w_stopval != WSTOPPED) {
566 			(void) close(fi);
567 			syslog(LOG_WARNING, "%s: output filter died (%d)",
568 				printer, status.w_retcode);
569 			return(REPRINT);
570 		}
571 		stopped++;
572 	}
573 start:
574 	if ((child = dofork(DORETURN)) == 0) {	/* child */
575 		dup2(fi, 0);
576 		dup2(fo, 1);
577 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
578 		if (n >= 0)
579 			dup2(n, 2);
580 		for (n = 3; n < NOFILE; n++)
581 			(void) close(n);
582 		execv(prog, av);
583 		syslog(LOG_ERR, "cannot execv %s", prog);
584 		exit(2);
585 	}
586 	(void) close(fi);
587 	if (child < 0)
588 		status.w_retcode = 100;
589 	else
590 		while ((pid = wait((int *)&status)) > 0 && pid != child)
591 			;
592 	child = 0;
593 	prchild = 0;
594 	if (stopped) {		/* restart output filter */
595 		if (kill(ofilter, SIGCONT) < 0) {
596 			syslog(LOG_ERR, "cannot restart output filter");
597 			exit(1);
598 		}
599 	}
600 	tof = 0;
601 
602 	/* Copy filter output to "lf" logfile */
603 	if (fp = fopen(tempfile, "r")) {
604 		char tbuf[512];
605 
606 		while (fgets(buf, sizeof(buf), fp))
607 			fputs(buf, stderr);
608 		close(fp);
609 	}
610 
611 	if (!WIFEXITED(status)) {
612 		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
613 			printer, format, status.w_termsig);
614 		return(ERROR);
615 	}
616 	switch (status.w_retcode) {
617 	case 0:
618 		tof = 1;
619 		return(OK);
620 	case 1:
621 		return(REPRINT);
622 	default:
623 		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
624 			printer, format, status.w_retcode);
625 	case 2:
626 		return(ERROR);
627 	}
628 }
629 
630 /*
631  * Send the daemon control file (cf) and any data files.
632  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
633  * 0 if all is well.
634  */
635 sendit(file)
636 	char *file;
637 {
638 	register int i, err = OK;
639 	char *cp, last[BUFSIZ];
640 
641 	/*
642 	 * open control file
643 	 */
644 	if ((cfp = fopen(file, "r")) == NULL)
645 		return(OK);
646 	/*
647 	 *      read the control file for work to do
648 	 *
649 	 *      file format -- first character in the line is a command
650 	 *      rest of the line is the argument.
651 	 *      commands of interest are:
652 	 *
653 	 *            a-z -- "file name" name of file to print
654 	 *              U -- "unlink" name of file to remove
655 	 *                    (after we print it. (Pass 2 only)).
656 	 */
657 
658 	/*
659 	 * pass 1
660 	 */
661 	while (getline(cfp)) {
662 	again:
663 		if (line[0] == 'S') {
664 			cp = line+1;
665 			i = 0;
666 			while (*cp >= '0' && *cp <= '9')
667 				i = i * 10 + (*cp++ - '0');
668 			fdev = i;
669 			cp++;
670 			i = 0;
671 			while (*cp >= '0' && *cp <= '9')
672 				i = i * 10 + (*cp++ - '0');
673 			fino = i;
674 			continue;
675 		}
676 		if (line[0] >= 'a' && line[0] <= 'z') {
677 			strcpy(last, line);
678 			while (i = getline(cfp))
679 				if (strcmp(last, line))
680 					break;
681 			switch (sendfile('\3', last+1)) {
682 			case OK:
683 				if (i)
684 					goto again;
685 				break;
686 			case REPRINT:
687 				(void) fclose(cfp);
688 				return(REPRINT);
689 			case ACCESS:
690 				sendmail(logname, ACCESS);
691 			case ERROR:
692 				err = ERROR;
693 			}
694 			break;
695 		}
696 	}
697 	if (err == OK && sendfile('\2', file) > 0) {
698 		(void) fclose(cfp);
699 		return(REPRINT);
700 	}
701 	/*
702 	 * pass 2
703 	 */
704 	fseek(cfp, 0L, 0);
705 	while (getline(cfp))
706 		if (line[0] == 'U')
707 			(void) unlink(line+1);
708 	/*
709 	 * clean-up in case another control file exists
710 	 */
711 	(void) fclose(cfp);
712 	(void) unlink(file);
713 	return(err);
714 }
715 
716 /*
717  * Send a data file to the remote machine and spool it.
718  * Return positive if we should try resending.
719  */
720 sendfile(type, file)
721 	char type, *file;
722 {
723 	register int f, i, amt;
724 	struct stat stb;
725 	char buf[BUFSIZ];
726 	int sizerr, resp;
727 
728 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
729 		return(ERROR);
730 	/*
731 	 * Check to see if data file is a symbolic link. If so, it should
732 	 * still point to the same file or someone is trying to print something
733 	 * he shouldn't.
734 	 */
735 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
736 	    (stb.st_dev != fdev || stb.st_ino != fino))
737 		return(ACCESS);
738 	(void) sprintf(buf, "%c%d %s\n", type, stb.st_size, file);
739 	amt = strlen(buf);
740 	for (i = 0;  ; i++) {
741 		if (write(pfd, buf, amt) != amt ||
742 		    (resp = response()) < 0 || resp == '\1') {
743 			(void) close(f);
744 			return(REPRINT);
745 		} else if (resp == '\0')
746 			break;
747 		if (i == 0)
748 			status("no space on remote; waiting for queue to drain");
749 		if (i == 10)
750 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
751 				printer, RM);
752 		sleep(5 * 60);
753 	}
754 	if (i)
755 		status("sending to %s", RM);
756 	sizerr = 0;
757 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
758 		amt = BUFSIZ;
759 		if (i + amt > stb.st_size)
760 			amt = stb.st_size - i;
761 		if (sizerr == 0 && read(f, buf, amt) != amt)
762 			sizerr = 1;
763 		if (write(pfd, buf, amt) != amt) {
764 			(void) close(f);
765 			return(REPRINT);
766 		}
767 	}
768 	(void) close(f);
769 	if (sizerr) {
770 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
771 		/* tell recvjob to ignore this file */
772 		(void) write(pfd, "\1", 1);
773 		return(ERROR);
774 	}
775 	if (write(pfd, "", 1) != 1 || response())
776 		return(REPRINT);
777 	return(OK);
778 }
779 
780 /*
781  * Check to make sure there have been no errors and that both programs
782  * are in sync with eachother.
783  * Return non-zero if the connection was lost.
784  */
785 response()
786 {
787 	char resp;
788 
789 	if (read(pfd, &resp, 1) != 1) {
790 		syslog(LOG_INFO, "%s: lost connection", printer);
791 		return(-1);
792 	}
793 	return(resp);
794 }
795 
796 /*
797  * Banner printing stuff
798  */
799 banner(name1, name2)
800 	char *name1, *name2;
801 {
802 	time_t tvec;
803 	extern char *ctime();
804 
805 	time(&tvec);
806 	if (!SF && !tof)
807 		(void) write(ofd, FF, strlen(FF));
808 	if (SB) {	/* short banner only */
809 		if (class[0]) {
810 			(void) write(ofd, class, strlen(class));
811 			(void) write(ofd, ":", 1);
812 		}
813 		(void) write(ofd, name1, strlen(name1));
814 		(void) write(ofd, "  Job: ", 7);
815 		(void) write(ofd, name2, strlen(name2));
816 		(void) write(ofd, "  Date: ", 8);
817 		(void) write(ofd, ctime(&tvec), 24);
818 		(void) write(ofd, "\n", 1);
819 	} else {	/* normal banner */
820 		(void) write(ofd, "\n\n\n", 3);
821 		scan_out(ofd, name1, '\0');
822 		(void) write(ofd, "\n\n", 2);
823 		scan_out(ofd, name2, '\0');
824 		if (class[0]) {
825 			(void) write(ofd,"\n\n\n",3);
826 			scan_out(ofd, class, '\0');
827 		}
828 		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
829 		(void) write(ofd, name2, strlen(name2));
830 		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
831 		(void) write(ofd, ctime(&tvec), 24);
832 		(void) write(ofd, "\n", 1);
833 	}
834 	if (!SF)
835 		(void) write(ofd, FF, strlen(FF));
836 	tof = 1;
837 }
838 
839 char *
840 scnline(key, p, c)
841 	register char key, *p;
842 	char c;
843 {
844 	register scnwidth;
845 
846 	for (scnwidth = WIDTH; --scnwidth;) {
847 		key <<= 1;
848 		*p++ = key & 0200 ? c : BACKGND;
849 	}
850 	return (p);
851 }
852 
853 #define TRC(q)	(((q)-' ')&0177)
854 
855 scan_out(scfd, scsp, dlm)
856 	int scfd;
857 	char *scsp, dlm;
858 {
859 	register char *strp;
860 	register nchrs, j;
861 	char outbuf[LINELEN+1], *sp, c, cc;
862 	int d, scnhgt;
863 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
864 
865 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
866 		strp = &outbuf[0];
867 		sp = scsp;
868 		for (nchrs = 0; ; ) {
869 			d = dropit(c = TRC(cc = *sp++));
870 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
871 				for (j = WIDTH; --j;)
872 					*strp++ = BACKGND;
873 			else
874 				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
875 			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
876 				break;
877 			*strp++ = BACKGND;
878 			*strp++ = BACKGND;
879 		}
880 		while (*--strp == BACKGND && strp >= outbuf)
881 			;
882 		strp++;
883 		*strp++ = '\n';
884 		(void) write(scfd, outbuf, strp-outbuf);
885 	}
886 }
887 
888 dropit(c)
889 	char c;
890 {
891 	switch(c) {
892 
893 	case TRC('_'):
894 	case TRC(';'):
895 	case TRC(','):
896 	case TRC('g'):
897 	case TRC('j'):
898 	case TRC('p'):
899 	case TRC('q'):
900 	case TRC('y'):
901 		return (DROP);
902 
903 	default:
904 		return (0);
905 	}
906 }
907 
908 /*
909  * sendmail ---
910  *   tell people about job completion
911  */
912 sendmail(user, bombed)
913 	char *user;
914 	int bombed;
915 {
916 	register int i;
917 	int p[2], s;
918 	register char *cp;
919 	char buf[100];
920 	struct stat stb;
921 	FILE *fp;
922 
923 	pipe(p);
924 	if ((s = dofork(DORETURN)) == 0) {		/* child */
925 		dup2(p[0], 0);
926 		for (i = 3; i < NOFILE; i++)
927 			(void) close(i);
928 		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
929 			cp++;
930 		else
931 			cp = _PATH_SENDMAIL;
932 		sprintf(buf, "%s@%s", user, fromhost);
933 		execl(_PATH_SENDMAIL, cp, buf, 0);
934 		exit(0);
935 	} else if (s > 0) {				/* parent */
936 		dup2(p[1], 1);
937 		printf("To: %s@%s\n", user, fromhost);
938 		printf("Subject: printer job\n\n");
939 		printf("Your printer job ");
940 		if (*jobname)
941 			printf("(%s) ", jobname);
942 		switch (bombed) {
943 		case OK:
944 			printf("\ncompleted successfully\n");
945 			break;
946 		default:
947 		case FATALERR:
948 			printf("\ncould not be printed\n");
949 			break;
950 		case NOACCT:
951 			printf("\ncould not be printed without an account on %s\n", host);
952 			break;
953 		case FILTERERR:
954 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
955 			    (fp = fopen(tempfile, "r")) == NULL) {
956 				printf("\nwas printed but had some errors\n");
957 				break;
958 			}
959 			printf("\nwas printed but had the following errors:\n");
960 			while ((i = getc(fp)) != EOF)
961 				putchar(i);
962 			(void) fclose(fp);
963 			break;
964 		case ACCESS:
965 			printf("\nwas not printed because it was not linked to the original file\n");
966 		}
967 		fflush(stdout);
968 		(void) close(1);
969 	}
970 	(void) close(p[0]);
971 	(void) close(p[1]);
972 	wait(&s);
973 }
974 
975 /*
976  * dofork - fork with retries on failure
977  */
978 dofork(action)
979 	int action;
980 {
981 	register int i, pid;
982 
983 	for (i = 0; i < 20; i++) {
984 		if ((pid = fork()) < 0) {
985 			sleep((unsigned)(i*i));
986 			continue;
987 		}
988 		/*
989 		 * Child should run as daemon instead of root
990 		 */
991 		if (pid == 0)
992 			setuid(DU);
993 		return(pid);
994 	}
995 	syslog(LOG_ERR, "can't fork");
996 
997 	switch (action) {
998 	case DORETURN:
999 		return (-1);
1000 	default:
1001 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1002 		/*FALL THRU*/
1003 	case DOABORT:
1004 		exit(1);
1005 	}
1006 	/*NOTREACHED*/
1007 }
1008 
1009 /*
1010  * Kill child processes to abort current job.
1011  */
1012 void
1013 abortpr()
1014 {
1015 	(void) unlink(tempfile);
1016 	kill(0, SIGINT);
1017 	if (ofilter > 0)
1018 		kill(ofilter, SIGCONT);
1019 	while (wait(NULL) > 0)
1020 		;
1021 	exit(0);
1022 }
1023 
1024 init()
1025 {
1026 	int status;
1027 	char *s;
1028 
1029 	if ((status = pgetent(line, printer)) < 0) {
1030 		syslog(LOG_ERR, "can't open printer description file");
1031 		exit(1);
1032 	} else if (status == 0) {
1033 		syslog(LOG_ERR, "unknown printer: %s", printer);
1034 		exit(1);
1035 	}
1036 	if ((LP = pgetstr("lp", &bp)) == NULL)
1037 		LP = _PATH_DEFDEVLP;
1038 	if ((RP = pgetstr("rp", &bp)) == NULL)
1039 		RP = DEFLP;
1040 	if ((LO = pgetstr("lo", &bp)) == NULL)
1041 		LO = DEFLOCK;
1042 	if ((ST = pgetstr("st", &bp)) == NULL)
1043 		ST = DEFSTAT;
1044 	if ((LF = pgetstr("lf", &bp)) == NULL)
1045 		LF = _PATH_CONSOLE;
1046 	if ((SD = pgetstr("sd", &bp)) == NULL)
1047 		SD = _PATH_DEFSPOOL;
1048 	if ((DU = pgetnum("du")) < 0)
1049 		DU = DEFUID;
1050 	if ((FF = pgetstr("ff", &bp)) == NULL)
1051 		FF = DEFFF;
1052 	if ((PW = pgetnum("pw")) < 0)
1053 		PW = DEFWIDTH;
1054 	sprintf(&width[2], "%d", PW);
1055 	if ((PL = pgetnum("pl")) < 0)
1056 		PL = DEFLENGTH;
1057 	sprintf(&length[2], "%d", PL);
1058 	if ((PX = pgetnum("px")) < 0)
1059 		PX = 0;
1060 	sprintf(&pxwidth[2], "%d", PX);
1061 	if ((PY = pgetnum("py")) < 0)
1062 		PY = 0;
1063 	sprintf(&pxlength[2], "%d", PY);
1064 	RM = pgetstr("rm", &bp);
1065 	if (s = checkremote())
1066 		syslog(LOG_WARNING, s);
1067 
1068 	AF = pgetstr("af", &bp);
1069 	OF = pgetstr("of", &bp);
1070 	IF = pgetstr("if", &bp);
1071 	RF = pgetstr("rf", &bp);
1072 	TF = pgetstr("tf", &bp);
1073 	NF = pgetstr("nf", &bp);
1074 	DF = pgetstr("df", &bp);
1075 	GF = pgetstr("gf", &bp);
1076 	VF = pgetstr("vf", &bp);
1077 	CF = pgetstr("cf", &bp);
1078 	TR = pgetstr("tr", &bp);
1079 	RS = pgetflag("rs");
1080 	SF = pgetflag("sf");
1081 	SH = pgetflag("sh");
1082 	SB = pgetflag("sb");
1083 	HL = pgetflag("hl");
1084 	RW = pgetflag("rw");
1085 	BR = pgetnum("br");
1086 	if ((FC = pgetnum("fc")) < 0)
1087 		FC = 0;
1088 	if ((FS = pgetnum("fs")) < 0)
1089 		FS = 0;
1090 	if ((XC = pgetnum("xc")) < 0)
1091 		XC = 0;
1092 	if ((XS = pgetnum("xs")) < 0)
1093 		XS = 0;
1094 	tof = !pgetflag("fo");
1095 }
1096 
1097 /*
1098  * Acquire line printer or remote connection.
1099  */
1100 openpr()
1101 {
1102 	register int i, n;
1103 	int resp;
1104 
1105 	if (!sendtorem && *LP) {
1106 		for (i = 1; ; i = i < 32 ? i << 1 : i) {
1107 			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1108 			if (pfd >= 0)
1109 				break;
1110 			if (errno == ENOENT) {
1111 				syslog(LOG_ERR, "%s: %m", LP);
1112 				exit(1);
1113 			}
1114 			if (i == 1)
1115 				status("waiting for %s to become ready (offline ?)", printer);
1116 			sleep(i);
1117 		}
1118 		if (isatty(pfd))
1119 			setty();
1120 		status("%s is ready and printing", printer);
1121 	} else if (RM != NULL) {
1122 		for (i = 1; ; i = i < 256 ? i << 1 : i) {
1123 			resp = -1;
1124 			pfd = getport(RM);
1125 			if (pfd >= 0) {
1126 				(void) sprintf(line, "\2%s\n", RP);
1127 				n = strlen(line);
1128 				if (write(pfd, line, n) == n &&
1129 				    (resp = response()) == '\0')
1130 					break;
1131 				(void) close(pfd);
1132 			}
1133 			if (i == 1) {
1134 				if (resp < 0)
1135 					status("waiting for %s to come up", RM);
1136 				else {
1137 					status("waiting for queue to be enabled on %s", RM);
1138 					i = 256;
1139 				}
1140 			}
1141 			sleep(i);
1142 		}
1143 		status("sending to %s", RM);
1144 		remote = 1;
1145 	} else {
1146 		syslog(LOG_ERR, "%s: no line printer device or host name",
1147 			printer);
1148 		exit(1);
1149 	}
1150 	/*
1151 	 * Start up an output filter, if needed.
1152 	 */
1153 	if (!remote && OF) {
1154 		int p[2];
1155 		char *cp;
1156 
1157 		pipe(p);
1158 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1159 			dup2(p[0], 0);		/* pipe is std in */
1160 			dup2(pfd, 1);		/* printer is std out */
1161 			for (i = 3; i < NOFILE; i++)
1162 				(void) close(i);
1163 			if ((cp = rindex(OF, '/')) == NULL)
1164 				cp = OF;
1165 			else
1166 				cp++;
1167 			execl(OF, cp, width, length, 0);
1168 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1169 			exit(1);
1170 		}
1171 		(void) close(p[0]);		/* close input side */
1172 		ofd = p[1];			/* use pipe for output */
1173 	} else {
1174 		ofd = pfd;
1175 		ofilter = 0;
1176 	}
1177 }
1178 
1179 struct bauds {
1180 	int	baud;
1181 	int	speed;
1182 } bauds[] = {
1183 	50,	B50,
1184 	75,	B75,
1185 	110,	B110,
1186 	134,	B134,
1187 	150,	B150,
1188 	200,	B200,
1189 	300,	B300,
1190 	600,	B600,
1191 	1200,	B1200,
1192 	1800,	B1800,
1193 	2400,	B2400,
1194 	4800,	B4800,
1195 	9600,	B9600,
1196 	19200,	EXTA,
1197 	38400,	EXTB,
1198 	0,	0
1199 };
1200 
1201 /*
1202  * setup tty lines.
1203  */
1204 setty()
1205 {
1206 	struct sgttyb ttybuf;
1207 	register struct bauds *bp;
1208 
1209 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1210 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1211 		exit(1);
1212 	}
1213 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1214 		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
1215 		exit(1);
1216 	}
1217 	if (BR > 0) {
1218 		for (bp = bauds; bp->baud; bp++)
1219 			if (BR == bp->baud)
1220 				break;
1221 		if (!bp->baud) {
1222 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1223 			exit(1);
1224 		}
1225 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1226 	}
1227 	ttybuf.sg_flags &= ~FC;
1228 	ttybuf.sg_flags |= FS;
1229 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1230 		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
1231 		exit(1);
1232 	}
1233 	if (XC) {
1234 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1235 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
1236 			exit(1);
1237 		}
1238 	}
1239 	if (XS) {
1240 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1241 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
1242 			exit(1);
1243 		}
1244 	}
1245 }
1246 
1247 /*VARARGS1*/
1248 status(msg, a1, a2, a3)
1249 	char *msg;
1250 {
1251 	register int fd;
1252 	char buf[BUFSIZ];
1253 
1254 	umask(0);
1255 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1256 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1257 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1258 		exit(1);
1259 	}
1260 	ftruncate(fd, 0);
1261 	sprintf(buf, msg, a1, a2, a3);
1262 	strcat(buf, "\n");
1263 	(void) write(fd, buf, strlen(buf));
1264 	(void) close(fd);
1265 }
1266