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