Home | History | Annotate | Line # | Download | only in lpd
printjob.c revision 1.16
      1 /*	$NetBSD: printjob.c,v 1.16 1997/10/05 15:12:14 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.16 1997/10/05 15:12:14 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 			continue;
    430 
    431 		case 'T':	/* header title for pr */
    432 			strncpy(title, line+1, sizeof(title) - 1);
    433 			title[sizeof(title)-1] = '\0';
    434 			continue;
    435 
    436 		case 'L':	/* identification line */
    437 			if (!SH && !HL)
    438 				banner(line+1, jobname);
    439 			continue;
    440 
    441 		case '1':	/* troff fonts */
    442 		case '2':
    443 		case '3':
    444 		case '4':
    445 			if (line[1] != '\0') {
    446 				strncpy(fonts[line[0]-'1'], line+1, FONTLEN - 1);
    447 				fonts[line[0]-'1'][50-1] = '\0';
    448 			}
    449 			continue;
    450 
    451 		case 'W':	/* page width */
    452 			strncpy(width+2, line+1, sizeof(width) - 3);
    453 			width[sizeof(width)-1] = '\0';
    454 			continue;
    455 
    456 		case 'I':	/* indent amount */
    457 			strncpy(indent+2, line+1, sizeof(indent) - 3);
    458 			indent[sizeof(indent)-1] = '\0';
    459 			continue;
    460 
    461 		default:	/* some file to print */
    462 			switch (i = print(line[0], line+1)) {
    463 			case ERROR:
    464 				if (bombed == OK)
    465 					bombed = FATALERR;
    466 				break;
    467 			case REPRINT:
    468 				(void)fclose(cfp);
    469 				return(REPRINT);
    470 			case FILTERERR:
    471 			case ACCESS:
    472 				bombed = i;
    473 				sendmail(logname, bombed);
    474 			}
    475 			title[0] = '\0';
    476 			continue;
    477 
    478 		case 'N':
    479 		case 'U':
    480 		case 'M':
    481 		case 'R':
    482 			continue;
    483 		}
    484 
    485 	/* pass 2 */
    486 
    487 pass2:
    488 	fseek(cfp, 0L, 0);
    489 	while (getline(cfp))
    490 		switch (line[0]) {
    491 		case 'L':	/* identification line */
    492 			if (!SH && HL)
    493 				banner(line+1, jobname);
    494 			continue;
    495 
    496 		case 'M':
    497 			if (bombed < NOACCT)	/* already sent if >= NOACCT */
    498 				sendmail(line+1, bombed);
    499 			continue;
    500 
    501 		case 'U':
    502 			if (strchr(line+1, '/'))
    503 				continue;
    504 			(void)unlink(line+1);
    505 		}
    506 	/*
    507 	 * clean-up in case another control file exists
    508 	 */
    509 	(void)fclose(cfp);
    510 	(void)unlink(file);
    511 	return(bombed == OK ? OK : ERROR);
    512 }
    513 
    514 /*
    515  * Print a file.
    516  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
    517  * Return -1 if a non-recoverable error occured,
    518  * 2 if the filter detected some errors (but printed the job anyway),
    519  * 1 if we should try to reprint this job and
    520  * 0 if all is well.
    521  * Note: all filters take stdin as the file, stdout as the printer,
    522  * stderr as the log file, and must not ignore SIGINT.
    523  */
    524 static int
    525 print(format, file)
    526 	int format;
    527 	char *file;
    528 {
    529 	FILE *fp;
    530 	int status;
    531 	struct stat stb;
    532 	char *prog, *av[15], buf[BUFSIZ];
    533 	int n, fi, fo, pid, p[2], stopped = 0, nofile;
    534 
    535 	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
    536 		return(ERROR);
    537 	/*
    538 	 * Check to see if data file is a symbolic link. If so, it should
    539 	 * still point to the same file or someone is trying to print
    540 	 * something he shouldn't.
    541 	 */
    542 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
    543 	    (stb.st_dev != fdev || stb.st_ino != fino))
    544 		return(ACCESS);
    545 	if (!SF && !tof) {		/* start on a fresh page */
    546 		(void)write(ofd, FF, strlen(FF));
    547 		tof = 1;
    548 	}
    549 	if (IF == NULL && (format == 'f' || format == 'l')) {
    550 		tof = 0;
    551 		while ((n = read(fi, buf, BUFSIZ)) > 0)
    552 			if (write(ofd, buf, n) != n) {
    553 				(void)close(fi);
    554 				return(REPRINT);
    555 			}
    556 		(void)close(fi);
    557 		return(OK);
    558 	}
    559 	switch (format) {
    560 	case 'p':	/* print file using 'pr' */
    561 		if (IF == NULL) {	/* use output filter */
    562 			prog = _PATH_PR;
    563 			av[0] = "pr";
    564 			av[1] = width;
    565 			av[2] = length;
    566 			av[3] = "-h";
    567 			av[4] = *title ? title : " ";
    568 			av[5] = 0;
    569 			fo = ofd;
    570 			goto start;
    571 		}
    572 		pipe(p);
    573 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
    574 			dup2(fi, 0);		/* file is stdin */
    575 			dup2(p[1], 1);		/* pipe is stdout */
    576 			closelog();
    577 			nofile = sysconf(_SC_OPEN_MAX);
    578 			for (n = 3; n < nofile; n++)
    579 				(void)close(n);
    580 			execl(_PATH_PR, "pr", width, length,
    581 			    "-h", *title ? title : " ", 0);
    582 			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
    583 			exit(2);
    584 		}
    585 		(void)close(p[1]);		/* close output side */
    586 		(void)close(fi);
    587 		if (prchild < 0) {
    588 			prchild = 0;
    589 			(void)close(p[0]);
    590 			return(ERROR);
    591 		}
    592 		fi = p[0];			/* use pipe for input */
    593 	case 'f':	/* print plain text file */
    594 		prog = IF;
    595 		av[1] = width;
    596 		av[2] = length;
    597 		av[3] = indent;
    598 		n = 4;
    599 		break;
    600 	case 'l':	/* like 'f' but pass control characters */
    601 		prog = IF;
    602 		av[1] = "-c";
    603 		av[2] = width;
    604 		av[3] = length;
    605 		av[4] = indent;
    606 		n = 5;
    607 		break;
    608 	case 'r':	/* print a fortran text file */
    609 		prog = RF;
    610 		av[1] = width;
    611 		av[2] = length;
    612 		n = 3;
    613 		break;
    614 	case 't':	/* print troff output */
    615 	case 'n':	/* print ditroff output */
    616 	case 'd':	/* print tex output */
    617 		(void)unlink(".railmag");
    618 		if ((fo = creat(".railmag", FILMOD)) < 0) {
    619 			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
    620 			(void)unlink(".railmag");
    621 		} else {
    622 			for (n = 0; n < 4; n++) {
    623 				if (fonts[n][0] != '/')
    624 					(void)write(fo, _PATH_VFONT,
    625 					    sizeof(_PATH_VFONT) - 1);
    626 				(void)write(fo, fonts[n], strlen(fonts[n]));
    627 				(void)write(fo, "\n", 1);
    628 			}
    629 			(void)close(fo);
    630 		}
    631 		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
    632 		av[1] = pxwidth;
    633 		av[2] = pxlength;
    634 		n = 3;
    635 		break;
    636 	case 'c':	/* print cifplot output */
    637 		prog = CF;
    638 		av[1] = pxwidth;
    639 		av[2] = pxlength;
    640 		n = 3;
    641 		break;
    642 	case 'g':	/* print plot(1G) output */
    643 		prog = GF;
    644 		av[1] = pxwidth;
    645 		av[2] = pxlength;
    646 		n = 3;
    647 		break;
    648 	case 'v':	/* print raster output */
    649 		prog = VF;
    650 		av[1] = pxwidth;
    651 		av[2] = pxlength;
    652 		n = 3;
    653 		break;
    654 	default:
    655 		(void)close(fi);
    656 		syslog(LOG_ERR, "%s: illegal format character '%c'",
    657 			printer, format);
    658 		return(ERROR);
    659 	}
    660 	if (prog == NULL) {
    661 		(void)close(fi);
    662 		syslog(LOG_ERR,
    663 		    "%s: no filter found in printcap for format character '%c'",
    664 		    printer, format);
    665 		return (ERROR);
    666 	}
    667 	if ((av[0] = strrchr(prog, '/')) != NULL)
    668 		av[0]++;
    669 	else
    670 		av[0] = prog;
    671 	av[n++] = "-n";
    672 	av[n++] = logname;
    673 	av[n++] = "-h";
    674 	av[n++] = fromhost;
    675 	av[n++] = AF;
    676 	av[n] = 0;
    677 	fo = pfd;
    678 	if (ofilter > 0) {		/* stop output filter */
    679 		write(ofd, "\031\1", 2);
    680 		while ((pid =
    681 		    wait3(&status, WUNTRACED, 0)) > 0 && pid != ofilter)
    682 			;
    683 		if (WIFSTOPPED(status) == 0) {
    684 			(void)close(fi);
    685 			syslog(LOG_WARNING,
    686 			    "%s: output filter died (retcode=%d termsig=%d)",
    687 				printer, WEXITSTATUS(status), WTERMSIG(status));
    688 			return(REPRINT);
    689 		}
    690 		stopped++;
    691 	}
    692 start:
    693 	if ((child = dofork(DORETURN)) == 0) {	/* child */
    694 		dup2(fi, 0);
    695 		dup2(fo, 1);
    696 		unlink(tempfile);
    697 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0664);
    698 		if (n >= 0)
    699 			dup2(n, 2);
    700 		closelog();
    701 		nofile = sysconf(_SC_OPEN_MAX);
    702 		for (n = 3; n < nofile; n++)
    703 			(void)close(n);
    704 		execv(prog, av);
    705 		syslog(LOG_ERR, "cannot execv %s", prog);
    706 		exit(2);
    707 	}
    708 	if (child < 0) {
    709 		child = 0;
    710 		prchild = 0;
    711 		tof = 0;
    712 		syslog(LOG_ERR, "cannot start child process: %m");
    713 		return (ERROR);
    714 	}
    715 	(void)close(fi);
    716 	while ((pid = wait(&status)) > 0 && pid != child)
    717 		;
    718 	child = 0;
    719 	prchild = 0;
    720 	if (stopped) {		/* restart output filter */
    721 		if (kill(ofilter, SIGCONT) < 0) {
    722 			syslog(LOG_ERR, "cannot restart output filter");
    723 			exit(1);
    724 		}
    725 	}
    726 	tof = 0;
    727 
    728 	/* Copy filter output to "lf" logfile */
    729 	if ((fp = fopen(tempfile, "r")) != NULL) {
    730 		while (fgets(buf, sizeof(buf), fp))
    731 			fputs(buf, stderr);
    732 		fclose(fp);
    733 	}
    734 
    735 	if (!WIFEXITED(status)) {
    736 		syslog(LOG_WARNING,
    737 		    "%s: Daemon filter '%c' terminated (termsig=%d)",
    738 			printer, format, WTERMSIG(status));
    739 		return(ERROR);
    740 	}
    741 	switch (WEXITSTATUS(status)) {
    742 	case 0:
    743 		tof = 1;
    744 		return(OK);
    745 	case 1:
    746 		return(REPRINT);
    747 	case 2:
    748 		return(ERROR);
    749 	default:
    750 		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
    751 			printer, format, WEXITSTATUS(status));
    752 		return(FILTERERR);
    753 	}
    754 }
    755 
    756 /*
    757  * Send the daemon control file (cf) and any data files.
    758  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
    759  * 0 if all is well.
    760  */
    761 static int
    762 sendit(file)
    763 	char *file;
    764 {
    765 	int i, err = OK;
    766 	char *cp, last[BUFSIZ];
    767 
    768 	/*
    769 	 * open control file
    770 	 */
    771 	if ((cfp = fopen(file, "r")) == NULL)
    772 		return(OK);
    773 	/*
    774 	 *      read the control file for work to do
    775 	 *
    776 	 *      file format -- first character in the line is a command
    777 	 *      rest of the line is the argument.
    778 	 *      commands of interest are:
    779 	 *
    780 	 *            a-z -- "file name" name of file to print
    781 	 *              U -- "unlink" name of file to remove
    782 	 *                    (after we print it. (Pass 2 only)).
    783 	 */
    784 
    785 	/*
    786 	 * pass 1
    787 	 */
    788 	while (getline(cfp)) {
    789 	again:
    790 		if (line[0] == 'S') {
    791 			cp = line+1;
    792 			i = 0;
    793 			while (*cp >= '0' && *cp <= '9')
    794 				i = i * 10 + (*cp++ - '0');
    795 			fdev = i;
    796 			cp++;
    797 			i = 0;
    798 			while (*cp >= '0' && *cp <= '9')
    799 				i = i * 10 + (*cp++ - '0');
    800 			fino = i;
    801 			continue;
    802 		}
    803 		if (line[0] >= 'a' && line[0] <= 'z') {
    804 			strncpy(last, line, sizeof(last) - 1);
    805 			last[sizeof(last) - 1] = '\0';
    806 			while ((i = getline(cfp)) != 0)
    807 				if (strcmp(last, line))
    808 					break;
    809 			switch (sendfile('\3', last+1)) {
    810 			case OK:
    811 				if (i)
    812 					goto again;
    813 				break;
    814 			case REPRINT:
    815 				(void)fclose(cfp);
    816 				return(REPRINT);
    817 			case ACCESS:
    818 				sendmail(logname, ACCESS);
    819 			case ERROR:
    820 				err = ERROR;
    821 			}
    822 			break;
    823 		}
    824 	}
    825 	if (err == OK && sendfile('\2', file) > 0) {
    826 		(void)fclose(cfp);
    827 		return(REPRINT);
    828 	}
    829 	/*
    830 	 * pass 2
    831 	 */
    832 	fseek(cfp, 0L, 0);
    833 	while (getline(cfp))
    834 		if (line[0] == 'U' && strchr(line+1, '/') == 0)
    835 			(void)unlink(line+1);
    836 	/*
    837 	 * clean-up in case another control file exists
    838 	 */
    839 	(void)fclose(cfp);
    840 	(void)unlink(file);
    841 	return(err);
    842 }
    843 
    844 /*
    845  * Send a data file to the remote machine and spool it.
    846  * Return positive if we should try resending.
    847  */
    848 static int
    849 sendfile(type, file)
    850 	int type;
    851 	char *file;
    852 {
    853 	int f, i, amt;
    854 	struct stat stb;
    855 	char buf[BUFSIZ];
    856 	int sizerr, resp;
    857 
    858 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
    859 		return(ERROR);
    860 	/*
    861 	 * Check to see if data file is a symbolic link. If so, it should
    862 	 * still point to the same file or someone is trying to print something
    863 	 * he shouldn't.
    864 	 */
    865 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
    866 	    (stb.st_dev != fdev || stb.st_ino != fino))
    867 		return(ACCESS);
    868 	amt = snprintf(buf, sizeof(buf), "%c%qd %s\n", type, stb.st_size, file);
    869 	for (i = 0;  ; i++) {
    870 		if (write(pfd, buf, amt) != amt ||
    871 		    (resp = response()) < 0 || resp == '\1') {
    872 			(void)close(f);
    873 			return(REPRINT);
    874 		} else if (resp == '\0')
    875 			break;
    876 		if (i == 0)
    877 			pstatus("no space on remote; waiting for queue to drain");
    878 		if (i == 10)
    879 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
    880 				printer, RM);
    881 		sleep(5 * 60);
    882 	}
    883 	if (i)
    884 		pstatus("sending to %s", RM);
    885 	sizerr = 0;
    886 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
    887 		amt = BUFSIZ;
    888 		if (i + amt > stb.st_size)
    889 			amt = stb.st_size - i;
    890 		if (sizerr == 0 && read(f, buf, amt) != amt)
    891 			sizerr = 1;
    892 		if (write(pfd, buf, amt) != amt) {
    893 			(void)close(f);
    894 			return(REPRINT);
    895 		}
    896 	}
    897 
    898 	(void)close(f);
    899 	if (sizerr) {
    900 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
    901 		/* tell recvjob to ignore this file */
    902 		(void)write(pfd, "\1", 1);
    903 		return(ERROR);
    904 	}
    905 	if (write(pfd, "", 1) != 1 || response())
    906 		return(REPRINT);
    907 	return(OK);
    908 }
    909 
    910 /*
    911  * Check to make sure there have been no errors and that both programs
    912  * are in sync with eachother.
    913  * Return non-zero if the connection was lost.
    914  */
    915 static char
    916 response()
    917 {
    918 	char resp;
    919 
    920 	if (read(pfd, &resp, 1) != 1) {
    921 		syslog(LOG_INFO, "%s: lost connection", printer);
    922 		return(-1);
    923 	}
    924 	return(resp);
    925 }
    926 
    927 /*
    928  * Banner printing stuff
    929  */
    930 static void
    931 banner(name1, name2)
    932 	char *name1, *name2;
    933 {
    934 	time_t tvec;
    935 
    936 	time(&tvec);
    937 	if (!SF && !tof)
    938 		(void)write(ofd, FF, strlen(FF));
    939 	if (SB) {	/* short banner only */
    940 		if (class[0]) {
    941 			(void)write(ofd, class, strlen(class));
    942 			(void)write(ofd, ":", 1);
    943 		}
    944 		(void)write(ofd, name1, strlen(name1));
    945 		(void)write(ofd, "  Job: ", 7);
    946 		(void)write(ofd, name2, strlen(name2));
    947 		(void)write(ofd, "  Date: ", 8);
    948 		(void)write(ofd, ctime(&tvec), 24);
    949 		(void)write(ofd, "\n", 1);
    950 	} else {	/* normal banner */
    951 		(void)write(ofd, "\n\n\n", 3);
    952 		scan_out(ofd, name1, '\0');
    953 		(void)write(ofd, "\n\n", 2);
    954 		scan_out(ofd, name2, '\0');
    955 		if (class[0]) {
    956 			(void)write(ofd,"\n\n\n",3);
    957 			scan_out(ofd, class, '\0');
    958 		}
    959 		(void)write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
    960 		(void)write(ofd, name2, strlen(name2));
    961 		(void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
    962 		(void)write(ofd, ctime(&tvec), 24);
    963 		(void)write(ofd, "\n", 1);
    964 	}
    965 	if (!SF)
    966 		(void)write(ofd, FF, strlen(FF));
    967 	tof = 1;
    968 }
    969 
    970 static char *
    971 scnline(key, p, c)
    972 	int key;
    973 	char *p;
    974 	int c;
    975 {
    976 	int scnwidth;
    977 
    978 	for (scnwidth = WIDTH; --scnwidth;) {
    979 		key <<= 1;
    980 		*p++ = key & 0200 ? c : BACKGND;
    981 	}
    982 	return (p);
    983 }
    984 
    985 #define TRC(q)	(((q)-' ')&0177)
    986 
    987 static void
    988 scan_out(scfd, scsp, dlm)
    989 	int scfd, dlm;
    990 	char *scsp;
    991 {
    992 	char *strp;
    993 	int nchrs, j;
    994 	char outbuf[LINELEN+1], *sp, c, cc;
    995 	int d, scnhgt;
    996 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
    997 
    998 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
    999 		strp = &outbuf[0];
   1000 		sp = scsp;
   1001 		for (nchrs = 0; ; ) {
   1002 			d = dropit(c = TRC(cc = *sp++));
   1003 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
   1004 				for (j = WIDTH; --j;)
   1005 					*strp++ = BACKGND;
   1006 			else
   1007 				strp = scnline(scnkey[(int)c][scnhgt-1-d],
   1008 				    strp, cc);
   1009 			if (*sp == dlm || *sp == '\0' ||
   1010 			    nchrs++ >= PW/(WIDTH+1)-1)
   1011 				break;
   1012 			*strp++ = BACKGND;
   1013 			*strp++ = BACKGND;
   1014 		}
   1015 		while (*--strp == BACKGND && strp >= outbuf)
   1016 			;
   1017 		strp++;
   1018 		*strp++ = '\n';
   1019 		(void)write(scfd, outbuf, strp-outbuf);
   1020 	}
   1021 }
   1022 
   1023 static int
   1024 dropit(c)
   1025 	int c;
   1026 {
   1027 	switch(c) {
   1028 
   1029 	case TRC('_'):
   1030 	case TRC(';'):
   1031 	case TRC(','):
   1032 	case TRC('g'):
   1033 	case TRC('j'):
   1034 	case TRC('p'):
   1035 	case TRC('q'):
   1036 	case TRC('y'):
   1037 		return (DROP);
   1038 
   1039 	default:
   1040 		return (0);
   1041 	}
   1042 }
   1043 
   1044 /*
   1045  * sendmail ---
   1046  *   tell people about job completion
   1047  */
   1048 static void
   1049 sendmail(user, bombed)
   1050 	char *user;
   1051 	int bombed;
   1052 {
   1053 	int i, p[2], s, nofile;
   1054 	char *cp = NULL; /* XXX gcc */
   1055 	struct stat stb;
   1056 	FILE *fp;
   1057 
   1058 	if (user[0] == '-' || user[0] == '/' || !isprint(user[0]))
   1059 		return;
   1060 	pipe(p);
   1061 	if ((s = dofork(DORETURN)) == 0) {		/* child */
   1062 		dup2(p[0], 0);
   1063 		closelog();
   1064 		nofile = sysconf(_SC_OPEN_MAX);
   1065 		for (i = 3; i < nofile; i++)
   1066 			(void)close(i);
   1067 		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
   1068 			cp++;
   1069 		else
   1070 			cp = _PATH_SENDMAIL;
   1071 		execl(_PATH_SENDMAIL, cp, "-t", 0);
   1072 		exit(0);
   1073 	} else if (s > 0) {				/* parent */
   1074 		dup2(p[1], 1);
   1075 		printf("To: %s@%s\n", user, fromhost);
   1076 		printf("Subject: %s printer job \"%s\"\n", printer,
   1077 			*jobname ? jobname : "<unknown>");
   1078 		printf("Reply-To: root@%s\n\n", host);
   1079 		printf("Your printer job ");
   1080 		if (*jobname)
   1081 			printf("(%s) ", jobname);
   1082 		switch (bombed) {
   1083 		case OK:
   1084 			printf("\ncompleted successfully\n");
   1085 			cp = "OK";
   1086 			break;
   1087 		default:
   1088 		case FATALERR:
   1089 			printf("\ncould not be printed\n");
   1090 			cp = "FATALERR";
   1091 			break;
   1092 		case NOACCT:
   1093 			printf("\ncould not be printed without an account on %s\n", host);
   1094 			cp = "NOACCT";
   1095 			break;
   1096 		case FILTERERR:
   1097 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
   1098 			    (fp = fopen(tempfile, "r")) == NULL) {
   1099 				printf("\nhad some errors and may not have printed\n");
   1100 				break;
   1101 			}
   1102 			printf("\nhad the following errors and may not have printed:\n");
   1103 			while ((i = getc(fp)) != EOF)
   1104 				putchar(i);
   1105 			(void)fclose(fp);
   1106 			cp = "FILTERERR";
   1107 			break;
   1108 		case ACCESS:
   1109 			printf("\nwas not printed because it was not linked to the original file\n");
   1110 			cp = "ACCESS";
   1111 		}
   1112 		fflush(stdout);
   1113 		(void)close(1);
   1114 	} else {
   1115 		syslog(LOG_ERR, "fork for sendmail failed: %m");
   1116 	}
   1117 	(void)close(p[0]);
   1118 	(void)close(p[1]);
   1119 	if (s > 0) {
   1120 		wait(NULL);
   1121 		syslog(LOG_INFO, "mail sent to user %s about job %s on "
   1122 		    "printer %s (%s)", user, *jobname ? jobname : "<unknown>",
   1123 		    printer, cp);
   1124 	}
   1125 }
   1126 
   1127 /*
   1128  * dofork - fork with retries on failure
   1129  */
   1130 static int
   1131 dofork(action)
   1132 	int action;
   1133 {
   1134 	int i, pid;
   1135 	struct passwd *pw;
   1136 
   1137 	for (i = 0; i < 20; i++) {
   1138 		if ((pid = fork()) < 0) {
   1139 			sleep((unsigned)(i*i));
   1140 			continue;
   1141 		}
   1142 		/*
   1143 		 * Child should run as daemon instead of root
   1144 		 */
   1145 		if (pid == 0) {
   1146 			pw = getpwuid(DU);
   1147 			if (pw == 0) {
   1148 				syslog(LOG_ERR, "uid %ld not in password file",
   1149 				    DU);
   1150 				break;
   1151 			}
   1152 			initgroups(pw->pw_name, pw->pw_gid);
   1153 			setgid(pw->pw_gid);
   1154 			setuid(DU);
   1155 		}
   1156 		return (pid);
   1157 	}
   1158 	syslog(LOG_ERR, "can't fork");
   1159 
   1160 	switch (action) {
   1161 	case DORETURN:
   1162 		return (-1);
   1163 	default:
   1164 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
   1165 		/*FALL THRU*/
   1166 	case DOABORT:
   1167 		exit(1);
   1168 	}
   1169 	/*NOTREACHED*/
   1170 }
   1171 
   1172 /*
   1173  * Kill child processes to abort current job.
   1174  */
   1175 static void
   1176 abortpr(signo)
   1177 	int signo;
   1178 {
   1179 	(void)unlink(tempfile);
   1180 	kill(0, SIGINT);
   1181 	if (ofilter > 0)
   1182 		kill(ofilter, SIGCONT);
   1183 	while (wait(NULL) > 0)
   1184 		;
   1185 	exit(0);
   1186 }
   1187 
   1188 static void
   1189 init()
   1190 {
   1191 	int status;
   1192 	char *s;
   1193 
   1194 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
   1195 		syslog(LOG_ERR, "can't open printer description file");
   1196 		exit(1);
   1197 	} else if (status == -1) {
   1198 		syslog(LOG_ERR, "unknown printer: %s", printer);
   1199 		exit(1);
   1200 	} else if (status == -3)
   1201 		fatal("potential reference loop detected in printcap file");
   1202 
   1203 	if (cgetstr(bp, "lp", &LP) == -1)
   1204 		LP = _PATH_DEFDEVLP;
   1205 	if (cgetstr(bp, "rp", &RP) == -1)
   1206 		RP = DEFLP;
   1207 	if (cgetstr(bp, "lo", &LO) == -1)
   1208 		LO = DEFLOCK;
   1209 	if (cgetstr(bp, "st", &ST) == -1)
   1210 		ST = DEFSTAT;
   1211 	if (cgetstr(bp, "lf", &LF) == -1)
   1212 		LF = _PATH_CONSOLE;
   1213 	if (cgetstr(bp, "sd", &SD) == -1)
   1214 		SD = _PATH_DEFSPOOL;
   1215 	if (cgetnum(bp, "du", &DU) < 0)
   1216 		DU = DEFUID;
   1217 	if (cgetstr(bp,"ff", &FF) == -1)
   1218 		FF = DEFFF;
   1219 	if (cgetnum(bp, "pw", &PW) < 0)
   1220 		PW = DEFWIDTH;
   1221 	(void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
   1222 	if (cgetnum(bp, "pl", &PL) < 0)
   1223 		PL = DEFLENGTH;
   1224 	(void)snprintf(&length[2], sizeof(length) - 2, "%ld", PL);
   1225 	if (cgetnum(bp,"px", &PX) < 0)
   1226 		PX = 0;
   1227 	(void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%ld", PX);
   1228 	if (cgetnum(bp, "py", &PY) < 0)
   1229 		PY = 0;
   1230 	(void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%ld", PY);
   1231 	cgetstr(bp, "rm", &RM);
   1232 	if ((s = checkremote()) != NULL)
   1233 		syslog(LOG_WARNING, s);
   1234 
   1235 	cgetstr(bp, "af", &AF);
   1236 	cgetstr(bp, "of", &OF);
   1237 	cgetstr(bp, "if", &IF);
   1238 	cgetstr(bp, "rf", &RF);
   1239 	cgetstr(bp, "tf", &TF);
   1240 	cgetstr(bp, "nf", &NF);
   1241 	cgetstr(bp, "df", &DF);
   1242 	cgetstr(bp, "gf", &GF);
   1243 	cgetstr(bp, "vf", &VF);
   1244 	cgetstr(bp, "cf", &CF);
   1245 	cgetstr(bp, "tr", &TR);
   1246 
   1247 	RS = (cgetcap(bp, "rs", ':') != NULL);
   1248 	SF = (cgetcap(bp, "sf", ':') != NULL);
   1249 	SH = (cgetcap(bp, "sh", ':') != NULL);
   1250 	SB = (cgetcap(bp, "sb", ':') != NULL);
   1251 	HL = (cgetcap(bp, "hl", ':') != NULL);
   1252 	RW = (cgetcap(bp, "rw", ':') != NULL);
   1253 
   1254 	cgetnum(bp, "br", &BR);
   1255 	if (cgetnum(bp, "fc", &FC) < 0)
   1256 		FC = 0;
   1257 	if (cgetnum(bp, "fs", &FS) < 0)
   1258 		FS = 0;
   1259 	if (cgetnum(bp, "xc", &XC) < 0)
   1260 		XC = 0;
   1261 	if (cgetnum(bp, "xs", &XS) < 0)
   1262 		XS = 0;
   1263 	cgetstr(bp, "ms", &MS);
   1264 
   1265 	tof = (cgetcap(bp, "fo", ':') == NULL);
   1266 }
   1267 
   1268 /*
   1269  * Acquire line printer or remote connection.
   1270  */
   1271 static void
   1272 openpr()
   1273 {
   1274 	int i, nofile;
   1275 	char *cp;
   1276 
   1277 	if (!remote && *LP) {
   1278 		if ((cp = strchr(LP, '@')))
   1279 			opennet(cp);
   1280 		else
   1281 			opentty();
   1282 	} else if (remote) {
   1283 		openrem();
   1284 	} else {
   1285 		syslog(LOG_ERR, "%s: no line printer device or host name",
   1286 			printer);
   1287 		exit(1);
   1288 	}
   1289 
   1290 	/*
   1291 	 * Start up an output filter, if needed.
   1292 	 */
   1293 	if (!remote && OF) {
   1294 		int p[2];
   1295 
   1296 		pipe(p);
   1297 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
   1298 			dup2(p[0], 0);		/* pipe is std in */
   1299 			dup2(pfd, 1);		/* printer is std out */
   1300 			closelog();
   1301 			nofile = sysconf(_SC_OPEN_MAX);
   1302 			for (i = 3; i < nofile; i++)
   1303 				(void)close(i);
   1304 			if ((cp = strrchr(OF, '/')) == NULL)
   1305 				cp = OF;
   1306 			else
   1307 				cp++;
   1308 			execl(OF, cp, width, length, 0);
   1309 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
   1310 			exit(1);
   1311 		}
   1312 		(void)close(p[0]);		/* close input side */
   1313 		ofd = p[1];			/* use pipe for output */
   1314 	} else {
   1315 		ofd = pfd;
   1316 		ofilter = 0;
   1317 	}
   1318 }
   1319 
   1320 /*
   1321  * Printer connected directly to the network
   1322  * or to a terminal server on the net
   1323  */
   1324 static void
   1325 opennet(cp)
   1326 	char *cp;
   1327 {
   1328 	int i;
   1329 	int resp, port;
   1330 	char save_ch;
   1331 
   1332 	save_ch = *cp;
   1333 	*cp = '\0';
   1334 	port = atoi(LP);
   1335 	if (port <= 0) {
   1336 		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
   1337 		exit(1);
   1338 	}
   1339 	*cp++ = save_ch;
   1340 
   1341 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1342 		resp = -1;
   1343 		pfd = getport(cp, port);
   1344 		if (pfd < 0 && errno == ECONNREFUSED)
   1345 			resp = 1;
   1346 		else if (pfd >= 0) {
   1347 			/*
   1348 			 * need to delay a bit for rs232 lines
   1349 			 * to stabilize in case printer is
   1350 			 * connected via a terminal server
   1351 			 */
   1352 			delay(500);
   1353 			break;
   1354 		}
   1355 		if (i == 1) {
   1356 		   if (resp < 0)
   1357 			pstatus("waiting for %s to come up", LP);
   1358 		   else
   1359 			pstatus("waiting for access to printer on %s", LP);
   1360 		}
   1361 		sleep(i);
   1362 	}
   1363 	pstatus("sending to %s port %d", cp, port);
   1364 }
   1365 
   1366 /*
   1367  * Printer is connected to an RS232 port on this host
   1368  */
   1369 static void
   1370 opentty()
   1371 {
   1372 	int i;
   1373 
   1374 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
   1375 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
   1376 		if (pfd >= 0) {
   1377 			delay(500);
   1378 			break;
   1379 		}
   1380 		if (errno == ENOENT) {
   1381 			syslog(LOG_ERR, "%s: %m", LP);
   1382 			exit(1);
   1383 		}
   1384 		if (i == 1)
   1385 			pstatus("waiting for %s to become ready (offline ?)",
   1386 				printer);
   1387 		sleep(i);
   1388 	}
   1389 	if (isatty(pfd))
   1390 		setty();
   1391 	pstatus("%s is ready and printing", printer);
   1392 }
   1393 
   1394 /*
   1395  * Printer is on a remote host
   1396  */
   1397 static void
   1398 openrem()
   1399 {
   1400 	int i, n;
   1401 	int resp;
   1402 
   1403 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1404 		resp = -1;
   1405 		pfd = getport(RM, 0);
   1406 		if (pfd >= 0) {
   1407 			n = snprintf(line, sizeof(line), "\2%s\n", RP);
   1408 			if (write(pfd, line, n) == n &&
   1409 			    (resp = response()) == '\0')
   1410 				break;
   1411 			(void) close(pfd);
   1412 		}
   1413 		if (i == 1) {
   1414 			if (resp < 0)
   1415 				pstatus("waiting for %s to come up", RM);
   1416 			else {
   1417 				pstatus("waiting for queue to be enabled on %s",
   1418 					RM);
   1419 				i = 256;
   1420 			}
   1421 		}
   1422 		sleep(i);
   1423 	}
   1424 	pstatus("sending to %s", RM);
   1425 }
   1426 
   1427 #if !defined(__NetBSD__)
   1428 struct bauds {
   1429 	int	baud;
   1430 	int	speed;
   1431 } bauds[] = {
   1432 	50,	B50,
   1433 	75,	B75,
   1434 	110,	B110,
   1435 	134,	B134,
   1436 	150,	B150,
   1437 	200,	B200,
   1438 	300,	B300,
   1439 	600,	B600,
   1440 	1200,	B1200,
   1441 	1800,	B1800,
   1442 	2400,	B2400,
   1443 	4800,	B4800,
   1444 	9600,	B9600,
   1445 	19200,	B19200,
   1446 	38400,	B38400,
   1447 	57600,	B57600,
   1448 	115200,	B115200,
   1449 	0,	0
   1450 };
   1451 #endif
   1452 
   1453 /*
   1454  * setup tty lines.
   1455  */
   1456 static void
   1457 setty()
   1458 {
   1459 	struct info i;
   1460 	char **argv, **ap, *p, *val;
   1461 
   1462 	i.fd = pfd;
   1463 	i.set = i.wset = 0;
   1464 	if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
   1465 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
   1466 		exit(1);
   1467 	}
   1468 	if (tcgetattr(i.fd, &i.t) < 0) {
   1469 		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
   1470 		exit(1);
   1471 	}
   1472 	if (BR > 0) {
   1473 #if !defined(__NetBSD__)
   1474 		struct bauds *bp;
   1475 		for (bp = bauds; bp->baud; bp++)
   1476 			if (BR == bp->baud)
   1477 				break;
   1478 		if (!bp->baud) {
   1479 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
   1480 			exit(1);
   1481 		}
   1482 		cfsetspeed(&i.t, bp->speed);
   1483 #else
   1484 		cfsetspeed(&i.t, BR);
   1485 #endif
   1486 		i.set = 1;
   1487 	}
   1488 	if (MS) {
   1489 		if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
   1490 			syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
   1491 			exit(1);
   1492 		}
   1493 		if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
   1494 			syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
   1495 			       printer);
   1496 
   1497 		argv = (char **)calloc(256, sizeof(char *));
   1498 		if (argv == NULL) {
   1499 			syslog(LOG_ERR, "%s: calloc: %m", printer);
   1500 			exit(1);
   1501 		}
   1502 		p = strdup(MS);
   1503 		ap = argv;
   1504 		while ((val = strsep(&p, " \t,")) != NULL) {
   1505 			*ap++ = strdup(val);
   1506 		}
   1507 
   1508 		for (; *argv; ++argv) {
   1509 			if (ksearch(&argv, &i))
   1510 				continue;
   1511 			if (msearch(&argv, &i))
   1512 				continue;
   1513 			syslog(LOG_INFO, "%s: unknown stty flag: %s",
   1514 			       printer, *argv);
   1515 		}
   1516 	} else {
   1517 		if (FC) {
   1518 			sttyclearflags(&i.t, FC);
   1519 			i.set = 1;
   1520 		}
   1521 		if (FS) {
   1522 			sttysetflags(&i.t, FS);
   1523 			i.set = 1;
   1524 		}
   1525 		if (XC) {
   1526 			sttyclearlflags(&i.t, XC);
   1527 			i.set = 1;
   1528 		}
   1529 		if (XS) {
   1530 			sttysetlflags(&i.t, XS);
   1531 			i.set = 1;
   1532 		}
   1533 	}
   1534 
   1535 	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
   1536 		syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
   1537 		exit(1);
   1538 	}
   1539 	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
   1540 		syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
   1541 	return;
   1542 }
   1543 
   1544 #ifdef __STDC__
   1545 #include <stdarg.h>
   1546 #else
   1547 #include <varargs.h>
   1548 #endif
   1549 
   1550 static void
   1551 #ifdef __STDC__
   1552 pstatus(const char *msg, ...)
   1553 #else
   1554 pstatus(msg, va_alist)
   1555 	char *msg;
   1556         va_dcl
   1557 #endif
   1558 {
   1559 	int fd;
   1560 	char buf[BUFSIZ];
   1561 	va_list ap;
   1562 #ifdef __STDC__
   1563 	va_start(ap, msg);
   1564 #else
   1565 	va_start(ap);
   1566 #endif
   1567 
   1568 	umask(0);
   1569 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
   1570 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
   1571 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
   1572 		exit(1);
   1573 	}
   1574 	ftruncate(fd, 0);
   1575 	(void)vsnprintf(buf, sizeof(buf) - 2, msg, ap);
   1576 	va_end(ap);
   1577 	strncat(buf, "\n", 2);
   1578 	(void)write(fd, buf, strlen(buf));
   1579 	(void)close(fd);
   1580 }
   1581