Home | History | Annotate | Line # | Download | only in lpd
printjob.c revision 1.47
      1 /*	$NetBSD: printjob.c,v 1.47 2006/01/19 19:17:59 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.47 2006/01/19 19:17:59 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 	char *save_file;
    874 
    875 	save_file = file;
    876 	if (type == '\3' && rflag && (OF || IF)) {
    877 		int	save_pfd = pfd;
    878 
    879 		(void)unlink(tempremote);
    880 		pfd = open(tempremote, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0664);
    881 		if (pfd == -1) {
    882 			pfd = save_pfd;
    883 			return ERROR;
    884 		}
    885 		setup_ofilter(1);
    886 		switch (i = print('f', file)) {
    887 		case ERROR:
    888 		case REPRINT:
    889 		case FILTERERR:
    890 		case ACCESS:
    891 			return(i);
    892 		}
    893 		close_ofilter();
    894 		pfd = save_pfd;
    895 		file = tempremote;
    896 	}
    897 
    898 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
    899 		return(ERROR);
    900 	/*
    901 	 * Check to see if data file is a symbolic link. If so, it should
    902 	 * still point to the same file or someone is trying to print something
    903 	 * he shouldn't.
    904 	 */
    905 	if (S_ISLNK(stb.st_mode) && fstat(f, &stb) == 0 &&
    906 	    (stb.st_dev != fdev || stb.st_ino != fino))
    907 		return(ACCESS);
    908 
    909 	amt = snprintf(buf, sizeof(buf), "%c%lld %s\n", type,
    910 	    (long long)stb.st_size, save_file);
    911 	for (i = 0; ; i++) {
    912 		if (write(pfd, buf, amt) != amt ||
    913 		    (resp = response()) < 0 || resp == '\1') {
    914 			(void)close(f);
    915 			return(REPRINT);
    916 		} else if (resp == '\0')
    917 			break;
    918 		if (i == 0)
    919 			pstatus("no space on remote; waiting for queue to drain");
    920 		if (i == 10)
    921 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
    922 				printer, RM);
    923 		sleep(5 * 60);
    924 	}
    925 	if (i)
    926 		pstatus("sending to %s", RM);
    927 	sizerr = 0;
    928 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
    929 		struct sigaction osa, nsa;
    930 
    931 		amt = BUFSIZ;
    932 		if (i + amt > stb.st_size)
    933 			amt = stb.st_size - i;
    934 		if (sizerr == 0 && read(f, buf, amt) != amt)
    935 			sizerr = 1;
    936 		nsa.sa_handler = alarmer;
    937 		sigemptyset(&nsa.sa_mask);
    938 		sigaddset(&nsa.sa_mask, SIGALRM);
    939 		nsa.sa_flags = 0;
    940 		(void)sigaction(SIGALRM, &nsa, &osa);
    941 		alarm(wait_time);
    942 		if (write(pfd, buf, amt) != amt) {
    943 			alarm(0);
    944 			(void)sigaction(SIGALRM, &osa, NULL);
    945 			(void)close(f);
    946 			return(REPRINT);
    947 		}
    948 		alarm(0);
    949 		(void)sigaction(SIGALRM, &osa, NULL);
    950 	}
    951 
    952 	(void)close(f);
    953 	if (sizerr) {
    954 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
    955 		/* tell recvjob to ignore this file */
    956 		(void)write(pfd, "\1", 1);
    957 		return(ERROR);
    958 	}
    959 	if (write(pfd, "", 1) != 1 || response())
    960 		return(REPRINT);
    961 	return(OK);
    962 }
    963 
    964 /*
    965  * Check to make sure there have been no errors and that both programs
    966  * are in sync with eachother.
    967  * Return non-zero if the connection was lost.
    968  */
    969 static char
    970 response(void)
    971 {
    972 	struct sigaction osa, nsa;
    973 	char resp;
    974 
    975 	nsa.sa_handler = alarmer;
    976 	sigemptyset(&nsa.sa_mask);
    977 	sigaddset(&nsa.sa_mask, SIGALRM);
    978 	nsa.sa_flags = 0;
    979 	(void)sigaction(SIGALRM, &nsa, &osa);
    980 	alarm(wait_time);
    981 	if (read(pfd, &resp, 1) != 1) {
    982 		syslog(LOG_INFO, "%s: lost connection", printer);
    983 		resp = -1;
    984 	}
    985 	alarm(0);
    986 	(void)sigaction(SIGALRM, &osa, NULL);
    987 	return (resp);
    988 }
    989 
    990 /*
    991  * Banner printing stuff
    992  */
    993 static void
    994 banner(char *name1, char *name2)
    995 {
    996 	time_t tvec;
    997 
    998 	time(&tvec);
    999 	if (!SF && !tof)
   1000 		(void)write(ofd, FF, strlen(FF));
   1001 	if (SB) {	/* short banner only */
   1002 		if (class[0]) {
   1003 			(void)write(ofd, class, strlen(class));
   1004 			(void)write(ofd, ":", 1);
   1005 		}
   1006 		(void)write(ofd, name1, strlen(name1));
   1007 		(void)write(ofd, "  Job: ", 7);
   1008 		(void)write(ofd, name2, strlen(name2));
   1009 		(void)write(ofd, "  Date: ", 8);
   1010 		(void)write(ofd, ctime(&tvec), 24);
   1011 		(void)write(ofd, "\n", 1);
   1012 	} else {	/* normal banner */
   1013 		(void)write(ofd, "\n\n\n", 3);
   1014 		scan_out(ofd, name1, '\0');
   1015 		(void)write(ofd, "\n\n", 2);
   1016 		scan_out(ofd, name2, '\0');
   1017 		if (class[0]) {
   1018 			(void)write(ofd,"\n\n\n",3);
   1019 			scan_out(ofd, class, '\0');
   1020 		}
   1021 		(void)write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
   1022 		(void)write(ofd, name2, strlen(name2));
   1023 		(void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
   1024 		(void)write(ofd, ctime(&tvec), 24);
   1025 		(void)write(ofd, "\n", 1);
   1026 	}
   1027 	if (!SF)
   1028 		(void)write(ofd, FF, strlen(FF));
   1029 	tof = 1;
   1030 }
   1031 
   1032 static char *
   1033 scnline(int key, char *p, int c)
   1034 {
   1035 	int scnwidth;
   1036 
   1037 	for (scnwidth = WIDTH; --scnwidth;) {
   1038 		key <<= 1;
   1039 		*p++ = key & 0200 ? c : BACKGND;
   1040 	}
   1041 	return (p);
   1042 }
   1043 
   1044 #define TRC(q)	(((q)-' ')&0177)
   1045 
   1046 static void
   1047 scan_out(int scfd, char *scsp, int dlm)
   1048 {
   1049 	char *strp;
   1050 	int nchrs, j;
   1051 	char outbuf[LINELEN+1], *sp, c, cc;
   1052 	int d, scnhgt;
   1053 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
   1054 
   1055 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
   1056 		strp = &outbuf[0];
   1057 		sp = scsp;
   1058 		for (nchrs = 0; ; ) {
   1059 			d = dropit(c = TRC(cc = *sp++));
   1060 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
   1061 				for (j = WIDTH; --j;)
   1062 					*strp++ = BACKGND;
   1063 			else
   1064 				strp = scnline(scnkey[(int)c][scnhgt-1-d],
   1065 				    strp, cc);
   1066 			if (*sp == dlm || *sp == '\0' ||
   1067 			    nchrs++ >= PW/(WIDTH+1)-1)
   1068 				break;
   1069 			*strp++ = BACKGND;
   1070 			*strp++ = BACKGND;
   1071 		}
   1072 		while (*--strp == BACKGND && strp >= outbuf)
   1073 			;
   1074 		strp++;
   1075 		*strp++ = '\n';
   1076 		(void)write(scfd, outbuf, strp-outbuf);
   1077 	}
   1078 }
   1079 
   1080 static int
   1081 dropit(int c)
   1082 {
   1083 	switch(c) {
   1084 
   1085 	case TRC('_'):
   1086 	case TRC(';'):
   1087 	case TRC(','):
   1088 	case TRC('g'):
   1089 	case TRC('j'):
   1090 	case TRC('p'):
   1091 	case TRC('q'):
   1092 	case TRC('y'):
   1093 		return (DROP);
   1094 
   1095 	default:
   1096 		return (0);
   1097 	}
   1098 }
   1099 
   1100 /*
   1101  * sendmail ---
   1102  *   tell people about job completion
   1103  */
   1104 static void
   1105 sendmail(char *user, int bombed)
   1106 {
   1107 	int i, p[2], s, nofile;
   1108 	const char *cp = NULL; /* XXX gcc */
   1109 	struct stat stb;
   1110 	FILE *fp;
   1111 
   1112 	if (user[0] == '-' || user[0] == '/' || !isprint((unsigned char)user[0]))
   1113 		return;
   1114 	pipe(p);
   1115 	if ((s = dofork(DORETURN)) == 0) {		/* child */
   1116 		dup2(p[0], 0);
   1117 		closelog();
   1118 		nofile = sysconf(_SC_OPEN_MAX);
   1119 		for (i = 3; i < nofile; i++)
   1120 			(void)close(i);
   1121 		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
   1122 			cp++;
   1123 		else
   1124 			cp = _PATH_SENDMAIL;
   1125 		execl(_PATH_SENDMAIL, cp, "-t", 0);
   1126 		_exit(0);
   1127 	} else if (s > 0) {				/* parent */
   1128 		dup2(p[1], 1);
   1129 		printf("To: %s@%s\n", user, fromhost);
   1130 		printf("Subject: %s printer job \"%s\"\n", printer,
   1131 			*jobname ? jobname : "<unknown>");
   1132 		printf("Reply-To: root@%s\n\n", host);
   1133 		printf("Your printer job ");
   1134 		if (*jobname)
   1135 			printf("(%s) ", jobname);
   1136 		switch (bombed) {
   1137 		case OK:
   1138 			printf("\ncompleted successfully\n");
   1139 			cp = "OK";
   1140 			break;
   1141 		default:
   1142 		case FATALERR:
   1143 			printf("\ncould not be printed\n");
   1144 			cp = "FATALERR";
   1145 			break;
   1146 		case NOACCT:
   1147 			printf("\ncould not be printed without an account on %s\n", host);
   1148 			cp = "NOACCT";
   1149 			break;
   1150 		case FILTERERR:
   1151 			cp = "FILTERERR";
   1152 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
   1153 			    (fp = fopen(tempfile, "r")) == NULL) {
   1154 				printf("\nhad some errors and may not have printed\n");
   1155 				break;
   1156 			}
   1157 			printf("\nhad the following errors and may not have printed:\n");
   1158 			while ((i = getc(fp)) != EOF)
   1159 				putchar(i);
   1160 			(void)fclose(fp);
   1161 			break;
   1162 		case ACCESS:
   1163 			printf("\nwas not printed because it was not linked to the original file\n");
   1164 			cp = "ACCESS";
   1165 		}
   1166 		fflush(stdout);
   1167 		(void)close(1);
   1168 	} else {
   1169 		syslog(LOG_ERR, "fork for sendmail failed: %m");
   1170 	}
   1171 	(void)close(p[0]);
   1172 	(void)close(p[1]);
   1173 	if (s > 0) {
   1174 		wait(NULL);
   1175 		syslog(LOG_INFO, "mail sent to user %s about job %s on "
   1176 		    "printer %s (%s)", user, *jobname ? jobname : "<unknown>",
   1177 		    printer, cp);
   1178 	}
   1179 }
   1180 
   1181 /*
   1182  * dofork - fork with retries on failure
   1183  */
   1184 static int
   1185 dofork(int action)
   1186 {
   1187 	int i, child_pid;
   1188 	struct passwd *pw;
   1189 
   1190 	for (i = 0; i < 20; i++) {
   1191 		if ((child_pid = fork()) < 0) {
   1192 			sleep((unsigned)(i*i));
   1193 			continue;
   1194 		}
   1195 		/*
   1196 		 * Child should run as daemon instead of root
   1197 		 */
   1198 		if (child_pid == 0) {
   1199 			pw = getpwuid(DU);
   1200 			if (pw == 0) {
   1201 				syslog(LOG_ERR, "uid %ld not in password file",
   1202 				    DU);
   1203 				break;
   1204 			}
   1205 			initgroups(pw->pw_name, pw->pw_gid);
   1206 			setgid(pw->pw_gid);
   1207 			setuid(DU);
   1208 			signal(SIGCHLD, SIG_DFL);
   1209 		}
   1210 		return (child_pid);
   1211 	}
   1212 	syslog(LOG_ERR, "can't fork");
   1213 
   1214 	switch (action) {
   1215 	case DORETURN:
   1216 		return (-1);
   1217 	default:
   1218 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
   1219 		/*FALL THRU*/
   1220 	case DOABORT:
   1221 		exit(1);
   1222 	}
   1223 	/*NOTREACHED*/
   1224 }
   1225 
   1226 /*
   1227  * Kill child processes to abort current job.
   1228  */
   1229 static void
   1230 abortpr(int signo)
   1231 {
   1232 	(void)unlink(tempfile);
   1233 	(void)unlink(tempremote);
   1234 	kill(0, SIGINT);
   1235 	if (ofilter > 0)
   1236 		kill(ofilter, SIGCONT);
   1237 	while (wait(NULL) > 0)
   1238 		;
   1239 	exit(0);
   1240 }
   1241 
   1242 static void
   1243 init(void)
   1244 {
   1245 	char *s;
   1246 
   1247 	getprintcap(printer);
   1248 
   1249 	FF = cgetstr(bp, "ff", &s) == -1 ? DEFFF : s;
   1250 
   1251 	if (cgetnum(bp, "du", &DU) < 0)
   1252 		DU = DEFUID;
   1253 	if (cgetnum(bp, "pw", &PW) < 0)
   1254 		PW = DEFWIDTH;
   1255 	(void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
   1256 	if (cgetnum(bp, "pl", &PL) < 0)
   1257 		PL = DEFLENGTH;
   1258 	(void)snprintf(&length[2], sizeof(length) - 2, "%ld", PL);
   1259 	if (cgetnum(bp,"px", &PX) < 0)
   1260 		PX = 0;
   1261 	(void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%ld", PX);
   1262 	if (cgetnum(bp, "py", &PY) < 0)
   1263 		PY = 0;
   1264 	(void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%ld", PY);
   1265 
   1266 	AF = cgetstr(bp, "af", &s) == -1 ? NULL : s;
   1267 	OF = cgetstr(bp, "of", &s) == -1 ? NULL : s;
   1268 	IF = cgetstr(bp, "if", &s) == -1 ? NULL : s;
   1269 	RF = cgetstr(bp, "rf", &s) == -1 ? NULL : s;
   1270 	TF = cgetstr(bp, "tf", &s) == -1 ? NULL : s;
   1271 	NF = cgetstr(bp, "nf", &s) == -1 ? NULL : s;
   1272 	DF = cgetstr(bp, "df", &s) == -1 ? NULL : s;
   1273 	GF = cgetstr(bp, "gf", &s) == -1 ? NULL : s;
   1274 	VF = cgetstr(bp, "vf", &s) == -1 ? NULL : s;
   1275 	CF = cgetstr(bp, "cf", &s) == -1 ? NULL : s;
   1276 	PF = cgetstr(bp, "pf", &s) == -1 ? NULL : s;
   1277 	TR = cgetstr(bp, "tr", &s) == -1 ? NULL : s;
   1278 
   1279 	RS = (cgetcap(bp, "rs", ':') != NULL);
   1280 	SF = (cgetcap(bp, "sf", ':') != NULL);
   1281 	SH = (cgetcap(bp, "sh", ':') != NULL);
   1282 	SB = (cgetcap(bp, "sb", ':') != NULL);
   1283 	HL = (cgetcap(bp, "hl", ':') != NULL);
   1284 	RW = (cgetcap(bp, "rw", ':') != NULL);
   1285 
   1286 	cgetnum(bp, "br", &BR);
   1287 	if (cgetnum(bp, "fc", &FC) < 0)
   1288 		FC = 0;
   1289 	if (cgetnum(bp, "fs", &FS) < 0)
   1290 		FS = 0;
   1291 	if (cgetnum(bp, "xc", &XC) < 0)
   1292 		XC = 0;
   1293 	if (cgetnum(bp, "xs", &XS) < 0)
   1294 		XS = 0;
   1295 	MS = cgetstr(bp, "ms", &s) == -1 ? NULL : s;
   1296 
   1297 	tof = (cgetcap(bp, "fo", ':') == NULL);
   1298 }
   1299 
   1300 /*
   1301  * Setup output filter - called once for local printer, or (if -r given to lpd)
   1302  * once per file for remote printers
   1303  */
   1304 static void
   1305 setup_ofilter(int check_rflag)
   1306 {
   1307 	extern int rflag;
   1308 
   1309 	if (OF && (!remote || (check_rflag && rflag))) {
   1310 		int p[2];
   1311 		int i, nofile;
   1312 		const char *cp;
   1313 
   1314 		pipe(p);
   1315 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
   1316 			dup2(p[0], 0);		/* pipe is std in */
   1317 			dup2(pfd, 1);		/* printer is std out */
   1318 			closelog();
   1319 			nofile = sysconf(_SC_OPEN_MAX);
   1320 			for (i = 3; i < nofile; i++)
   1321 				(void)close(i);
   1322 			if ((cp = strrchr(OF, '/')) == NULL)
   1323 				cp = OF;
   1324 			else
   1325 				cp++;
   1326 			execl(OF, cp, width, length, 0);
   1327 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
   1328 			exit(1);
   1329 		}
   1330 		(void)close(p[0]);		/* close input side */
   1331 		ofd = p[1];			/* use pipe for output */
   1332 	} else {
   1333 		ofd = pfd;
   1334 		ofilter = 0;
   1335 	}
   1336 }
   1337 
   1338 /*
   1339  * Close the output filter and reset ofd back to the main pfd descriptor
   1340  */
   1341 static void
   1342 close_ofilter(void)
   1343 {
   1344 	int i;
   1345 
   1346 	if (ofilter) {
   1347 		kill(ofilter, SIGCONT);	/* to be sure */
   1348 		(void)close(ofd);
   1349 		ofd = pfd;
   1350 		while ((i = wait(NULL)) > 0 && i != ofilter)
   1351 			;
   1352 		ofilter = 0;
   1353 	}
   1354 }
   1355 
   1356 /*
   1357  * Acquire line printer or remote connection.
   1358  */
   1359 static void
   1360 openpr(void)
   1361 {
   1362 	char *cp;
   1363 
   1364 	if (!remote && *LP) {
   1365 		if ((cp = strchr(LP, '@')))
   1366 			opennet(cp);
   1367 		else
   1368 			opentty();
   1369 	} else if (remote) {
   1370 		openrem();
   1371 	} else {
   1372 		syslog(LOG_ERR, "%s: no line printer device or host name",
   1373 			printer);
   1374 		exit(1);
   1375 	}
   1376 
   1377 	/*
   1378 	 * Start up an output filter, if needed.
   1379 	 */
   1380 	setup_ofilter(0);
   1381 }
   1382 
   1383 /*
   1384  * Printer connected directly to the network
   1385  * or to a terminal server on the net
   1386  */
   1387 static void
   1388 opennet(char *cp)
   1389 {
   1390 	int i;
   1391 	int resp, port;
   1392 	char save_ch;
   1393 
   1394 	save_ch = *cp;
   1395 	*cp = '\0';
   1396 	port = atoi(LP);
   1397 	if (port <= 0) {
   1398 		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
   1399 		exit(1);
   1400 	}
   1401 	*cp++ = save_ch;
   1402 
   1403 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1404 		resp = -1;
   1405 		pfd = getport(cp, port);
   1406 		if (pfd < 0 && errno == ECONNREFUSED)
   1407 			resp = 1;
   1408 		else if (pfd >= 0) {
   1409 			/*
   1410 			 * need to delay a bit for rs232 lines
   1411 			 * to stabilize in case printer is
   1412 			 * connected via a terminal server
   1413 			 */
   1414 			delay(500);
   1415 			break;
   1416 		}
   1417 		if (i == 1) {
   1418 		   if (resp < 0)
   1419 			pstatus("waiting for %s to come up", LP);
   1420 		   else
   1421 			pstatus("waiting for access to printer on %s", LP);
   1422 		}
   1423 		sleep(i);
   1424 	}
   1425 	pstatus("sending to %s port %d", cp, port);
   1426 }
   1427 
   1428 /*
   1429  * Printer is connected to an RS232 port on this host
   1430  */
   1431 static void
   1432 opentty(void)
   1433 {
   1434 	int i;
   1435 
   1436 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
   1437 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
   1438 		if (pfd >= 0) {
   1439 			delay(500);
   1440 			break;
   1441 		}
   1442 		if (errno == ENOENT) {
   1443 			syslog(LOG_ERR, "%s: %m", LP);
   1444 			exit(1);
   1445 		}
   1446 		if (i == 1)
   1447 			pstatus("waiting for %s to become ready (offline ?)",
   1448 				printer);
   1449 		sleep(i);
   1450 	}
   1451 	if (isatty(pfd))
   1452 		setty();
   1453 	pstatus("%s is ready and printing", printer);
   1454 }
   1455 
   1456 /*
   1457  * Printer is on a remote host
   1458  */
   1459 static void
   1460 openrem(void)
   1461 {
   1462 	int i, n;
   1463 	int resp;
   1464 	char *rmhost;
   1465 
   1466 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1467 		resp = -1;
   1468 		if ((rmhost = strchr(RM, '@')))
   1469 			pfd = getport(rmhost+1, atoi(RM));
   1470 		else
   1471 			pfd = getport(RM, 0);
   1472 		if (pfd >= 0) {
   1473 			n = snprintf(line, sizeof(line), "\2%s\n", RP);
   1474 			if (write(pfd, line, n) == n &&
   1475 			    (resp = response()) == '\0')
   1476 				break;
   1477 			(void) close(pfd);
   1478 		}
   1479 		if (i == 1) {
   1480 			if (resp < 0)
   1481 				pstatus("waiting for %s to come up", RM);
   1482 			else {
   1483 				pstatus("waiting for queue to be enabled on %s",
   1484 					RM);
   1485 				i = 256;
   1486 			}
   1487 		}
   1488 		sleep(i);
   1489 	}
   1490 	pstatus("sending to %s", RM);
   1491 }
   1492 
   1493 static void
   1494 alarmer(int s)
   1495 {
   1496 	/* nothing */
   1497 }
   1498 
   1499 #if !defined(__NetBSD__)
   1500 struct bauds {
   1501 	int	baud;
   1502 	int	speed;
   1503 } bauds[] = {
   1504 	50,	B50,
   1505 	75,	B75,
   1506 	110,	B110,
   1507 	134,	B134,
   1508 	150,	B150,
   1509 	200,	B200,
   1510 	300,	B300,
   1511 	600,	B600,
   1512 	1200,	B1200,
   1513 	1800,	B1800,
   1514 	2400,	B2400,
   1515 	4800,	B4800,
   1516 	9600,	B9600,
   1517 	19200,	B19200,
   1518 	38400,	B38400,
   1519 	57600,	B57600,
   1520 	115200,	B115200,
   1521 	0,	0
   1522 };
   1523 #endif
   1524 
   1525 /*
   1526  * setup tty lines.
   1527  */
   1528 static void
   1529 setty(void)
   1530 {
   1531 	struct info i;
   1532 	char **argv, **ap, *p, *val;
   1533 
   1534 	i.fd = pfd;
   1535 	i.set = i.wset = 0;
   1536 	if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
   1537 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
   1538 		exit(1);
   1539 	}
   1540 	if (tcgetattr(i.fd, &i.t) < 0) {
   1541 		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
   1542 		exit(1);
   1543 	}
   1544 	if (BR > 0) {
   1545 #if !defined(__NetBSD__)
   1546 		struct bauds *bp;
   1547 		for (bp = bauds; bp->baud; bp++)
   1548 			if (BR == bp->baud)
   1549 				break;
   1550 		if (!bp->baud) {
   1551 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
   1552 			exit(1);
   1553 		}
   1554 		cfsetspeed(&i.t, bp->speed);
   1555 #else
   1556 		cfsetspeed(&i.t, BR);
   1557 #endif
   1558 		i.set = 1;
   1559 	}
   1560 	if (MS) {
   1561 		if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
   1562 			syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
   1563 			exit(1);
   1564 		}
   1565 		if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
   1566 			syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
   1567 			       printer);
   1568 
   1569 		argv = (char **)calloc(256, sizeof(char *));
   1570 		if (argv == NULL) {
   1571 			syslog(LOG_ERR, "%s: calloc: %m", printer);
   1572 			exit(1);
   1573 		}
   1574 		p = strdup(MS);
   1575 		ap = argv;
   1576 		while ((val = strsep(&p, " \t,")) != NULL) {
   1577 			*ap++ = strdup(val);
   1578 		}
   1579 
   1580 		for (; *argv; ++argv) {
   1581 			if (ksearch(&argv, &i))
   1582 				continue;
   1583 			if (msearch(&argv, &i))
   1584 				continue;
   1585 			syslog(LOG_INFO, "%s: unknown stty flag: %s",
   1586 			       printer, *argv);
   1587 		}
   1588 	} else {
   1589 		if (FC) {
   1590 			sttyclearflags(&i.t, FC);
   1591 			i.set = 1;
   1592 		}
   1593 		if (FS) {
   1594 			sttysetflags(&i.t, FS);
   1595 			i.set = 1;
   1596 		}
   1597 		if (XC) {
   1598 			sttyclearlflags(&i.t, XC);
   1599 			i.set = 1;
   1600 		}
   1601 		if (XS) {
   1602 			sttysetlflags(&i.t, XS);
   1603 			i.set = 1;
   1604 		}
   1605 	}
   1606 
   1607 	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
   1608 		syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
   1609 		exit(1);
   1610 	}
   1611 	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
   1612 		syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
   1613 	return;
   1614 }
   1615 
   1616 #include <stdarg.h>
   1617 
   1618 static void
   1619 pstatus(const char *msg, ...)
   1620 {
   1621 	int fd;
   1622 	char *buf;
   1623 	va_list ap;
   1624 	struct iovec iov[2];
   1625 
   1626 	umask(0);
   1627 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
   1628 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
   1629 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
   1630 		exit(1);
   1631 	}
   1632 	ftruncate(fd, 0);
   1633 	va_start(ap, msg);
   1634 	(void)vasprintf(&buf, msg, ap);
   1635 	va_end(ap);
   1636 
   1637 	iov[0].iov_base = buf;
   1638 	iov[0].iov_len = strlen(buf);
   1639 	iov[1].iov_base = __UNCONST("\n");
   1640 	iov[1].iov_len = 1;
   1641 	(void)writev(fd, iov, 2);
   1642 	(void)close(fd);
   1643 	free(buf);
   1644 }
   1645