xref: /csrg-svn/usr.sbin/lpr/lpd/printjob.c (revision 38736)
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.9 (Berkeley) 08/22/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 	char *s;
1025 
1026 	if ((status = pgetent(line, printer)) < 0) {
1027 		syslog(LOG_ERR, "can't open printer description file");
1028 		exit(1);
1029 	} else if (status == 0) {
1030 		syslog(LOG_ERR, "unknown printer: %s", printer);
1031 		exit(1);
1032 	}
1033 	if ((LP = pgetstr("lp", &bp)) == NULL)
1034 		LP = _PATH_DEFDEVLP;
1035 	if ((RP = pgetstr("rp", &bp)) == NULL)
1036 		RP = DEFLP;
1037 	if ((LO = pgetstr("lo", &bp)) == NULL)
1038 		LO = DEFLOCK;
1039 	if ((ST = pgetstr("st", &bp)) == NULL)
1040 		ST = DEFSTAT;
1041 	if ((LF = pgetstr("lf", &bp)) == NULL)
1042 		LF = _PATH_CONSOLE;
1043 	if ((SD = pgetstr("sd", &bp)) == NULL)
1044 		SD = _PATH_DEFSPOOL;
1045 	if ((DU = pgetnum("du")) < 0)
1046 		DU = DEFUID;
1047 	if ((FF = pgetstr("ff", &bp)) == NULL)
1048 		FF = DEFFF;
1049 	if ((PW = pgetnum("pw")) < 0)
1050 		PW = DEFWIDTH;
1051 	sprintf(&width[2], "%d", PW);
1052 	if ((PL = pgetnum("pl")) < 0)
1053 		PL = DEFLENGTH;
1054 	sprintf(&length[2], "%d", PL);
1055 	if ((PX = pgetnum("px")) < 0)
1056 		PX = 0;
1057 	sprintf(&pxwidth[2], "%d", PX);
1058 	if ((PY = pgetnum("py")) < 0)
1059 		PY = 0;
1060 	sprintf(&pxlength[2], "%d", PY);
1061 	RM = pgetstr("rm", &bp);
1062 	if (s = checkremote())
1063 		syslog(LOG_WARNING, s);
1064 
1065 	AF = pgetstr("af", &bp);
1066 	OF = pgetstr("of", &bp);
1067 	IF = pgetstr("if", &bp);
1068 	RF = pgetstr("rf", &bp);
1069 	TF = pgetstr("tf", &bp);
1070 	NF = pgetstr("nf", &bp);
1071 	DF = pgetstr("df", &bp);
1072 	GF = pgetstr("gf", &bp);
1073 	VF = pgetstr("vf", &bp);
1074 	CF = pgetstr("cf", &bp);
1075 	TR = pgetstr("tr", &bp);
1076 	RS = pgetflag("rs");
1077 	SF = pgetflag("sf");
1078 	SH = pgetflag("sh");
1079 	SB = pgetflag("sb");
1080 	HL = pgetflag("hl");
1081 	RW = pgetflag("rw");
1082 	BR = pgetnum("br");
1083 	if ((FC = pgetnum("fc")) < 0)
1084 		FC = 0;
1085 	if ((FS = pgetnum("fs")) < 0)
1086 		FS = 0;
1087 	if ((XC = pgetnum("xc")) < 0)
1088 		XC = 0;
1089 	if ((XS = pgetnum("xs")) < 0)
1090 		XS = 0;
1091 	tof = !pgetflag("fo");
1092 }
1093 
1094 /*
1095  * Acquire line printer or remote connection.
1096  */
1097 openpr()
1098 {
1099 	register int i, n;
1100 	int resp;
1101 
1102 	if (!sendtorem && *LP) {
1103 		for (i = 1; ; i = i < 32 ? i << 1 : i) {
1104 			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1105 			if (pfd >= 0)
1106 				break;
1107 			if (errno == ENOENT) {
1108 				syslog(LOG_ERR, "%s: %m", LP);
1109 				exit(1);
1110 			}
1111 			if (i == 1)
1112 				status("waiting for %s to become ready (offline ?)", printer);
1113 			sleep(i);
1114 		}
1115 		if (isatty(pfd))
1116 			setty();
1117 		status("%s is ready and printing", printer);
1118 	} else if (RM != NULL) {
1119 		for (i = 1; ; i = i < 256 ? i << 1 : i) {
1120 			resp = -1;
1121 			pfd = getport(RM);
1122 			if (pfd >= 0) {
1123 				(void) sprintf(line, "\2%s\n", RP);
1124 				n = strlen(line);
1125 				if (write(pfd, line, n) == n &&
1126 				    (resp = response()) == '\0')
1127 					break;
1128 				(void) close(pfd);
1129 			}
1130 			if (i == 1) {
1131 				if (resp < 0)
1132 					status("waiting for %s to come up", RM);
1133 				else {
1134 					status("waiting for queue to be enabled on %s", RM);
1135 					i = 256;
1136 				}
1137 			}
1138 			sleep(i);
1139 		}
1140 		status("sending to %s", RM);
1141 		remote = 1;
1142 	} else {
1143 		syslog(LOG_ERR, "%s: no line printer device or host name",
1144 			printer);
1145 		exit(1);
1146 	}
1147 	/*
1148 	 * Start up an output filter, if needed.
1149 	 */
1150 	if (OF) {
1151 		int p[2];
1152 		char *cp;
1153 
1154 		pipe(p);
1155 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1156 			dup2(p[0], 0);		/* pipe is std in */
1157 			dup2(pfd, 1);		/* printer is std out */
1158 			for (i = 3; i < NOFILE; i++)
1159 				(void) close(i);
1160 			if ((cp = rindex(OF, '/')) == NULL)
1161 				cp = OF;
1162 			else
1163 				cp++;
1164 			execl(OF, cp, width, length, 0);
1165 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1166 			exit(1);
1167 		}
1168 		(void) close(p[0]);		/* close input side */
1169 		ofd = p[1];			/* use pipe for output */
1170 	} else {
1171 		ofd = pfd;
1172 		ofilter = 0;
1173 	}
1174 }
1175 
1176 struct bauds {
1177 	int	baud;
1178 	int	speed;
1179 } bauds[] = {
1180 	50,	B50,
1181 	75,	B75,
1182 	110,	B110,
1183 	134,	B134,
1184 	150,	B150,
1185 	200,	B200,
1186 	300,	B300,
1187 	600,	B600,
1188 	1200,	B1200,
1189 	1800,	B1800,
1190 	2400,	B2400,
1191 	4800,	B4800,
1192 	9600,	B9600,
1193 	19200,	EXTA,
1194 	38400,	EXTB,
1195 	0,	0
1196 };
1197 
1198 /*
1199  * setup tty lines.
1200  */
1201 setty()
1202 {
1203 	struct sgttyb ttybuf;
1204 	register struct bauds *bp;
1205 
1206 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1207 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1208 		exit(1);
1209 	}
1210 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1211 		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
1212 		exit(1);
1213 	}
1214 	if (BR > 0) {
1215 		for (bp = bauds; bp->baud; bp++)
1216 			if (BR == bp->baud)
1217 				break;
1218 		if (!bp->baud) {
1219 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1220 			exit(1);
1221 		}
1222 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1223 	}
1224 	ttybuf.sg_flags &= ~FC;
1225 	ttybuf.sg_flags |= FS;
1226 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1227 		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
1228 		exit(1);
1229 	}
1230 	if (XC) {
1231 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1232 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
1233 			exit(1);
1234 		}
1235 	}
1236 	if (XS) {
1237 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1238 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
1239 			exit(1);
1240 		}
1241 	}
1242 }
1243 
1244 /*VARARGS1*/
1245 status(msg, a1, a2, a3)
1246 	char *msg;
1247 {
1248 	register int fd;
1249 	char buf[BUFSIZ];
1250 
1251 	umask(0);
1252 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1253 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1254 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1255 		exit(1);
1256 	}
1257 	ftruncate(fd, 0);
1258 	sprintf(buf, msg, a1, a2, a3);
1259 	strcat(buf, "\n");
1260 	(void) write(fd, buf, strlen(buf));
1261 	(void) close(fd);
1262 }
1263