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