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