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