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