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