xref: /netbsd-src/usr.sbin/lpr/lpd/printjob.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: printjob.c,v 1.56 2011/08/30 19:27:37 joerg Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 
35 #ifndef lint
36 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\
37  The Regents of the University of California.  All rights reserved.");
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)printjob.c	8.7 (Berkeley) 5/10/95";
43 #else
44 __RCSID("$NetBSD: printjob.c,v 1.56 2011/08/30 19:27:37 joerg Exp $");
45 #endif
46 #endif /* not lint */
47 
48 
49 /*
50  * printjob -- print jobs in the queue.
51  *
52  *	NOTE: the lock file is used to pass information to lpq and lprm.
53  *	it does not need to be removed because file locks are dynamic.
54  */
55 
56 #include <sys/param.h>
57 #include <sys/wait.h>
58 #include <sys/stat.h>
59 #include <sys/types.h>
60 #include <sys/file.h>
61 
62 #include <pwd.h>
63 #include <unistd.h>
64 #include <sys/uio.h>
65 #include <signal.h>
66 #include <termios.h>
67 #include <syslog.h>
68 #include <fcntl.h>
69 #include <dirent.h>
70 #include <errno.h>
71 #include <stdio.h>
72 #include <string.h>
73 #include <stdlib.h>
74 #include <ctype.h>
75 #include "lp.h"
76 #include "lp.local.h"
77 #include "pathnames.h"
78 #include "extern.h"
79 
80 #define DORETURN	0	/* absorb fork error */
81 #define DOABORT		1	/* abort if dofork fails */
82 
83 /*
84  * Error tokens
85  */
86 #define REPRINT		-2
87 #define ERROR		-1
88 #define	OK		0
89 #define	FATALERR	1
90 #define	NOACCT		2
91 #define	FILTERERR	3
92 #define	ACCESS		4
93 
94 static dev_t	fdev;		/* device of file pointed to by symlink */
95 static ino_t	fino;		/* inode of file pointed to by symlink */
96 static FILE	*cfp;		/* control file */
97 static int	child;		/* id of any filters */
98 static int	lfd;		/* lock file descriptor */
99 static int	ofd;		/* output filter file descriptor */
100 static int	ofilter;	/* id of output filter, if any */
101 static int	pfd;		/* printer file descriptor */
102 static int	pid;		/* pid of lpd process */
103 static int	prchild;	/* id of pr process */
104 static char	title[80];	/* ``pr'' title */
105 static int	tof;		/* true if at top of form */
106 
107 static char	class[32];		/* classification field */
108 static char	fromhost[32];		/* user's host machine */
109 				/* indentation size in static characters */
110 static char	indent[10] = "-i0";
111 static char	jobname[100];		/* job or file name */
112 static char	length[10] = "-l";	/* page length in lines */
113 static char	logname[32];		/* user's login name */
114 static char	pxlength[10] = "-y";	/* page length in pixels */
115 static char	pxwidth[10] = "-x";	/* page width in pixels */
116 static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
117 static char	tempremote[] = "remoteXXXXXX"; /* file name for remote filter */
118 static char	width[10] = "-w";	/* page width in static characters */
119 
120 __dead static void	abortpr(int);
121 static void	banner(char *, char *);
122 static int	dofork(int);
123 static int	dropit(int);
124 static void	init(void);
125 static void	setup_ofilter(int);
126 static void	close_ofilter(void);
127 static void	openpr(void);
128 static void	opennet(void);
129 static void	opentty(void);
130 static void	openrem(void);
131 static int	print(int, char *);
132 static int	printit(char *);
133 static void	pstatus(const char *, ...)
134 	__attribute__((__format__(__printf__, 1, 2)));
135 static char	response(void);
136 static void	scan_out(int, char *, int);
137 static char	*scnline(int, char *, int);
138 static int	sendfile(int, char *);
139 static int	sendit(char *);
140 static void	sendmail(char *, int);
141 static void	setty(void);
142 static void	alarmer(int);
143 
144 void
145 printjob(void)
146 {
147 	struct stat stb;
148 	struct queue *q, **qp;
149 	struct queue **queue;
150 	int i, nitems, fd;
151 	off_t pidoff;
152 	int errcnt, count = 0;
153 
154 	init();					/* set up capabilities */
155 	(void)write(STDOUT_FILENO, "", 1);	/* ack that daemon is started */
156 
157 	/* set up log file */
158 	if ((fd = open(LF, O_WRONLY|O_APPEND, 0664)) < 0) {
159 		syslog(LOG_ERR, "%s: %m", LF);
160 		fd = open(_PATH_DEVNULL, O_WRONLY);
161 	}
162 	if (fd > 0) {
163 		(void) dup2(fd, STDERR_FILENO);
164 		(void) close(fd);
165 	} else
166 		(void)close(STDERR_FILENO);
167 
168 	setgid(getegid());
169 	pid = getpid();				/* for use with lprm */
170 	setpgrp(0, pid);
171 	signal(SIGHUP, abortpr);
172 	signal(SIGINT, abortpr);
173 	signal(SIGQUIT, abortpr);
174 	signal(SIGTERM, abortpr);
175 
176 	/*
177 	 * uses short form file names
178 	 */
179 	if (chdir(SD) < 0) {
180 		syslog(LOG_ERR, "%s: %m", SD);
181 		exit(1);
182 	}
183 	if (stat(LO, &stb) == 0 && (stb.st_mode & S_IXUSR))
184 		exit(0);		/* printing disabled */
185 	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
186 	if (lfd < 0) {
187 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
188 		exit(1);
189 	}
190 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
191 		if (errno == EWOULDBLOCK)	/* active daemon present */
192 			exit(0);
193 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
194 		exit(1);
195 	}
196 	ftruncate(lfd, 0);
197 	/*
198 	 * write process id for others to know
199 	 */
200 	pidoff = i = snprintf(line, sizeof(line), "%u\n", pid);
201 	if (write(lfd, line, i) != i) {
202 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
203 		exit(1);
204 	}
205 
206 	/*
207 	 * create the temp filenames.
208 	 * XXX	arguably we should keep the fds open and fdopen(3) dup()s,
209 	 * XXX	but we're in a protected directory so it shouldn't matter.
210 	 */
211 	if ((fd = mkstemp(tempfile)) != -1) {
212 		(void)close(fd);
213 		(void)unlink(tempfile);
214 	}
215 	if ((fd = mkstemp(tempremote)) != -1) {
216 		(void)close(fd);
217 		(void)unlink(tempremote);
218 	}
219 
220 	/*
221 	 * search the spool directory for work and sort by queue order.
222 	 */
223 	if ((nitems = getq(&queue)) < 0) {
224 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
225 		exit(1);
226 	}
227 	if (nitems == 0)		/* no work to do */
228 		exit(0);
229 	if (stb.st_mode & S_IXOTH) {	/* reset queue flag */
230 		stb.st_mode &= ~S_IXOTH;
231 		if (fchmod(lfd, stb.st_mode & 0777) < 0)
232 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
233 	}
234 	openpr();			/* open printer or remote */
235 again:
236 	/*
237 	 * we found something to do now do it --
238 	 *    write the name of the current control file into the lock file
239 	 *    so the spool queue program can tell what we're working on
240 	 */
241 	for (qp = queue; nitems--; free((char *) q)) {
242 		q = *qp++;
243 		if (stat(q->q_name, &stb) < 0)
244 			continue;
245 		errcnt = 0;
246 	restart:
247 		(void)lseek(lfd, pidoff, 0);
248 		i = snprintf(line, sizeof(line), "%s\n", q->q_name);
249 		if (write(lfd, line, i) != i)
250 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
251 		if (!remote)
252 			i = printit(q->q_name);
253 		else
254 			i = sendit(q->q_name);
255 		/*
256 		 * Check to see if we are supposed to stop printing or
257 		 * if we are to rebuild the queue.
258 		 */
259 		if (fstat(lfd, &stb) == 0) {
260 			/* stop printing before starting next job? */
261 			if (stb.st_mode & S_IXUSR)
262 				goto done;
263 			/* rebuild queue (after lpc topq) */
264 			if (stb.st_mode & S_IXOTH) {
265 				for (free((char *) q); nitems--; free((char *) q))
266 					q = *qp++;
267 				stb.st_mode &= ~S_IXOTH;
268 				if (fchmod(lfd, stb.st_mode & 0777) < 0)
269 					syslog(LOG_WARNING, "%s: %s: %m",
270 						printer, LO);
271 				break;
272 			}
273 		}
274 		if (i == OK)		/* file ok and printed */
275 			count++;
276 		else if (i == REPRINT && ++errcnt < 5) {
277 			/* try reprinting the job */
278 			syslog(LOG_INFO, "restarting %s", printer);
279 			if (ofilter > 0)
280 				close_ofilter();
281 			(void)close(pfd);	/* close printer */
282 			if (ftruncate(lfd, pidoff) < 0)
283 				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
284 			openpr();		/* try to reopen printer */
285 			goto restart;
286 		} else {
287 			syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
288 				remote ? "sent to remote host" : "printed", q->q_name);
289 			if (i == REPRINT) {
290 				/* ensure we don't attempt this job again */
291 				(void) unlink(q->q_name);
292 				q->q_name[0] = 'd';
293 				(void) unlink(q->q_name);
294 				if (logname[0])
295 					sendmail(logname, FATALERR);
296 			}
297 		}
298 	}
299 	free(queue);
300 	queue = NULL;
301 	/*
302 	 * search the spool directory for more work.
303 	 */
304 	if ((nitems = getq(&queue)) < 0) {
305 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
306 		exit(1);
307 	}
308 	if (nitems == 0) {		/* no more work to do */
309 	done:
310 		if (count > 0) {	/* Files actually printed */
311 			if (!SF && !tof)
312 				(void)write(ofd, FF, strlen(FF));
313 			if (TR != NULL)		/* output trailer */
314 				(void)write(ofd, TR, strlen(TR));
315 		}
316 		(void)unlink(tempfile);
317 		(void)unlink(tempremote);
318 		exit(0);
319 	}
320 	goto again;
321 }
322 
323 #define FONTLEN	50
324 char	fonts[4][FONTLEN];	/* fonts for troff */
325 
326 char ifonts[4][40] = {
327 	_PATH_VFONTR,
328 	_PATH_VFONTI,
329 	_PATH_VFONTB,
330 	_PATH_VFONTS,
331 };
332 
333 /*
334  * The remaining part is the reading of the control file (cf)
335  * and performing the various actions.
336  */
337 static int
338 printit(char *file)
339 {
340 	int i;
341 	char *cp;
342 	int bombed = OK;
343 
344 	/*
345 	 * open control file; ignore if no longer there.
346 	 */
347 	if ((cfp = fopen(file, "r")) == NULL) {
348 		syslog(LOG_INFO, "%s: %s: %m", printer, file);
349 		return(OK);
350 	}
351 	/*
352 	 * Reset troff fonts.
353 	 */
354 	for (i = 0; i < 4; i++)
355 		strlcpy(fonts[i], ifonts[i], sizeof(fonts[i]));
356 	(void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
357 	indent[2] = '0';
358 	indent[3] = '\0';
359 
360 	/*
361 	 *      read the control file for work to do
362 	 *
363 	 *      file format -- first character in the line is a command
364 	 *      rest of the line is the argument.
365 	 *      valid commands are:
366 	 *
367 	 *		S -- "stat info" for symbolic link protection
368 	 *		J -- "job name" on banner page
369 	 *		C -- "class name" on banner page
370 	 *              L -- "literal" user's name to print on banner
371 	 *		T -- "title" for pr
372 	 *		H -- "host name" of machine where lpr was done
373 	 *              P -- "person" user's login name
374 	 *              I -- "indent" amount to indent output
375 	 *		R -- laser dpi "resolution"
376 	 *              f -- "file name" name of text file to print
377 	 *		l -- "file name" text file with control chars
378 	 *		p -- "file name" text file to print with pr(1)
379 	 *		t -- "file name" troff(1) file to print
380 	 *		n -- "file name" ditroff(1) file to print
381 	 *		d -- "file name" dvi file to print
382 	 *		g -- "file name" plot(1G) file to print
383 	 *		v -- "file name" plain raster file to print
384 	 *		c -- "file name" cifplot file to print
385 	 *		o -- "file name" postscript file to print
386 	 *		1 -- "R font file" for troff
387 	 *		2 -- "I font file" for troff
388 	 *		3 -- "B font file" for troff
389 	 *		4 -- "S font file" for troff
390 	 *		N -- "name" of file (used by lpq)
391 	 *              U -- "unlink" name of file to remove
392 	 *                    (after we print it. (Pass 2 only)).
393 	 *		M -- "mail" to user when done printing
394 	 *
395 	 *      get_line reads a line and expands tabs to blanks
396 	 */
397 
398 	/* pass 1 */
399 
400 	while (get_line(cfp))
401 		switch (line[0]) {
402 		case 'H':
403 			strlcpy(fromhost, line+1, sizeof(fromhost));
404 			if (class[0] == '\0')
405 				strlcpy(class, line+1, sizeof(class));
406 			continue;
407 
408 		case 'P':
409 			strlcpy(logname, line+1, sizeof(logname));
410 			if (RS) {			/* restricted */
411 				if (getpwnam(logname) == NULL) {
412 					bombed = NOACCT;
413 					sendmail(line+1, bombed);
414 					goto pass2;
415 				}
416 			}
417 			continue;
418 
419 		case 'S':
420 			cp = line+1;
421 			i = 0;
422 			while (*cp >= '0' && *cp <= '9')
423 				i = i * 10 + (*cp++ - '0');
424 			fdev = i;
425 			cp++;
426 			i = 0;
427 			while (*cp >= '0' && *cp <= '9')
428 				i = i * 10 + (*cp++ - '0');
429 			fino = i;
430 			continue;
431 
432 		case 'J':
433 			if (line[1] != '\0')
434 				strlcpy(jobname, line+1, sizeof(jobname));
435 			else {
436 				jobname[0] = ' ';
437 				jobname[1] = '\0';
438 			}
439 			continue;
440 
441 		case 'C':
442 			if (line[1] != '\0')
443 				strlcpy(class, line+1, sizeof(class));
444 			else if (class[0] == '\0') {
445 				gethostname(class, sizeof(class));
446 				class[sizeof(class) - 1] = '\0';
447 			}
448 			continue;
449 
450 		case 'T':	/* header title for pr */
451 			strlcpy(title, line+1, sizeof(title));
452 			continue;
453 
454 		case 'L':	/* identification line */
455 			if (!SH && !HL)
456 				banner(line+1, jobname);
457 			continue;
458 
459 		case '1':	/* troff fonts */
460 		case '2':
461 		case '3':
462 		case '4':
463 			if (line[1] != '\0') {
464 				strlcpy(fonts[line[0]-'1'], line+1,
465 				    sizeof(fonts[line[0]-'1']));
466 			}
467 			continue;
468 
469 		case 'W':	/* page width */
470 			strlcpy(width+2, line+1, sizeof(width) - 2);
471 			continue;
472 
473 		case 'I':	/* indent amount */
474 			strlcpy(indent+2, line+1, sizeof(indent) - 2);
475 			continue;
476 
477 		default:	/* some file to print */
478 			switch (i = print(line[0], line+1)) {
479 			case ERROR:
480 				if (bombed == OK)
481 					bombed = FATALERR;
482 				break;
483 			case REPRINT:
484 				(void)fclose(cfp);
485 				return(REPRINT);
486 			case FILTERERR:
487 			case ACCESS:
488 				bombed = i;
489 				sendmail(logname, bombed);
490 			}
491 			title[0] = '\0';
492 			continue;
493 
494 		case 'N':
495 		case 'U':
496 		case 'M':
497 		case 'R':
498 			continue;
499 		}
500 
501 	/* pass 2 */
502 
503 pass2:
504 	fseek(cfp, 0L, 0);
505 	while (get_line(cfp))
506 		switch (line[0]) {
507 		case 'L':	/* identification line */
508 			if (!SH && HL)
509 				banner(line+1, jobname);
510 			continue;
511 
512 		case 'M':
513 			if (bombed < NOACCT)	/* already sent if >= NOACCT */
514 				sendmail(line+1, bombed);
515 			continue;
516 
517 		case 'U':
518 			if (strchr(line+1, '/'))
519 				continue;
520 			(void)unlink(line+1);
521 		}
522 	/*
523 	 * clean-up in case another control file exists
524 	 */
525 	(void)fclose(cfp);
526 	(void)unlink(file);
527 	return(bombed == OK ? OK : ERROR);
528 }
529 
530 /*
531  * Print a file.
532  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
533  * Return -1 if a non-recoverable error occurred,
534  * 2 if the filter detected some errors (but printed the job anyway),
535  * 1 if we should try to reprint this job and
536  * 0 if all is well.
537  * Note: all filters take stdin as the file, stdout as the printer,
538  * stderr as the log file, and must not ignore SIGINT.
539  */
540 static int
541 print(int format, char *file)
542 {
543 	FILE *fp;
544 	int status;
545 	struct stat stb;
546 	const char *prog, *av[17];
547 	char buf[BUFSIZ];
548 	int n, fi, fo, child_pid, p[2], stopped = 0, nofile;
549 
550 	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
551 		return(ERROR);
552 	/*
553 	 * Check to see if data file is a symbolic link. If so, it should
554 	 * still point to the same file or someone is trying to print
555 	 * something he shouldn't.
556 	 */
557 	if (S_ISLNK(stb.st_mode) && fstat(fi, &stb) == 0 &&
558 	    (stb.st_dev != fdev || stb.st_ino != fino))
559 		return(ACCESS);
560 	if (!SF && !tof) {		/* start on a fresh page */
561 		(void)write(ofd, FF, strlen(FF));
562 		tof = 1;
563 	}
564 	if (IF == NULL && (format == 'f' || format == 'l')) {
565 		tof = 0;
566 		while ((n = read(fi, buf, BUFSIZ)) > 0)
567 			if (write(ofd, buf, n) != n) {
568 				(void)close(fi);
569 				return(REPRINT);
570 			}
571 		(void)close(fi);
572 		return(OK);
573 	}
574 	switch (format) {
575 	case 'p':	/* print file using 'pr' */
576 		if (IF == NULL) {	/* use output filter */
577 			prog = _PATH_PR;
578 			av[0] = "pr";
579 			av[1] = width;
580 			av[2] = length;
581 			av[3] = "-h";
582 			av[4] = *title ? title : " ";
583 			av[5] = 0;
584 			fo = ofd;
585 			goto start;
586 		}
587 		pipe(p);
588 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
589 			dup2(fi, 0);		/* file is stdin */
590 			dup2(p[1], 1);		/* pipe is stdout */
591 			closelog();
592 			nofile = sysconf(_SC_OPEN_MAX);
593 			for (n = 3; n < nofile; n++)
594 				(void)close(n);
595 			execl(_PATH_PR, "pr", width, length,
596 			    "-h", *title ? title : " ", NULL);
597 			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
598 			exit(2);
599 		}
600 		(void)close(p[1]);		/* close output side */
601 		(void)close(fi);
602 		if (prchild < 0) {
603 			prchild = 0;
604 			(void)close(p[0]);
605 			return(ERROR);
606 		}
607 		fi = p[0];			/* use pipe for input */
608 	case 'f':	/* print plain text file */
609 		prog = IF;
610 		av[1] = width;
611 		av[2] = length;
612 		av[3] = indent;
613 		n = 4;
614 		break;
615 	case 'o':	/* print a postscript file */
616 		if (PF == NULL) {
617 			/* if PF is not set, handle it like an 'l' */
618 			prog = IF;
619 			av[1] = "-c";
620 			av[2] = width;
621 			av[3] = length;
622 			av[4] = indent;
623 			n = 5;
624 			break;
625 		} else {
626 			prog = PF;
627 			av[1] = pxwidth;
628 			av[2] = pxlength;
629 			n = 3;
630 			break;
631 		}
632 	case 'l':	/* like 'f' but pass control characters */
633 		prog = IF;
634 		av[1] = "-c";
635 		av[2] = width;
636 		av[3] = length;
637 		av[4] = indent;
638 		n = 5;
639 		break;
640 	case 'r':	/* print a fortran text file */
641 		prog = RF;
642 		av[1] = width;
643 		av[2] = length;
644 		n = 3;
645 		break;
646 	case 't':	/* print troff output */
647 	case 'n':	/* print ditroff output */
648 	case 'd':	/* print tex output */
649 		(void)unlink(".railmag");
650 		if ((fo = creat(".railmag", FILMOD)) < 0) {
651 			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
652 			(void)unlink(".railmag");
653 		} else {
654 			for (n = 0; n < 4; n++) {
655 				if (fonts[n][0] != '/')
656 					(void)write(fo, _PATH_VFONT,
657 					    sizeof(_PATH_VFONT) - 1);
658 				(void)write(fo, fonts[n], strlen(fonts[n]));
659 				(void)write(fo, "\n", 1);
660 			}
661 			(void)close(fo);
662 		}
663 		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
664 		av[1] = pxwidth;
665 		av[2] = pxlength;
666 		n = 3;
667 		break;
668 	case 'c':	/* print cifplot output */
669 		prog = CF;
670 		av[1] = pxwidth;
671 		av[2] = pxlength;
672 		n = 3;
673 		break;
674 	case 'g':	/* print plot(1G) output */
675 		prog = GF;
676 		av[1] = pxwidth;
677 		av[2] = pxlength;
678 		n = 3;
679 		break;
680 	case 'v':	/* print raster output */
681 		prog = VF;
682 		av[1] = pxwidth;
683 		av[2] = pxlength;
684 		n = 3;
685 		break;
686 	default:
687 		(void)close(fi);
688 		syslog(LOG_ERR, "%s: illegal format character '%c'",
689 			printer, format);
690 		return(ERROR);
691 	}
692 	if (prog == NULL) {
693 		(void)close(fi);
694 		syslog(LOG_ERR,
695 		    "%s: no filter found in printcap for format character '%c'",
696 		    printer, format);
697 		return (ERROR);
698 	}
699 	if ((av[0] = strrchr(prog, '/')) != NULL)
700 		av[0]++;
701 	else
702 		av[0] = prog;
703 	av[n++] = "-n";
704 	av[n++] = logname;
705 	if (*jobname != '\0' && strcmp(jobname, " ") != 0) {
706 		av[n++] = "-j";
707 		av[n++] = jobname;
708 	}
709 	av[n++] = "-h";
710 	av[n++] = fromhost;
711 	av[n++] = AF;
712 	av[n] = 0;
713 	fo = pfd;
714 	if (ofilter > 0) {		/* stop output filter */
715 		write(ofd, "\031\1", 2);
716 		while ((child_pid =
717 		    wait3(&status, WUNTRACED, 0)) > 0 && child_pid != ofilter)
718 			;
719 		if (WIFSTOPPED(status) == 0) {
720 			(void)close(fi);
721 			syslog(LOG_WARNING,
722 			    "%s: output filter died (retcode=%d termsig=%d)",
723 				printer, WEXITSTATUS(status), WTERMSIG(status));
724 			return(REPRINT);
725 		}
726 		stopped++;
727 	}
728 start:
729 	if ((child = dofork(DORETURN)) == 0) {	/* child */
730 		dup2(fi, 0);
731 		dup2(fo, 1);
732 		unlink(tempfile);
733 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0664);
734 		if (n >= 0)
735 			dup2(n, 2);
736 		closelog();
737 		nofile = sysconf(_SC_OPEN_MAX);
738 		for (n = 3; n < nofile; n++)
739 			(void)close(n);
740 		execv(prog, __UNCONST(av));
741 		syslog(LOG_ERR, "cannot execv %s", prog);
742 		exit(2);
743 	}
744 	if (child < 0) {
745 		child = 0;
746 		prchild = 0;
747 		tof = 0;
748 		syslog(LOG_ERR, "cannot start child process: %m");
749 		return (ERROR);
750 	}
751 	(void)close(fi);
752 	while ((child_pid = wait(&status)) > 0 && child_pid != child)
753 		;
754 	child = 0;
755 	prchild = 0;
756 	if (stopped) {		/* restart output filter */
757 		if (kill(ofilter, SIGCONT) < 0) {
758 			syslog(LOG_ERR, "cannot restart output filter");
759 			exit(1);
760 		}
761 	}
762 	tof = 0;
763 
764 	/* Copy filter output to "lf" logfile */
765 	if ((fp = fopen(tempfile, "r")) != NULL) {
766 		while (fgets(buf, sizeof(buf), fp))
767 			fputs(buf, stderr);
768 		fclose(fp);
769 	}
770 
771 	if (!WIFEXITED(status)) {
772 		syslog(LOG_WARNING,
773 		    "%s: Daemon filter '%c' terminated (pid=%d) (termsig=%d)",
774 			printer, format, (int)child_pid, WTERMSIG(status));
775 		return(ERROR);
776 	}
777 	switch (WEXITSTATUS(status)) {
778 	case 0:
779 		tof = 1;
780 		return(OK);
781 	case 1:
782 		return(REPRINT);
783 	case 2:
784 		return(ERROR);
785 	default:
786 		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
787 			printer, format, WEXITSTATUS(status));
788 		return(FILTERERR);
789 	}
790 }
791 
792 /*
793  * Send the daemon control file (cf) and any data files.
794  * Return -1 if a non-recoverable error occurred, 1 if a recoverable error and
795  * 0 if all is well.
796  */
797 static int
798 sendit(char *file)
799 {
800 	int i, err = OK;
801 	char *cp, last[BUFSIZ];
802 
803 	/*
804 	 * open control file
805 	 */
806 	if ((cfp = fopen(file, "r")) == NULL)
807 		return(OK);
808 	/*
809 	 *      read the control file for work to do
810 	 *
811 	 *      file format -- first character in the line is a command
812 	 *      rest of the line is the argument.
813 	 *      commands of interest are:
814 	 *
815 	 *            a-z -- "file name" name of file to print
816 	 *              U -- "unlink" name of file to remove
817 	 *                    (after we print it. (Pass 2 only)).
818 	 */
819 
820 	/*
821 	 * pass 1
822 	 */
823 	while (get_line(cfp)) {
824 	again:
825 		if (line[0] == 'S') {
826 			cp = line+1;
827 			i = 0;
828 			while (*cp >= '0' && *cp <= '9')
829 				i = i * 10 + (*cp++ - '0');
830 			fdev = i;
831 			cp++;
832 			i = 0;
833 			while (*cp >= '0' && *cp <= '9')
834 				i = i * 10 + (*cp++ - '0');
835 			fino = i;
836 			continue;
837 		}
838 		if (line[0] >= 'a' && line[0] <= 'z') {
839 			strlcpy(last, line, sizeof(last));
840 			while ((i = get_line(cfp)) != 0)
841 				if (strcmp(last, line))
842 					break;
843 			switch (sendfile('\3', last+1)) {
844 			case OK:
845 				if (i)
846 					goto again;
847 				break;
848 			case REPRINT:
849 				(void)fclose(cfp);
850 				return(REPRINT);
851 			case ACCESS:
852 				sendmail(logname, ACCESS);
853 			case ERROR:
854 				err = ERROR;
855 			}
856 			break;
857 		}
858 	}
859 	if (err == OK && sendfile('\2', file) > 0) {
860 		(void)fclose(cfp);
861 		return(REPRINT);
862 	}
863 	/*
864 	 * pass 2
865 	 */
866 	fseek(cfp, 0L, 0);
867 	while (get_line(cfp))
868 		if (line[0] == 'U' && strchr(line+1, '/') == 0)
869 			(void)unlink(line+1);
870 	/*
871 	 * clean-up in case another control file exists
872 	 */
873 	(void)fclose(cfp);
874 	(void)unlink(file);
875 	return(err);
876 }
877 
878 /*
879  * Send a data file to the remote machine and spool it.
880  * Return positive if we should try resending.
881  */
882 static int
883 sendfile(int type, char *file)
884 {
885 	int f, i, amt;
886 	struct stat stb;
887 	char buf[BUFSIZ];
888 	int sizerr, resp;
889 	extern int rflag;
890 	char *save_file;
891 
892 	save_file = file;
893 	if (type == '\3' && rflag && (OF || IF)) {
894 		int	save_pfd = pfd;
895 
896 		(void)unlink(tempremote);
897 		pfd = open(tempremote, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0664);
898 		if (pfd == -1) {
899 			pfd = save_pfd;
900 			return ERROR;
901 		}
902 		setup_ofilter(1);
903 		switch (i = print('f', file)) {
904 		case ERROR:
905 		case REPRINT:
906 		case FILTERERR:
907 		case ACCESS:
908 			return(i);
909 		}
910 		close_ofilter();
911 		pfd = save_pfd;
912 		file = tempremote;
913 	}
914 
915 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
916 		return(ERROR);
917 	/*
918 	 * Check to see if data file is a symbolic link. If so, it should
919 	 * still point to the same file or someone is trying to print something
920 	 * he shouldn't.
921 	 */
922 	if (S_ISLNK(stb.st_mode) && fstat(f, &stb) == 0 &&
923 	    (stb.st_dev != fdev || stb.st_ino != fino))
924 		return(ACCESS);
925 
926 	amt = snprintf(buf, sizeof(buf), "%c%lld %s\n", type,
927 	    (long long)stb.st_size, save_file);
928 	for (i = 0; ; i++) {
929 		if (write(pfd, buf, amt) != amt ||
930 		    (resp = response()) < 0 || resp == '\1') {
931 			(void)close(f);
932 			return(REPRINT);
933 		} else if (resp == '\0')
934 			break;
935 		if (i == 0)
936 			pstatus("no space on remote; waiting for queue to drain");
937 		if (i == 10)
938 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
939 				printer, RM);
940 		sleep(5 * 60);
941 	}
942 	if (i)
943 		pstatus("sending to %s", RM);
944 	sizerr = 0;
945 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
946 		struct sigaction osa, nsa;
947 
948 		amt = BUFSIZ;
949 		if (i + amt > stb.st_size)
950 			amt = stb.st_size - i;
951 		if (sizerr == 0 && read(f, buf, amt) != amt)
952 			sizerr = 1;
953 		nsa.sa_handler = alarmer;
954 		sigemptyset(&nsa.sa_mask);
955 		sigaddset(&nsa.sa_mask, SIGALRM);
956 		nsa.sa_flags = 0;
957 		(void)sigaction(SIGALRM, &nsa, &osa);
958 		alarm(wait_time);
959 		if (write(pfd, buf, amt) != amt) {
960 			alarm(0);
961 			(void)sigaction(SIGALRM, &osa, NULL);
962 			(void)close(f);
963 			return(REPRINT);
964 		}
965 		alarm(0);
966 		(void)sigaction(SIGALRM, &osa, NULL);
967 	}
968 
969 	(void)close(f);
970 	if (sizerr) {
971 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
972 		/* tell recvjob to ignore this file */
973 		(void)write(pfd, "\1", 1);
974 		return(ERROR);
975 	}
976 	if (write(pfd, "", 1) != 1 || response())
977 		return(REPRINT);
978 	return(OK);
979 }
980 
981 /*
982  * Check to make sure there have been no errors and that both programs
983  * are in sync with eachother.
984  * Return non-zero if the connection was lost.
985  */
986 static char
987 response(void)
988 {
989 	struct sigaction osa, nsa;
990 	char resp;
991 
992 	nsa.sa_handler = alarmer;
993 	sigemptyset(&nsa.sa_mask);
994 	sigaddset(&nsa.sa_mask, SIGALRM);
995 	nsa.sa_flags = 0;
996 	(void)sigaction(SIGALRM, &nsa, &osa);
997 	alarm(wait_time);
998 	if (read(pfd, &resp, 1) != 1) {
999 		syslog(LOG_INFO, "%s: lost connection", printer);
1000 		resp = -1;
1001 	}
1002 	alarm(0);
1003 	(void)sigaction(SIGALRM, &osa, NULL);
1004 	return (resp);
1005 }
1006 
1007 /*
1008  * Banner printing stuff
1009  */
1010 static void
1011 banner(char *name1, char *name2)
1012 {
1013 	time_t tvec;
1014 
1015 	time(&tvec);
1016 	if (!SF && !tof)
1017 		(void)write(ofd, FF, strlen(FF));
1018 	if (SB) {	/* short banner only */
1019 		if (class[0]) {
1020 			(void)write(ofd, class, strlen(class));
1021 			(void)write(ofd, ":", 1);
1022 		}
1023 		(void)write(ofd, name1, strlen(name1));
1024 		(void)write(ofd, "  Job: ", 7);
1025 		(void)write(ofd, name2, strlen(name2));
1026 		(void)write(ofd, "  Date: ", 8);
1027 		(void)write(ofd, ctime(&tvec), 24);
1028 		(void)write(ofd, "\n", 1);
1029 	} else {	/* normal banner */
1030 		(void)write(ofd, "\n\n\n", 3);
1031 		scan_out(ofd, name1, '\0');
1032 		(void)write(ofd, "\n\n", 2);
1033 		scan_out(ofd, name2, '\0');
1034 		if (class[0]) {
1035 			(void)write(ofd,"\n\n\n",3);
1036 			scan_out(ofd, class, '\0');
1037 		}
1038 		(void)write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
1039 		(void)write(ofd, name2, strlen(name2));
1040 		(void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
1041 		(void)write(ofd, ctime(&tvec), 24);
1042 		(void)write(ofd, "\n", 1);
1043 	}
1044 	if (!SF)
1045 		(void)write(ofd, FF, strlen(FF));
1046 	tof = 1;
1047 }
1048 
1049 static char *
1050 scnline(int key, char *p, int c)
1051 {
1052 	int scnwidth;
1053 
1054 	for (scnwidth = WIDTH; --scnwidth;) {
1055 		key <<= 1;
1056 		*p++ = key & 0200 ? c : BACKGND;
1057 	}
1058 	return (p);
1059 }
1060 
1061 #define TRC(q)	(((q)-' ')&0177)
1062 
1063 static void
1064 scan_out(int scfd, char *scsp, int dlm)
1065 {
1066 	char *strp;
1067 	int nchrs, j;
1068 	char outbuf[LINELEN+1], *sp, c, cc;
1069 	int d, scnhgt;
1070 	extern const char scnkey[][HEIGHT];	/* in lpdchar.c */
1071 
1072 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
1073 		strp = &outbuf[0];
1074 		sp = scsp;
1075 		for (nchrs = 0; ; ) {
1076 			d = dropit(c = TRC(cc = *sp++));
1077 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
1078 				for (j = WIDTH; --j;)
1079 					*strp++ = BACKGND;
1080 			else
1081 				strp = scnline(scnkey[(int)c][scnhgt-1-d],
1082 				    strp, cc);
1083 			if (*sp == dlm || *sp == '\0' ||
1084 			    nchrs++ >= PW/(WIDTH+1)-1)
1085 				break;
1086 			*strp++ = BACKGND;
1087 			*strp++ = BACKGND;
1088 		}
1089 		while (*--strp == BACKGND && strp >= outbuf)
1090 			;
1091 		strp++;
1092 		*strp++ = '\n';
1093 		(void)write(scfd, outbuf, strp-outbuf);
1094 	}
1095 }
1096 
1097 static int
1098 dropit(int c)
1099 {
1100 	switch(c) {
1101 
1102 	case TRC('_'):
1103 	case TRC(';'):
1104 	case TRC(','):
1105 	case TRC('g'):
1106 	case TRC('j'):
1107 	case TRC('p'):
1108 	case TRC('q'):
1109 	case TRC('y'):
1110 		return (DROP);
1111 
1112 	default:
1113 		return (0);
1114 	}
1115 }
1116 
1117 /*
1118  * sendmail ---
1119  *   tell people about job completion
1120  */
1121 static void
1122 sendmail(char *user, int bombed)
1123 {
1124 	int i, p[2], s, nofile;
1125 	const char *cp = NULL; /* XXX gcc */
1126 	struct stat stb;
1127 	FILE *fp;
1128 
1129 	if (user[0] == '-' || user[0] == '/' || !isprint((unsigned char)user[0]))
1130 		return;
1131 	pipe(p);
1132 	if ((s = dofork(DORETURN)) == 0) {		/* child */
1133 		dup2(p[0], 0);
1134 		closelog();
1135 		nofile = sysconf(_SC_OPEN_MAX);
1136 		for (i = 3; i < nofile; i++)
1137 			(void)close(i);
1138 		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
1139 			cp++;
1140 		else
1141 			cp = _PATH_SENDMAIL;
1142 		execl(_PATH_SENDMAIL, cp, "-t", NULL);
1143 		_exit(0);
1144 	} else if (s > 0) {				/* parent */
1145 		dup2(p[1], 1);
1146 		printf("To: %s@%s\n", user, fromhost);
1147 		printf("Subject: %s printer job \"%s\"\n", printer,
1148 			*jobname ? jobname : "<unknown>");
1149 		printf("Reply-To: root@%s\n\n", host);
1150 		printf("Your printer job ");
1151 		if (*jobname)
1152 			printf("(%s) ", jobname);
1153 		switch (bombed) {
1154 		case OK:
1155 			printf("\ncompleted successfully\n");
1156 			cp = "OK";
1157 			break;
1158 		default:
1159 		case FATALERR:
1160 			printf("\ncould not be printed\n");
1161 			cp = "FATALERR";
1162 			break;
1163 		case NOACCT:
1164 			printf("\ncould not be printed without an account on %s\n", host);
1165 			cp = "NOACCT";
1166 			break;
1167 		case FILTERERR:
1168 			cp = "FILTERERR";
1169 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1170 			    (fp = fopen(tempfile, "r")) == NULL) {
1171 				printf("\nhad some errors and may not have printed\n");
1172 				break;
1173 			}
1174 			printf("\nhad the following errors and may not have printed:\n");
1175 			while ((i = getc(fp)) != EOF)
1176 				putchar(i);
1177 			(void)fclose(fp);
1178 			break;
1179 		case ACCESS:
1180 			printf("\nwas not printed because it was not linked to the original file\n");
1181 			cp = "ACCESS";
1182 		}
1183 		fflush(stdout);
1184 		(void)close(1);
1185 	} else {
1186 		syslog(LOG_ERR, "fork for sendmail failed: %m");
1187 	}
1188 	(void)close(p[0]);
1189 	(void)close(p[1]);
1190 	if (s > 0) {
1191 		wait(NULL);
1192 		syslog(LOG_INFO, "mail sent to user %s about job %s on "
1193 		    "printer %s (%s)", user, *jobname ? jobname : "<unknown>",
1194 		    printer, cp);
1195 	}
1196 }
1197 
1198 /*
1199  * dofork - fork with retries on failure
1200  */
1201 static int
1202 dofork(int action)
1203 {
1204 	int i, child_pid;
1205 	struct passwd *pw;
1206 
1207 	for (i = 0; i < 20; i++) {
1208 		if ((child_pid = fork()) < 0) {
1209 			sleep((unsigned)(i*i));
1210 			continue;
1211 		}
1212 		/*
1213 		 * Child should run as daemon instead of root
1214 		 */
1215 		if (child_pid == 0) {
1216 			pw = getpwuid(DU);
1217 			if (pw == 0) {
1218 				syslog(LOG_ERR, "uid %ld not in password file",
1219 				    DU);
1220 				break;
1221 			}
1222 			initgroups(pw->pw_name, pw->pw_gid);
1223 			setgid(pw->pw_gid);
1224 			setuid(DU);
1225 			signal(SIGCHLD, SIG_DFL);
1226 		}
1227 		return (child_pid);
1228 	}
1229 	syslog(LOG_ERR, "can't fork");
1230 
1231 	switch (action) {
1232 	case DORETURN:
1233 		return (-1);
1234 	default:
1235 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1236 		/*FALL THRU*/
1237 	case DOABORT:
1238 		exit(1);
1239 	}
1240 	/*NOTREACHED*/
1241 }
1242 
1243 /*
1244  * Kill child processes to abort current job.
1245  */
1246 static void
1247 abortpr(int signo)
1248 {
1249 	(void)unlink(tempfile);
1250 	(void)unlink(tempremote);
1251 	kill(0, SIGINT);
1252 	if (ofilter > 0)
1253 		kill(ofilter, SIGCONT);
1254 	while (wait(NULL) > 0)
1255 		;
1256 	exit(0);
1257 }
1258 
1259 static void
1260 init(void)
1261 {
1262 	char *s;
1263 
1264 	getprintcap(printer);
1265 
1266 	FF = cgetstr(bp, "ff", &s) == -1 ? DEFFF : s;
1267 
1268 	if (cgetnum(bp, "du", &DU) < 0)
1269 		DU = DEFUID;
1270 	if (cgetnum(bp, "pw", &PW) < 0)
1271 		PW = DEFWIDTH;
1272 	(void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
1273 	if (cgetnum(bp, "pl", &PL) < 0)
1274 		PL = DEFLENGTH;
1275 	(void)snprintf(&length[2], sizeof(length) - 2, "%ld", PL);
1276 	if (cgetnum(bp,"px", &PX) < 0)
1277 		PX = 0;
1278 	(void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%ld", PX);
1279 	if (cgetnum(bp, "py", &PY) < 0)
1280 		PY = 0;
1281 	(void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%ld", PY);
1282 
1283 	AF = cgetstr(bp, "af", &s) == -1 ? NULL : s;
1284 	OF = cgetstr(bp, "of", &s) == -1 ? NULL : s;
1285 	IF = cgetstr(bp, "if", &s) == -1 ? NULL : s;
1286 	RF = cgetstr(bp, "rf", &s) == -1 ? NULL : s;
1287 	TF = cgetstr(bp, "tf", &s) == -1 ? NULL : s;
1288 	NF = cgetstr(bp, "nf", &s) == -1 ? NULL : s;
1289 	DF = cgetstr(bp, "df", &s) == -1 ? NULL : s;
1290 	GF = cgetstr(bp, "gf", &s) == -1 ? NULL : s;
1291 	VF = cgetstr(bp, "vf", &s) == -1 ? NULL : s;
1292 	CF = cgetstr(bp, "cf", &s) == -1 ? NULL : s;
1293 	PF = cgetstr(bp, "pf", &s) == -1 ? NULL : s;
1294 	TR = cgetstr(bp, "tr", &s) == -1 ? NULL : s;
1295 
1296 	RS = (cgetcap(bp, "rs", ':') != NULL);
1297 	SF = (cgetcap(bp, "sf", ':') != NULL);
1298 	SH = (cgetcap(bp, "sh", ':') != NULL);
1299 	SB = (cgetcap(bp, "sb", ':') != NULL);
1300 	HL = (cgetcap(bp, "hl", ':') != NULL);
1301 	RW = (cgetcap(bp, "rw", ':') != NULL);
1302 
1303 	cgetnum(bp, "br", &BR);
1304 	if (cgetnum(bp, "fc", &FC) < 0)
1305 		FC = 0;
1306 	if (cgetnum(bp, "fs", &FS) < 0)
1307 		FS = 0;
1308 	if (cgetnum(bp, "xc", &XC) < 0)
1309 		XC = 0;
1310 	if (cgetnum(bp, "xs", &XS) < 0)
1311 		XS = 0;
1312 	MS = cgetstr(bp, "ms", &s) == -1 ? NULL : s;
1313 
1314 	tof = (cgetcap(bp, "fo", ':') == NULL);
1315 }
1316 
1317 /*
1318  * Setup output filter - called once for local printer, or (if -r given to lpd)
1319  * once per file for remote printers
1320  */
1321 static void
1322 setup_ofilter(int check_rflag)
1323 {
1324 	extern int rflag;
1325 
1326 	if (OF && (!remote || (check_rflag && rflag))) {
1327 		int p[2];
1328 		int i, nofile;
1329 		const char *cp;
1330 
1331 		pipe(p);
1332 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1333 			dup2(p[0], 0);		/* pipe is std in */
1334 			dup2(pfd, 1);		/* printer is std out */
1335 			closelog();
1336 			nofile = sysconf(_SC_OPEN_MAX);
1337 			for (i = 3; i < nofile; i++)
1338 				(void)close(i);
1339 			if ((cp = strrchr(OF, '/')) == NULL)
1340 				cp = OF;
1341 			else
1342 				cp++;
1343 			execl(OF, cp, width, length, NULL);
1344 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1345 			exit(1);
1346 		}
1347 		(void)close(p[0]);		/* close input side */
1348 		ofd = p[1];			/* use pipe for output */
1349 	} else {
1350 		ofd = pfd;
1351 		ofilter = 0;
1352 	}
1353 }
1354 
1355 /*
1356  * Close the output filter and reset ofd back to the main pfd descriptor
1357  */
1358 static void
1359 close_ofilter(void)
1360 {
1361 	int i;
1362 
1363 	if (ofilter) {
1364 		kill(ofilter, SIGCONT);	/* to be sure */
1365 		(void)close(ofd);
1366 		ofd = pfd;
1367 		while ((i = wait(NULL)) > 0 && i != ofilter)
1368 			;
1369 		ofilter = 0;
1370 	}
1371 }
1372 
1373 /*
1374  * Acquire line printer or remote connection.
1375  */
1376 static void
1377 openpr(void)
1378 {
1379 	if (!remote && *LP) {
1380 		if (strchr(LP, '@') != NULL)
1381 			opennet();
1382 		else
1383 			opentty();
1384 	} else if (remote) {
1385 		openrem();
1386 	} else {
1387 		syslog(LOG_ERR, "%s: no line printer device or host name",
1388 			printer);
1389 		exit(1);
1390 	}
1391 
1392 	/*
1393 	 * Start up an output filter, if needed.
1394 	 */
1395 	setup_ofilter(0);
1396 }
1397 
1398 /*
1399  * Printer connected directly to the network
1400  * or to a terminal server on the net
1401  */
1402 static void
1403 opennet(void)
1404 {
1405 	int i;
1406 	int resp;
1407 
1408 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1409 		resp = -1;
1410 		pfd = getport(LP);
1411 		if (pfd < 0 && errno == ECONNREFUSED)
1412 			resp = 1;
1413 		else if (pfd >= 0) {
1414 			/*
1415 			 * need to delay a bit for rs232 lines
1416 			 * to stabilize in case printer is
1417 			 * connected via a terminal server
1418 			 */
1419 			delay(500);
1420 			break;
1421 		}
1422 		if (i == 1) {
1423 		   if (resp < 0)
1424 			pstatus("waiting for %s to come up", LP);
1425 		   else
1426 			pstatus("waiting for access to printer on %s", LP);
1427 		}
1428 		sleep(i);
1429 	}
1430 	pstatus("sending to %s", LP);
1431 }
1432 
1433 /*
1434  * Printer is connected to an RS232 port on this host
1435  */
1436 static void
1437 opentty(void)
1438 {
1439 	int i;
1440 
1441 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
1442 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1443 		if (pfd >= 0) {
1444 			delay(500);
1445 			break;
1446 		}
1447 		if (errno == ENOENT) {
1448 			syslog(LOG_ERR, "%s: %m", LP);
1449 			exit(1);
1450 		}
1451 		if (i == 1)
1452 			pstatus("waiting for %s to become ready (offline ?)",
1453 				printer);
1454 		sleep(i);
1455 	}
1456 	if (isatty(pfd))
1457 		setty();
1458 	pstatus("%s is ready and printing", printer);
1459 }
1460 
1461 /*
1462  * Printer is on a remote host
1463  */
1464 static void
1465 openrem(void)
1466 {
1467 	int i, n;
1468 	int resp;
1469 
1470 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1471 		resp = -1;
1472 		pfd = getport(RM);
1473 		if (pfd >= 0) {
1474 			n = snprintf(line, sizeof(line), "\2%s\n", RP);
1475 			if (write(pfd, line, n) == n &&
1476 			    (resp = response()) == '\0')
1477 				break;
1478 			(void) close(pfd);
1479 		}
1480 		if (i == 1) {
1481 			if (resp < 0)
1482 				pstatus("waiting for %s to come up", RM);
1483 			else {
1484 				pstatus("waiting for queue to be enabled on %s",
1485 					RM);
1486 				i = 256;
1487 			}
1488 		}
1489 		sleep(i);
1490 	}
1491 	pstatus("sending to %s", RM);
1492 }
1493 
1494 static void
1495 alarmer(int s)
1496 {
1497 	/* nothing */
1498 }
1499 
1500 #if !defined(__NetBSD__)
1501 struct bauds {
1502 	int	baud;
1503 	int	speed;
1504 } bauds[] = {
1505 	50,	B50,
1506 	75,	B75,
1507 	110,	B110,
1508 	134,	B134,
1509 	150,	B150,
1510 	200,	B200,
1511 	300,	B300,
1512 	600,	B600,
1513 	1200,	B1200,
1514 	1800,	B1800,
1515 	2400,	B2400,
1516 	4800,	B4800,
1517 	9600,	B9600,
1518 	19200,	B19200,
1519 	38400,	B38400,
1520 	57600,	B57600,
1521 	115200,	B115200,
1522 	0,	0
1523 };
1524 #endif
1525 
1526 /*
1527  * setup tty lines.
1528  */
1529 static void
1530 setty(void)
1531 {
1532 	struct info i;
1533 	char **argv, **ap, *p, *val;
1534 
1535 	i.fd = pfd;
1536 	i.set = i.wset = 0;
1537 	if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
1538 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1539 		exit(1);
1540 	}
1541 	if (tcgetattr(i.fd, &i.t) < 0) {
1542 		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
1543 		exit(1);
1544 	}
1545 	if (BR > 0) {
1546 #if !defined(__NetBSD__)
1547 		struct bauds *bp;
1548 		for (bp = bauds; bp->baud; bp++)
1549 			if (BR == bp->baud)
1550 				break;
1551 		if (!bp->baud) {
1552 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1553 			exit(1);
1554 		}
1555 		cfsetspeed(&i.t, bp->speed);
1556 #else
1557 		cfsetspeed(&i.t, BR);
1558 #endif
1559 		i.set = 1;
1560 	}
1561 	if (MS) {
1562 		if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
1563 			syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
1564 			exit(1);
1565 		}
1566 		if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
1567 			syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
1568 			       printer);
1569 
1570 		argv = (char **)calloc(256, sizeof(char *));
1571 		if (argv == NULL) {
1572 			syslog(LOG_ERR, "%s: calloc: %m", printer);
1573 			exit(1);
1574 		}
1575 		p = strdup(MS);
1576 		ap = argv;
1577 		while ((val = strsep(&p, " \t,")) != NULL) {
1578 			*ap++ = strdup(val);
1579 		}
1580 
1581 		for (; *argv; ++argv) {
1582 			if (ksearch(&argv, &i))
1583 				continue;
1584 			if (msearch(&argv, &i))
1585 				continue;
1586 			syslog(LOG_INFO, "%s: unknown stty flag: %s",
1587 			       printer, *argv);
1588 		}
1589 	} else {
1590 		if (FC) {
1591 			sttyclearflags(&i.t, FC);
1592 			i.set = 1;
1593 		}
1594 		if (FS) {
1595 			sttysetflags(&i.t, FS);
1596 			i.set = 1;
1597 		}
1598 		if (XC) {
1599 			sttyclearlflags(&i.t, XC);
1600 			i.set = 1;
1601 		}
1602 		if (XS) {
1603 			sttysetlflags(&i.t, XS);
1604 			i.set = 1;
1605 		}
1606 	}
1607 
1608 	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
1609 		syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
1610 		exit(1);
1611 	}
1612 	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
1613 		syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
1614 	return;
1615 }
1616 
1617 #include <stdarg.h>
1618 
1619 static void
1620 pstatus(const char *msg, ...)
1621 {
1622 	int fd;
1623 	char *buf;
1624 	va_list ap;
1625 	struct iovec iov[2];
1626 
1627 	umask(0);
1628 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1629 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1630 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1631 		exit(1);
1632 	}
1633 	ftruncate(fd, 0);
1634 	va_start(ap, msg);
1635 	(void)vasprintf(&buf, msg, ap);
1636 	va_end(ap);
1637 
1638 	iov[0].iov_base = buf;
1639 	iov[0].iov_len = strlen(buf);
1640 	iov[1].iov_base = __UNCONST("\n");
1641 	iov[1].iov_len = 1;
1642 	(void)writev(fd, iov, 2);
1643 	(void)close(fd);
1644 	free(buf);
1645 }
1646