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