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