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