Home | History | Annotate | Line # | Download | only in lpd
printjob.c revision 1.48
      1 /*	$NetBSD: printjob.c,v 1.48 2006/01/20 17:30:00 christos 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.48 2006/01/20 17:30:00 christos 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(void);
    129 static void	opentty(void);
    130 static void	openrem(void);
    131 static int	print(int, char *);
    132 static int	printit(char *);
    133 static void	pstatus(const char *, ...)
    134 	__attribute__((__format__(__printf__, 1, 2)));
    135 static char	response(void);
    136 static void	scan_out(int, char *, int);
    137 static char	*scnline(int, char *, int);
    138 static int	sendfile(int, char *);
    139 static int	sendit(char *);
    140 static void	sendmail(char *, int);
    141 static void	setty(void);
    142 static void	alarmer(int);
    143 
    144 void
    145 printjob(void)
    146 {
    147 	struct stat stb;
    148 	struct queue *q, **qp;
    149 	struct queue **queue;
    150 	int i, nitems, fd;
    151 	off_t pidoff;
    152 	int errcnt, count = 0;
    153 
    154 	init();					/* set up capabilities */
    155 	(void)write(STDOUT_FILENO, "", 1);	/* ack that daemon is started */
    156 
    157 	/* set up log file */
    158 	if ((fd = open(LF, O_WRONLY|O_APPEND, 0664)) < 0) {
    159 		syslog(LOG_ERR, "%s: %m", LF);
    160 		fd = open(_PATH_DEVNULL, O_WRONLY);
    161 	}
    162 	if (fd > 0) {
    163 		(void) dup2(fd, STDERR_FILENO);
    164 		(void) close(fd);
    165 	} else
    166 		(void)close(STDERR_FILENO);
    167 
    168 	setgid(getegid());
    169 	pid = getpid();				/* for use with lprm */
    170 	setpgrp(0, pid);
    171 	signal(SIGHUP, abortpr);
    172 	signal(SIGINT, abortpr);
    173 	signal(SIGQUIT, abortpr);
    174 	signal(SIGTERM, abortpr);
    175 
    176 	(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 	if (!remote && *LP) {
   1363 		if (strchr(LP, '@') != NULL)
   1364 			opennet();
   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(void)
   1387 {
   1388 	int i;
   1389 	int resp;
   1390 
   1391 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1392 		resp = -1;
   1393 		pfd = getport(LP);
   1394 		if (pfd < 0 && errno == ECONNREFUSED)
   1395 			resp = 1;
   1396 		else if (pfd >= 0) {
   1397 			/*
   1398 			 * need to delay a bit for rs232 lines
   1399 			 * to stabilize in case printer is
   1400 			 * connected via a terminal server
   1401 			 */
   1402 			delay(500);
   1403 			break;
   1404 		}
   1405 		if (i == 1) {
   1406 		   if (resp < 0)
   1407 			pstatus("waiting for %s to come up", LP);
   1408 		   else
   1409 			pstatus("waiting for access to printer on %s", LP);
   1410 		}
   1411 		sleep(i);
   1412 	}
   1413 	pstatus("sending to %s", LP);
   1414 }
   1415 
   1416 /*
   1417  * Printer is connected to an RS232 port on this host
   1418  */
   1419 static void
   1420 opentty(void)
   1421 {
   1422 	int i;
   1423 
   1424 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
   1425 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
   1426 		if (pfd >= 0) {
   1427 			delay(500);
   1428 			break;
   1429 		}
   1430 		if (errno == ENOENT) {
   1431 			syslog(LOG_ERR, "%s: %m", LP);
   1432 			exit(1);
   1433 		}
   1434 		if (i == 1)
   1435 			pstatus("waiting for %s to become ready (offline ?)",
   1436 				printer);
   1437 		sleep(i);
   1438 	}
   1439 	if (isatty(pfd))
   1440 		setty();
   1441 	pstatus("%s is ready and printing", printer);
   1442 }
   1443 
   1444 /*
   1445  * Printer is on a remote host
   1446  */
   1447 static void
   1448 openrem(void)
   1449 {
   1450 	int i, n;
   1451 	int resp;
   1452 
   1453 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1454 		resp = -1;
   1455 		pfd = getport(RM);
   1456 		if (pfd >= 0) {
   1457 			n = snprintf(line, sizeof(line), "\2%s\n", RP);
   1458 			if (write(pfd, line, n) == n &&
   1459 			    (resp = response()) == '\0')
   1460 				break;
   1461 			(void) close(pfd);
   1462 		}
   1463 		if (i == 1) {
   1464 			if (resp < 0)
   1465 				pstatus("waiting for %s to come up", RM);
   1466 			else {
   1467 				pstatus("waiting for queue to be enabled on %s",
   1468 					RM);
   1469 				i = 256;
   1470 			}
   1471 		}
   1472 		sleep(i);
   1473 	}
   1474 	pstatus("sending to %s", RM);
   1475 }
   1476 
   1477 static void
   1478 alarmer(int s)
   1479 {
   1480 	/* nothing */
   1481 }
   1482 
   1483 #if !defined(__NetBSD__)
   1484 struct bauds {
   1485 	int	baud;
   1486 	int	speed;
   1487 } bauds[] = {
   1488 	50,	B50,
   1489 	75,	B75,
   1490 	110,	B110,
   1491 	134,	B134,
   1492 	150,	B150,
   1493 	200,	B200,
   1494 	300,	B300,
   1495 	600,	B600,
   1496 	1200,	B1200,
   1497 	1800,	B1800,
   1498 	2400,	B2400,
   1499 	4800,	B4800,
   1500 	9600,	B9600,
   1501 	19200,	B19200,
   1502 	38400,	B38400,
   1503 	57600,	B57600,
   1504 	115200,	B115200,
   1505 	0,	0
   1506 };
   1507 #endif
   1508 
   1509 /*
   1510  * setup tty lines.
   1511  */
   1512 static void
   1513 setty(void)
   1514 {
   1515 	struct info i;
   1516 	char **argv, **ap, *p, *val;
   1517 
   1518 	i.fd = pfd;
   1519 	i.set = i.wset = 0;
   1520 	if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
   1521 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
   1522 		exit(1);
   1523 	}
   1524 	if (tcgetattr(i.fd, &i.t) < 0) {
   1525 		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
   1526 		exit(1);
   1527 	}
   1528 	if (BR > 0) {
   1529 #if !defined(__NetBSD__)
   1530 		struct bauds *bp;
   1531 		for (bp = bauds; bp->baud; bp++)
   1532 			if (BR == bp->baud)
   1533 				break;
   1534 		if (!bp->baud) {
   1535 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
   1536 			exit(1);
   1537 		}
   1538 		cfsetspeed(&i.t, bp->speed);
   1539 #else
   1540 		cfsetspeed(&i.t, BR);
   1541 #endif
   1542 		i.set = 1;
   1543 	}
   1544 	if (MS) {
   1545 		if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
   1546 			syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
   1547 			exit(1);
   1548 		}
   1549 		if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
   1550 			syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
   1551 			       printer);
   1552 
   1553 		argv = (char **)calloc(256, sizeof(char *));
   1554 		if (argv == NULL) {
   1555 			syslog(LOG_ERR, "%s: calloc: %m", printer);
   1556 			exit(1);
   1557 		}
   1558 		p = strdup(MS);
   1559 		ap = argv;
   1560 		while ((val = strsep(&p, " \t,")) != NULL) {
   1561 			*ap++ = strdup(val);
   1562 		}
   1563 
   1564 		for (; *argv; ++argv) {
   1565 			if (ksearch(&argv, &i))
   1566 				continue;
   1567 			if (msearch(&argv, &i))
   1568 				continue;
   1569 			syslog(LOG_INFO, "%s: unknown stty flag: %s",
   1570 			       printer, *argv);
   1571 		}
   1572 	} else {
   1573 		if (FC) {
   1574 			sttyclearflags(&i.t, FC);
   1575 			i.set = 1;
   1576 		}
   1577 		if (FS) {
   1578 			sttysetflags(&i.t, FS);
   1579 			i.set = 1;
   1580 		}
   1581 		if (XC) {
   1582 			sttyclearlflags(&i.t, XC);
   1583 			i.set = 1;
   1584 		}
   1585 		if (XS) {
   1586 			sttysetlflags(&i.t, XS);
   1587 			i.set = 1;
   1588 		}
   1589 	}
   1590 
   1591 	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
   1592 		syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
   1593 		exit(1);
   1594 	}
   1595 	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
   1596 		syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
   1597 	return;
   1598 }
   1599 
   1600 #include <stdarg.h>
   1601 
   1602 static void
   1603 pstatus(const char *msg, ...)
   1604 {
   1605 	int fd;
   1606 	char *buf;
   1607 	va_list ap;
   1608 	struct iovec iov[2];
   1609 
   1610 	umask(0);
   1611 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
   1612 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
   1613 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
   1614 		exit(1);
   1615 	}
   1616 	ftruncate(fd, 0);
   1617 	va_start(ap, msg);
   1618 	(void)vasprintf(&buf, msg, ap);
   1619 	va_end(ap);
   1620 
   1621 	iov[0].iov_base = buf;
   1622 	iov[0].iov_len = strlen(buf);
   1623 	iov[1].iov_base = __UNCONST("\n");
   1624 	iov[1].iov_len = 1;
   1625 	(void)writev(fd, iov, 2);
   1626 	(void)close(fd);
   1627 	free(buf);
   1628 }
   1629