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