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