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