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