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