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