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