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