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