Home | History | Annotate | Line # | Download | only in lpd
printjob.c revision 1.17
      1 /*	$NetBSD: printjob.c,v 1.17 1997/10/05 16:45:45 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.17 1997/10/05 16:45:45 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,
    869 	    (long long)stb.st_size, file);
    870 	for (i = 0;  ; i++) {
    871 		if (write(pfd, buf, amt) != amt ||
    872 		    (resp = response()) < 0 || resp == '\1') {
    873 			(void)close(f);
    874 			return(REPRINT);
    875 		} else if (resp == '\0')
    876 			break;
    877 		if (i == 0)
    878 			pstatus("no space on remote; waiting for queue to drain");
    879 		if (i == 10)
    880 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
    881 				printer, RM);
    882 		sleep(5 * 60);
    883 	}
    884 	if (i)
    885 		pstatus("sending to %s", RM);
    886 	sizerr = 0;
    887 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
    888 		amt = BUFSIZ;
    889 		if (i + amt > stb.st_size)
    890 			amt = stb.st_size - i;
    891 		if (sizerr == 0 && read(f, buf, amt) != amt)
    892 			sizerr = 1;
    893 		if (write(pfd, buf, amt) != amt) {
    894 			(void)close(f);
    895 			return(REPRINT);
    896 		}
    897 	}
    898 
    899 	(void)close(f);
    900 	if (sizerr) {
    901 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
    902 		/* tell recvjob to ignore this file */
    903 		(void)write(pfd, "\1", 1);
    904 		return(ERROR);
    905 	}
    906 	if (write(pfd, "", 1) != 1 || response())
    907 		return(REPRINT);
    908 	return(OK);
    909 }
    910 
    911 /*
    912  * Check to make sure there have been no errors and that both programs
    913  * are in sync with eachother.
    914  * Return non-zero if the connection was lost.
    915  */
    916 static char
    917 response()
    918 {
    919 	char resp;
    920 
    921 	if (read(pfd, &resp, 1) != 1) {
    922 		syslog(LOG_INFO, "%s: lost connection", printer);
    923 		return(-1);
    924 	}
    925 	return(resp);
    926 }
    927 
    928 /*
    929  * Banner printing stuff
    930  */
    931 static void
    932 banner(name1, name2)
    933 	char *name1, *name2;
    934 {
    935 	time_t tvec;
    936 
    937 	time(&tvec);
    938 	if (!SF && !tof)
    939 		(void)write(ofd, FF, strlen(FF));
    940 	if (SB) {	/* short banner only */
    941 		if (class[0]) {
    942 			(void)write(ofd, class, strlen(class));
    943 			(void)write(ofd, ":", 1);
    944 		}
    945 		(void)write(ofd, name1, strlen(name1));
    946 		(void)write(ofd, "  Job: ", 7);
    947 		(void)write(ofd, name2, strlen(name2));
    948 		(void)write(ofd, "  Date: ", 8);
    949 		(void)write(ofd, ctime(&tvec), 24);
    950 		(void)write(ofd, "\n", 1);
    951 	} else {	/* normal banner */
    952 		(void)write(ofd, "\n\n\n", 3);
    953 		scan_out(ofd, name1, '\0');
    954 		(void)write(ofd, "\n\n", 2);
    955 		scan_out(ofd, name2, '\0');
    956 		if (class[0]) {
    957 			(void)write(ofd,"\n\n\n",3);
    958 			scan_out(ofd, class, '\0');
    959 		}
    960 		(void)write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
    961 		(void)write(ofd, name2, strlen(name2));
    962 		(void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
    963 		(void)write(ofd, ctime(&tvec), 24);
    964 		(void)write(ofd, "\n", 1);
    965 	}
    966 	if (!SF)
    967 		(void)write(ofd, FF, strlen(FF));
    968 	tof = 1;
    969 }
    970 
    971 static char *
    972 scnline(key, p, c)
    973 	int key;
    974 	char *p;
    975 	int c;
    976 {
    977 	int scnwidth;
    978 
    979 	for (scnwidth = WIDTH; --scnwidth;) {
    980 		key <<= 1;
    981 		*p++ = key & 0200 ? c : BACKGND;
    982 	}
    983 	return (p);
    984 }
    985 
    986 #define TRC(q)	(((q)-' ')&0177)
    987 
    988 static void
    989 scan_out(scfd, scsp, dlm)
    990 	int scfd, dlm;
    991 	char *scsp;
    992 {
    993 	char *strp;
    994 	int nchrs, j;
    995 	char outbuf[LINELEN+1], *sp, c, cc;
    996 	int d, scnhgt;
    997 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
    998 
    999 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
   1000 		strp = &outbuf[0];
   1001 		sp = scsp;
   1002 		for (nchrs = 0; ; ) {
   1003 			d = dropit(c = TRC(cc = *sp++));
   1004 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
   1005 				for (j = WIDTH; --j;)
   1006 					*strp++ = BACKGND;
   1007 			else
   1008 				strp = scnline(scnkey[(int)c][scnhgt-1-d],
   1009 				    strp, cc);
   1010 			if (*sp == dlm || *sp == '\0' ||
   1011 			    nchrs++ >= PW/(WIDTH+1)-1)
   1012 				break;
   1013 			*strp++ = BACKGND;
   1014 			*strp++ = BACKGND;
   1015 		}
   1016 		while (*--strp == BACKGND && strp >= outbuf)
   1017 			;
   1018 		strp++;
   1019 		*strp++ = '\n';
   1020 		(void)write(scfd, outbuf, strp-outbuf);
   1021 	}
   1022 }
   1023 
   1024 static int
   1025 dropit(c)
   1026 	int c;
   1027 {
   1028 	switch(c) {
   1029 
   1030 	case TRC('_'):
   1031 	case TRC(';'):
   1032 	case TRC(','):
   1033 	case TRC('g'):
   1034 	case TRC('j'):
   1035 	case TRC('p'):
   1036 	case TRC('q'):
   1037 	case TRC('y'):
   1038 		return (DROP);
   1039 
   1040 	default:
   1041 		return (0);
   1042 	}
   1043 }
   1044 
   1045 /*
   1046  * sendmail ---
   1047  *   tell people about job completion
   1048  */
   1049 static void
   1050 sendmail(user, bombed)
   1051 	char *user;
   1052 	int bombed;
   1053 {
   1054 	int i, p[2], s, nofile;
   1055 	char *cp = NULL; /* XXX gcc */
   1056 	struct stat stb;
   1057 	FILE *fp;
   1058 
   1059 	if (user[0] == '-' || user[0] == '/' || !isprint(user[0]))
   1060 		return;
   1061 	pipe(p);
   1062 	if ((s = dofork(DORETURN)) == 0) {		/* child */
   1063 		dup2(p[0], 0);
   1064 		closelog();
   1065 		nofile = sysconf(_SC_OPEN_MAX);
   1066 		for (i = 3; i < nofile; i++)
   1067 			(void)close(i);
   1068 		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
   1069 			cp++;
   1070 		else
   1071 			cp = _PATH_SENDMAIL;
   1072 		execl(_PATH_SENDMAIL, cp, "-t", 0);
   1073 		exit(0);
   1074 	} else if (s > 0) {				/* parent */
   1075 		dup2(p[1], 1);
   1076 		printf("To: %s@%s\n", user, fromhost);
   1077 		printf("Subject: %s printer job \"%s\"\n", printer,
   1078 			*jobname ? jobname : "<unknown>");
   1079 		printf("Reply-To: root@%s\n\n", host);
   1080 		printf("Your printer job ");
   1081 		if (*jobname)
   1082 			printf("(%s) ", jobname);
   1083 		switch (bombed) {
   1084 		case OK:
   1085 			printf("\ncompleted successfully\n");
   1086 			cp = "OK";
   1087 			break;
   1088 		default:
   1089 		case FATALERR:
   1090 			printf("\ncould not be printed\n");
   1091 			cp = "FATALERR";
   1092 			break;
   1093 		case NOACCT:
   1094 			printf("\ncould not be printed without an account on %s\n", host);
   1095 			cp = "NOACCT";
   1096 			break;
   1097 		case FILTERERR:
   1098 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
   1099 			    (fp = fopen(tempfile, "r")) == NULL) {
   1100 				printf("\nhad some errors and may not have printed\n");
   1101 				break;
   1102 			}
   1103 			printf("\nhad the following errors and may not have printed:\n");
   1104 			while ((i = getc(fp)) != EOF)
   1105 				putchar(i);
   1106 			(void)fclose(fp);
   1107 			cp = "FILTERERR";
   1108 			break;
   1109 		case ACCESS:
   1110 			printf("\nwas not printed because it was not linked to the original file\n");
   1111 			cp = "ACCESS";
   1112 		}
   1113 		fflush(stdout);
   1114 		(void)close(1);
   1115 	} else {
   1116 		syslog(LOG_ERR, "fork for sendmail failed: %m");
   1117 	}
   1118 	(void)close(p[0]);
   1119 	(void)close(p[1]);
   1120 	if (s > 0) {
   1121 		wait(NULL);
   1122 		syslog(LOG_INFO, "mail sent to user %s about job %s on "
   1123 		    "printer %s (%s)", user, *jobname ? jobname : "<unknown>",
   1124 		    printer, cp);
   1125 	}
   1126 }
   1127 
   1128 /*
   1129  * dofork - fork with retries on failure
   1130  */
   1131 static int
   1132 dofork(action)
   1133 	int action;
   1134 {
   1135 	int i, pid;
   1136 	struct passwd *pw;
   1137 
   1138 	for (i = 0; i < 20; i++) {
   1139 		if ((pid = fork()) < 0) {
   1140 			sleep((unsigned)(i*i));
   1141 			continue;
   1142 		}
   1143 		/*
   1144 		 * Child should run as daemon instead of root
   1145 		 */
   1146 		if (pid == 0) {
   1147 			pw = getpwuid(DU);
   1148 			if (pw == 0) {
   1149 				syslog(LOG_ERR, "uid %ld not in password file",
   1150 				    DU);
   1151 				break;
   1152 			}
   1153 			initgroups(pw->pw_name, pw->pw_gid);
   1154 			setgid(pw->pw_gid);
   1155 			setuid(DU);
   1156 		}
   1157 		return (pid);
   1158 	}
   1159 	syslog(LOG_ERR, "can't fork");
   1160 
   1161 	switch (action) {
   1162 	case DORETURN:
   1163 		return (-1);
   1164 	default:
   1165 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
   1166 		/*FALL THRU*/
   1167 	case DOABORT:
   1168 		exit(1);
   1169 	}
   1170 	/*NOTREACHED*/
   1171 }
   1172 
   1173 /*
   1174  * Kill child processes to abort current job.
   1175  */
   1176 static void
   1177 abortpr(signo)
   1178 	int signo;
   1179 {
   1180 	(void)unlink(tempfile);
   1181 	kill(0, SIGINT);
   1182 	if (ofilter > 0)
   1183 		kill(ofilter, SIGCONT);
   1184 	while (wait(NULL) > 0)
   1185 		;
   1186 	exit(0);
   1187 }
   1188 
   1189 static void
   1190 init()
   1191 {
   1192 	int status;
   1193 	char *s;
   1194 
   1195 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
   1196 		syslog(LOG_ERR, "can't open printer description file");
   1197 		exit(1);
   1198 	} else if (status == -1) {
   1199 		syslog(LOG_ERR, "unknown printer: %s", printer);
   1200 		exit(1);
   1201 	} else if (status == -3)
   1202 		fatal("potential reference loop detected in printcap file");
   1203 
   1204 	if (cgetstr(bp, "lp", &LP) == -1)
   1205 		LP = _PATH_DEFDEVLP;
   1206 	if (cgetstr(bp, "rp", &RP) == -1)
   1207 		RP = DEFLP;
   1208 	if (cgetstr(bp, "lo", &LO) == -1)
   1209 		LO = DEFLOCK;
   1210 	if (cgetstr(bp, "st", &ST) == -1)
   1211 		ST = DEFSTAT;
   1212 	if (cgetstr(bp, "lf", &LF) == -1)
   1213 		LF = _PATH_CONSOLE;
   1214 	if (cgetstr(bp, "sd", &SD) == -1)
   1215 		SD = _PATH_DEFSPOOL;
   1216 	if (cgetnum(bp, "du", &DU) < 0)
   1217 		DU = DEFUID;
   1218 	if (cgetstr(bp,"ff", &FF) == -1)
   1219 		FF = DEFFF;
   1220 	if (cgetnum(bp, "pw", &PW) < 0)
   1221 		PW = DEFWIDTH;
   1222 	(void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
   1223 	if (cgetnum(bp, "pl", &PL) < 0)
   1224 		PL = DEFLENGTH;
   1225 	(void)snprintf(&length[2], sizeof(length) - 2, "%ld", PL);
   1226 	if (cgetnum(bp,"px", &PX) < 0)
   1227 		PX = 0;
   1228 	(void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%ld", PX);
   1229 	if (cgetnum(bp, "py", &PY) < 0)
   1230 		PY = 0;
   1231 	(void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%ld", PY);
   1232 	cgetstr(bp, "rm", &RM);
   1233 	if ((s = checkremote()) != NULL)
   1234 		syslog(LOG_WARNING, s);
   1235 
   1236 	cgetstr(bp, "af", &AF);
   1237 	cgetstr(bp, "of", &OF);
   1238 	cgetstr(bp, "if", &IF);
   1239 	cgetstr(bp, "rf", &RF);
   1240 	cgetstr(bp, "tf", &TF);
   1241 	cgetstr(bp, "nf", &NF);
   1242 	cgetstr(bp, "df", &DF);
   1243 	cgetstr(bp, "gf", &GF);
   1244 	cgetstr(bp, "vf", &VF);
   1245 	cgetstr(bp, "cf", &CF);
   1246 	cgetstr(bp, "tr", &TR);
   1247 
   1248 	RS = (cgetcap(bp, "rs", ':') != NULL);
   1249 	SF = (cgetcap(bp, "sf", ':') != NULL);
   1250 	SH = (cgetcap(bp, "sh", ':') != NULL);
   1251 	SB = (cgetcap(bp, "sb", ':') != NULL);
   1252 	HL = (cgetcap(bp, "hl", ':') != NULL);
   1253 	RW = (cgetcap(bp, "rw", ':') != NULL);
   1254 
   1255 	cgetnum(bp, "br", &BR);
   1256 	if (cgetnum(bp, "fc", &FC) < 0)
   1257 		FC = 0;
   1258 	if (cgetnum(bp, "fs", &FS) < 0)
   1259 		FS = 0;
   1260 	if (cgetnum(bp, "xc", &XC) < 0)
   1261 		XC = 0;
   1262 	if (cgetnum(bp, "xs", &XS) < 0)
   1263 		XS = 0;
   1264 	cgetstr(bp, "ms", &MS);
   1265 
   1266 	tof = (cgetcap(bp, "fo", ':') == NULL);
   1267 }
   1268 
   1269 /*
   1270  * Acquire line printer or remote connection.
   1271  */
   1272 static void
   1273 openpr()
   1274 {
   1275 	int i, nofile;
   1276 	char *cp;
   1277 
   1278 	if (!remote && *LP) {
   1279 		if ((cp = strchr(LP, '@')))
   1280 			opennet(cp);
   1281 		else
   1282 			opentty();
   1283 	} else if (remote) {
   1284 		openrem();
   1285 	} else {
   1286 		syslog(LOG_ERR, "%s: no line printer device or host name",
   1287 			printer);
   1288 		exit(1);
   1289 	}
   1290 
   1291 	/*
   1292 	 * Start up an output filter, if needed.
   1293 	 */
   1294 	if (!remote && OF) {
   1295 		int p[2];
   1296 
   1297 		pipe(p);
   1298 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
   1299 			dup2(p[0], 0);		/* pipe is std in */
   1300 			dup2(pfd, 1);		/* printer is std out */
   1301 			closelog();
   1302 			nofile = sysconf(_SC_OPEN_MAX);
   1303 			for (i = 3; i < nofile; i++)
   1304 				(void)close(i);
   1305 			if ((cp = strrchr(OF, '/')) == NULL)
   1306 				cp = OF;
   1307 			else
   1308 				cp++;
   1309 			execl(OF, cp, width, length, 0);
   1310 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
   1311 			exit(1);
   1312 		}
   1313 		(void)close(p[0]);		/* close input side */
   1314 		ofd = p[1];			/* use pipe for output */
   1315 	} else {
   1316 		ofd = pfd;
   1317 		ofilter = 0;
   1318 	}
   1319 }
   1320 
   1321 /*
   1322  * Printer connected directly to the network
   1323  * or to a terminal server on the net
   1324  */
   1325 static void
   1326 opennet(cp)
   1327 	char *cp;
   1328 {
   1329 	int i;
   1330 	int resp, port;
   1331 	char save_ch;
   1332 
   1333 	save_ch = *cp;
   1334 	*cp = '\0';
   1335 	port = atoi(LP);
   1336 	if (port <= 0) {
   1337 		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
   1338 		exit(1);
   1339 	}
   1340 	*cp++ = save_ch;
   1341 
   1342 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1343 		resp = -1;
   1344 		pfd = getport(cp, port);
   1345 		if (pfd < 0 && errno == ECONNREFUSED)
   1346 			resp = 1;
   1347 		else if (pfd >= 0) {
   1348 			/*
   1349 			 * need to delay a bit for rs232 lines
   1350 			 * to stabilize in case printer is
   1351 			 * connected via a terminal server
   1352 			 */
   1353 			delay(500);
   1354 			break;
   1355 		}
   1356 		if (i == 1) {
   1357 		   if (resp < 0)
   1358 			pstatus("waiting for %s to come up", LP);
   1359 		   else
   1360 			pstatus("waiting for access to printer on %s", LP);
   1361 		}
   1362 		sleep(i);
   1363 	}
   1364 	pstatus("sending to %s port %d", cp, port);
   1365 }
   1366 
   1367 /*
   1368  * Printer is connected to an RS232 port on this host
   1369  */
   1370 static void
   1371 opentty()
   1372 {
   1373 	int i;
   1374 
   1375 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
   1376 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
   1377 		if (pfd >= 0) {
   1378 			delay(500);
   1379 			break;
   1380 		}
   1381 		if (errno == ENOENT) {
   1382 			syslog(LOG_ERR, "%s: %m", LP);
   1383 			exit(1);
   1384 		}
   1385 		if (i == 1)
   1386 			pstatus("waiting for %s to become ready (offline ?)",
   1387 				printer);
   1388 		sleep(i);
   1389 	}
   1390 	if (isatty(pfd))
   1391 		setty();
   1392 	pstatus("%s is ready and printing", printer);
   1393 }
   1394 
   1395 /*
   1396  * Printer is on a remote host
   1397  */
   1398 static void
   1399 openrem()
   1400 {
   1401 	int i, n;
   1402 	int resp;
   1403 
   1404 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1405 		resp = -1;
   1406 		pfd = getport(RM, 0);
   1407 		if (pfd >= 0) {
   1408 			n = snprintf(line, sizeof(line), "\2%s\n", RP);
   1409 			if (write(pfd, line, n) == n &&
   1410 			    (resp = response()) == '\0')
   1411 				break;
   1412 			(void) close(pfd);
   1413 		}
   1414 		if (i == 1) {
   1415 			if (resp < 0)
   1416 				pstatus("waiting for %s to come up", RM);
   1417 			else {
   1418 				pstatus("waiting for queue to be enabled on %s",
   1419 					RM);
   1420 				i = 256;
   1421 			}
   1422 		}
   1423 		sleep(i);
   1424 	}
   1425 	pstatus("sending to %s", RM);
   1426 }
   1427 
   1428 #if !defined(__NetBSD__)
   1429 struct bauds {
   1430 	int	baud;
   1431 	int	speed;
   1432 } bauds[] = {
   1433 	50,	B50,
   1434 	75,	B75,
   1435 	110,	B110,
   1436 	134,	B134,
   1437 	150,	B150,
   1438 	200,	B200,
   1439 	300,	B300,
   1440 	600,	B600,
   1441 	1200,	B1200,
   1442 	1800,	B1800,
   1443 	2400,	B2400,
   1444 	4800,	B4800,
   1445 	9600,	B9600,
   1446 	19200,	B19200,
   1447 	38400,	B38400,
   1448 	57600,	B57600,
   1449 	115200,	B115200,
   1450 	0,	0
   1451 };
   1452 #endif
   1453 
   1454 /*
   1455  * setup tty lines.
   1456  */
   1457 static void
   1458 setty()
   1459 {
   1460 	struct info i;
   1461 	char **argv, **ap, *p, *val;
   1462 
   1463 	i.fd = pfd;
   1464 	i.set = i.wset = 0;
   1465 	if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
   1466 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
   1467 		exit(1);
   1468 	}
   1469 	if (tcgetattr(i.fd, &i.t) < 0) {
   1470 		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
   1471 		exit(1);
   1472 	}
   1473 	if (BR > 0) {
   1474 #if !defined(__NetBSD__)
   1475 		struct bauds *bp;
   1476 		for (bp = bauds; bp->baud; bp++)
   1477 			if (BR == bp->baud)
   1478 				break;
   1479 		if (!bp->baud) {
   1480 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
   1481 			exit(1);
   1482 		}
   1483 		cfsetspeed(&i.t, bp->speed);
   1484 #else
   1485 		cfsetspeed(&i.t, BR);
   1486 #endif
   1487 		i.set = 1;
   1488 	}
   1489 	if (MS) {
   1490 		if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
   1491 			syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
   1492 			exit(1);
   1493 		}
   1494 		if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
   1495 			syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
   1496 			       printer);
   1497 
   1498 		argv = (char **)calloc(256, sizeof(char *));
   1499 		if (argv == NULL) {
   1500 			syslog(LOG_ERR, "%s: calloc: %m", printer);
   1501 			exit(1);
   1502 		}
   1503 		p = strdup(MS);
   1504 		ap = argv;
   1505 		while ((val = strsep(&p, " \t,")) != NULL) {
   1506 			*ap++ = strdup(val);
   1507 		}
   1508 
   1509 		for (; *argv; ++argv) {
   1510 			if (ksearch(&argv, &i))
   1511 				continue;
   1512 			if (msearch(&argv, &i))
   1513 				continue;
   1514 			syslog(LOG_INFO, "%s: unknown stty flag: %s",
   1515 			       printer, *argv);
   1516 		}
   1517 	} else {
   1518 		if (FC) {
   1519 			sttyclearflags(&i.t, FC);
   1520 			i.set = 1;
   1521 		}
   1522 		if (FS) {
   1523 			sttysetflags(&i.t, FS);
   1524 			i.set = 1;
   1525 		}
   1526 		if (XC) {
   1527 			sttyclearlflags(&i.t, XC);
   1528 			i.set = 1;
   1529 		}
   1530 		if (XS) {
   1531 			sttysetlflags(&i.t, XS);
   1532 			i.set = 1;
   1533 		}
   1534 	}
   1535 
   1536 	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
   1537 		syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
   1538 		exit(1);
   1539 	}
   1540 	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
   1541 		syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
   1542 	return;
   1543 }
   1544 
   1545 #ifdef __STDC__
   1546 #include <stdarg.h>
   1547 #else
   1548 #include <varargs.h>
   1549 #endif
   1550 
   1551 static void
   1552 #ifdef __STDC__
   1553 pstatus(const char *msg, ...)
   1554 #else
   1555 pstatus(msg, va_alist)
   1556 	char *msg;
   1557         va_dcl
   1558 #endif
   1559 {
   1560 	int fd;
   1561 	char buf[BUFSIZ];
   1562 	va_list ap;
   1563 #ifdef __STDC__
   1564 	va_start(ap, msg);
   1565 #else
   1566 	va_start(ap);
   1567 #endif
   1568 
   1569 	umask(0);
   1570 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
   1571 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
   1572 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
   1573 		exit(1);
   1574 	}
   1575 	ftruncate(fd, 0);
   1576 	(void)vsnprintf(buf, sizeof(buf) - 2, msg, ap);
   1577 	va_end(ap);
   1578 	strncat(buf, "\n", 2);
   1579 	(void)write(fd, buf, strlen(buf));
   1580 	(void)close(fd);
   1581 }
   1582