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