Home | History | Annotate | Line # | Download | only in lpd
printjob.c revision 1.26
      1 /*	$NetBSD: printjob.c,v 1.26 2001/01/05 03:27:27 lukem 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.26 2001/01/05 03:27:27 lukem 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()
    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(1, "", 1);			/* ack that daemon is started */
    156 	(void)close(2);			/* 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 deamon 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 		if (fchmod(lfd, stb.st_mode & 0776) < 0)
    211 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
    212 	}
    213 	openpr();			/* open printer or remote */
    214 again:
    215 	/*
    216 	 * we found something to do now do it --
    217 	 *    write the name of the current control file into the lock file
    218 	 *    so the spool queue program can tell what we're working on
    219 	 */
    220 	for (qp = queue; nitems--; free((char *) q)) {
    221 		q = *qp++;
    222 		if (stat(q->q_name, &stb) < 0)
    223 			continue;
    224 		errcnt = 0;
    225 	restart:
    226 		(void)lseek(lfd, pidoff, 0);
    227 		i = snprintf(line, sizeof(line), "%s\n", q->q_name);
    228 		if (write(lfd, line, i) != i)
    229 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
    230 		if (!remote)
    231 			i = printit(q->q_name);
    232 		else
    233 			i = sendit(q->q_name);
    234 		/*
    235 		 * Check to see if we are supposed to stop printing or
    236 		 * if we are to rebuild the queue.
    237 		 */
    238 		if (fstat(lfd, &stb) == 0) {
    239 			/* stop printing before starting next job? */
    240 			if (stb.st_mode & S_IXUSR)
    241 				goto done;
    242 			/* rebuild queue (after lpc topq) */
    243 			if (stb.st_mode & S_IXOTH) {
    244 				for (free((char *) q); nitems--; free((char *) q))
    245 					q = *qp++;
    246 				if (fchmod(lfd, stb.st_mode & 0776) < 0)
    247 					syslog(LOG_WARNING, "%s: %s: %m",
    248 						printer, LO);
    249 				break;
    250 			}
    251 		}
    252 		if (i == OK)		/* file ok and printed */
    253 			count++;
    254 		else if (i == REPRINT && ++errcnt < 5) {
    255 			/* try reprinting the job */
    256 			syslog(LOG_INFO, "restarting %s", printer);
    257 			if (ofilter > 0) {
    258 				kill(ofilter, SIGCONT);	/* to be sure */
    259 				(void)close(ofd);
    260 				while ((i = wait(NULL)) > 0 && i != ofilter)
    261 					;
    262 				ofilter = 0;
    263 			}
    264 			(void)close(pfd);	/* close printer */
    265 			if (ftruncate(lfd, pidoff) < 0)
    266 				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
    267 			openpr();		/* try to reopen printer */
    268 			goto restart;
    269 		} else {
    270 			syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
    271 				remote ? "sent to remote host" : "printed", q->q_name);
    272 			if (i == REPRINT) {
    273 				/* ensure we don't attempt this job again */
    274 				(void) unlink(q->q_name);
    275 				q->q_name[0] = 'd';
    276 				(void) unlink(q->q_name);
    277 				if (logname[0])
    278 					sendmail(logname, FATALERR);
    279 			}
    280 		}
    281 	}
    282 	free((char *) queue);
    283 	/*
    284 	 * search the spool directory for more work.
    285 	 */
    286 	if ((nitems = getq(&queue)) < 0) {
    287 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
    288 		exit(1);
    289 	}
    290 	if (nitems == 0) {		/* no more work to do */
    291 	done:
    292 		if (count > 0) {	/* Files actually printed */
    293 			if (!SF && !tof)
    294 				(void)write(ofd, FF, strlen(FF));
    295 			if (TR != NULL)		/* output trailer */
    296 				(void)write(ofd, TR, strlen(TR));
    297 		}
    298 		(void)unlink(tempfile);
    299 		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 (termsig=%d)",
    742 			printer, format, 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 
    862 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
    863 		return(ERROR);
    864 	/*
    865 	 * Check to see if data file is a symbolic link. If so, it should
    866 	 * still point to the same file or someone is trying to print something
    867 	 * he shouldn't.
    868 	 */
    869 	if (S_ISLNK(stb.st_mode) && fstat(f, &stb) == 0 &&
    870 	    (stb.st_dev != fdev || stb.st_ino != fino))
    871 		return(ACCESS);
    872 	amt = snprintf(buf, sizeof(buf), "%c%lld %s\n", type,
    873 	    (long long)stb.st_size, file);
    874 	for (i = 0; ; i++) {
    875 		if (write(pfd, buf, amt) != amt ||
    876 		    (resp = response()) < 0 || resp == '\1') {
    877 			(void)close(f);
    878 			return(REPRINT);
    879 		} else if (resp == '\0')
    880 			break;
    881 		if (i == 0)
    882 			pstatus("no space on remote; waiting for queue to drain");
    883 		if (i == 10)
    884 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
    885 				printer, RM);
    886 		sleep(5 * 60);
    887 	}
    888 	if (i)
    889 		pstatus("sending to %s", RM);
    890 	sizerr = 0;
    891 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
    892 		struct sigaction osa, nsa;
    893 
    894 		amt = BUFSIZ;
    895 		if (i + amt > stb.st_size)
    896 			amt = stb.st_size - i;
    897 		if (sizerr == 0 && read(f, buf, amt) != amt)
    898 			sizerr = 1;
    899 		nsa.sa_handler = alarmer;
    900 		sigemptyset(&nsa.sa_mask);
    901 		sigaddset(&nsa.sa_mask, SIGALRM);
    902 		nsa.sa_flags = 0;
    903 		(void)sigaction(SIGALRM, &nsa, &osa);
    904 		alarm(wait_time);
    905 		if (write(pfd, buf, amt) != amt) {
    906 			alarm(0);
    907 			(void)sigaction(SIGALRM, &osa, NULL);
    908 			(void)close(f);
    909 			return(REPRINT);
    910 		}
    911 		alarm(0);
    912 		(void)sigaction(SIGALRM, &osa, NULL);
    913 	}
    914 
    915 	(void)close(f);
    916 	if (sizerr) {
    917 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
    918 		/* tell recvjob to ignore this file */
    919 		(void)write(pfd, "\1", 1);
    920 		return(ERROR);
    921 	}
    922 	if (write(pfd, "", 1) != 1 || response())
    923 		return(REPRINT);
    924 	return(OK);
    925 }
    926 
    927 /*
    928  * Check to make sure there have been no errors and that both programs
    929  * are in sync with eachother.
    930  * Return non-zero if the connection was lost.
    931  */
    932 static char
    933 response()
    934 {
    935 	struct sigaction osa, nsa;
    936 	char resp;
    937 
    938 	nsa.sa_handler = alarmer;
    939 	sigemptyset(&nsa.sa_mask);
    940 	sigaddset(&nsa.sa_mask, SIGALRM);
    941 	nsa.sa_flags = 0;
    942 	(void)sigaction(SIGALRM, &nsa, &osa);
    943 	alarm(wait_time);
    944 	if (read(pfd, &resp, 1) != 1) {
    945 		syslog(LOG_INFO, "%s: lost connection", printer);
    946 		resp = -1;
    947 	}
    948 	alarm(0);
    949 	(void)sigaction(SIGALRM, &osa, NULL);
    950 	return (resp);
    951 }
    952 
    953 /*
    954  * Banner printing stuff
    955  */
    956 static void
    957 banner(name1, name2)
    958 	char *name1, *name2;
    959 {
    960 	time_t tvec;
    961 
    962 	time(&tvec);
    963 	if (!SF && !tof)
    964 		(void)write(ofd, FF, strlen(FF));
    965 	if (SB) {	/* short banner only */
    966 		if (class[0]) {
    967 			(void)write(ofd, class, strlen(class));
    968 			(void)write(ofd, ":", 1);
    969 		}
    970 		(void)write(ofd, name1, strlen(name1));
    971 		(void)write(ofd, "  Job: ", 7);
    972 		(void)write(ofd, name2, strlen(name2));
    973 		(void)write(ofd, "  Date: ", 8);
    974 		(void)write(ofd, ctime(&tvec), 24);
    975 		(void)write(ofd, "\n", 1);
    976 	} else {	/* normal banner */
    977 		(void)write(ofd, "\n\n\n", 3);
    978 		scan_out(ofd, name1, '\0');
    979 		(void)write(ofd, "\n\n", 2);
    980 		scan_out(ofd, name2, '\0');
    981 		if (class[0]) {
    982 			(void)write(ofd,"\n\n\n",3);
    983 			scan_out(ofd, class, '\0');
    984 		}
    985 		(void)write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
    986 		(void)write(ofd, name2, strlen(name2));
    987 		(void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
    988 		(void)write(ofd, ctime(&tvec), 24);
    989 		(void)write(ofd, "\n", 1);
    990 	}
    991 	if (!SF)
    992 		(void)write(ofd, FF, strlen(FF));
    993 	tof = 1;
    994 }
    995 
    996 static char *
    997 scnline(key, p, c)
    998 	int key;
    999 	char *p;
   1000 	int c;
   1001 {
   1002 	int scnwidth;
   1003 
   1004 	for (scnwidth = WIDTH; --scnwidth;) {
   1005 		key <<= 1;
   1006 		*p++ = key & 0200 ? c : BACKGND;
   1007 	}
   1008 	return (p);
   1009 }
   1010 
   1011 #define TRC(q)	(((q)-' ')&0177)
   1012 
   1013 static void
   1014 scan_out(scfd, scsp, dlm)
   1015 	int scfd, dlm;
   1016 	char *scsp;
   1017 {
   1018 	char *strp;
   1019 	int nchrs, j;
   1020 	char outbuf[LINELEN+1], *sp, c, cc;
   1021 	int d, scnhgt;
   1022 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
   1023 
   1024 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
   1025 		strp = &outbuf[0];
   1026 		sp = scsp;
   1027 		for (nchrs = 0; ; ) {
   1028 			d = dropit(c = TRC(cc = *sp++));
   1029 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
   1030 				for (j = WIDTH; --j;)
   1031 					*strp++ = BACKGND;
   1032 			else
   1033 				strp = scnline(scnkey[(int)c][scnhgt-1-d],
   1034 				    strp, cc);
   1035 			if (*sp == dlm || *sp == '\0' ||
   1036 			    nchrs++ >= PW/(WIDTH+1)-1)
   1037 				break;
   1038 			*strp++ = BACKGND;
   1039 			*strp++ = BACKGND;
   1040 		}
   1041 		while (*--strp == BACKGND && strp >= outbuf)
   1042 			;
   1043 		strp++;
   1044 		*strp++ = '\n';
   1045 		(void)write(scfd, outbuf, strp-outbuf);
   1046 	}
   1047 }
   1048 
   1049 static int
   1050 dropit(c)
   1051 	int c;
   1052 {
   1053 	switch(c) {
   1054 
   1055 	case TRC('_'):
   1056 	case TRC(';'):
   1057 	case TRC(','):
   1058 	case TRC('g'):
   1059 	case TRC('j'):
   1060 	case TRC('p'):
   1061 	case TRC('q'):
   1062 	case TRC('y'):
   1063 		return (DROP);
   1064 
   1065 	default:
   1066 		return (0);
   1067 	}
   1068 }
   1069 
   1070 /*
   1071  * sendmail ---
   1072  *   tell people about job completion
   1073  */
   1074 static void
   1075 sendmail(user, bombed)
   1076 	char *user;
   1077 	int bombed;
   1078 {
   1079 	int i, p[2], s, nofile;
   1080 	char *cp = NULL; /* XXX gcc */
   1081 	struct stat stb;
   1082 	FILE *fp;
   1083 
   1084 	if (user[0] == '-' || user[0] == '/' || !isprint(user[0]))
   1085 		return;
   1086 	pipe(p);
   1087 	if ((s = dofork(DORETURN)) == 0) {		/* child */
   1088 		dup2(p[0], 0);
   1089 		closelog();
   1090 		nofile = sysconf(_SC_OPEN_MAX);
   1091 		for (i = 3; i < nofile; i++)
   1092 			(void)close(i);
   1093 		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
   1094 			cp++;
   1095 		else
   1096 			cp = _PATH_SENDMAIL;
   1097 		execl(_PATH_SENDMAIL, cp, "-t", 0);
   1098 		_exit(0);
   1099 	} else if (s > 0) {				/* parent */
   1100 		dup2(p[1], 1);
   1101 		printf("To: %s@%s\n", user, fromhost);
   1102 		printf("Subject: %s printer job \"%s\"\n", printer,
   1103 			*jobname ? jobname : "<unknown>");
   1104 		printf("Reply-To: root@%s\n\n", host);
   1105 		printf("Your printer job ");
   1106 		if (*jobname)
   1107 			printf("(%s) ", jobname);
   1108 		switch (bombed) {
   1109 		case OK:
   1110 			printf("\ncompleted successfully\n");
   1111 			cp = "OK";
   1112 			break;
   1113 		default:
   1114 		case FATALERR:
   1115 			printf("\ncould not be printed\n");
   1116 			cp = "FATALERR";
   1117 			break;
   1118 		case NOACCT:
   1119 			printf("\ncould not be printed without an account on %s\n", host);
   1120 			cp = "NOACCT";
   1121 			break;
   1122 		case FILTERERR:
   1123 			cp = "FILTERERR";
   1124 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
   1125 			    (fp = fopen(tempfile, "r")) == NULL) {
   1126 				printf("\nhad some errors and may not have printed\n");
   1127 				break;
   1128 			}
   1129 			printf("\nhad the following errors and may not have printed:\n");
   1130 			while ((i = getc(fp)) != EOF)
   1131 				putchar(i);
   1132 			(void)fclose(fp);
   1133 			break;
   1134 		case ACCESS:
   1135 			printf("\nwas not printed because it was not linked to the original file\n");
   1136 			cp = "ACCESS";
   1137 		}
   1138 		fflush(stdout);
   1139 		(void)close(1);
   1140 	} else {
   1141 		syslog(LOG_ERR, "fork for sendmail failed: %m");
   1142 	}
   1143 	(void)close(p[0]);
   1144 	(void)close(p[1]);
   1145 	if (s > 0) {
   1146 		wait(NULL);
   1147 		syslog(LOG_INFO, "mail sent to user %s about job %s on "
   1148 		    "printer %s (%s)", user, *jobname ? jobname : "<unknown>",
   1149 		    printer, cp);
   1150 	}
   1151 }
   1152 
   1153 /*
   1154  * dofork - fork with retries on failure
   1155  */
   1156 static int
   1157 dofork(action)
   1158 	int action;
   1159 {
   1160 	int i, pid;
   1161 	struct passwd *pw;
   1162 
   1163 	for (i = 0; i < 20; i++) {
   1164 		if ((pid = fork()) < 0) {
   1165 			sleep((unsigned)(i*i));
   1166 			continue;
   1167 		}
   1168 		/*
   1169 		 * Child should run as daemon instead of root
   1170 		 */
   1171 		if (pid == 0) {
   1172 			pw = getpwuid(DU);
   1173 			if (pw == 0) {
   1174 				syslog(LOG_ERR, "uid %ld not in password file",
   1175 				    DU);
   1176 				break;
   1177 			}
   1178 			initgroups(pw->pw_name, pw->pw_gid);
   1179 			setgid(pw->pw_gid);
   1180 			setuid(DU);
   1181 		}
   1182 		return (pid);
   1183 	}
   1184 	syslog(LOG_ERR, "can't fork");
   1185 
   1186 	switch (action) {
   1187 	case DORETURN:
   1188 		return (-1);
   1189 	default:
   1190 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
   1191 		/*FALL THRU*/
   1192 	case DOABORT:
   1193 		exit(1);
   1194 	}
   1195 	/*NOTREACHED*/
   1196 }
   1197 
   1198 /*
   1199  * Kill child processes to abort current job.
   1200  */
   1201 static void
   1202 abortpr(signo)
   1203 	int signo;
   1204 {
   1205 	(void)unlink(tempfile);
   1206 	kill(0, SIGINT);
   1207 	if (ofilter > 0)
   1208 		kill(ofilter, SIGCONT);
   1209 	while (wait(NULL) > 0)
   1210 		;
   1211 	exit(0);
   1212 }
   1213 
   1214 static void
   1215 init()
   1216 {
   1217 	int status;
   1218 	char *s;
   1219 
   1220 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
   1221 		syslog(LOG_ERR, "can't open printer description file");
   1222 		exit(1);
   1223 	} else if (status == -1) {
   1224 		syslog(LOG_ERR, "unknown printer: %s", printer);
   1225 		exit(1);
   1226 	} else if (status == -3)
   1227 		fatal("potential reference loop detected in printcap file");
   1228 
   1229 	if (cgetstr(bp, "lp", &LP) == -1)
   1230 		LP = _PATH_DEFDEVLP;
   1231 	if (cgetstr(bp, "rp", &RP) == -1)
   1232 		RP = DEFLP;
   1233 	if (cgetstr(bp, "lo", &LO) == -1)
   1234 		LO = DEFLOCK;
   1235 	if (cgetstr(bp, "st", &ST) == -1)
   1236 		ST = DEFSTAT;
   1237 	if (cgetstr(bp, "lf", &LF) == -1)
   1238 		LF = _PATH_CONSOLE;
   1239 	if (cgetstr(bp, "sd", &SD) == -1)
   1240 		SD = _PATH_DEFSPOOL;
   1241 	if (cgetnum(bp, "du", &DU) < 0)
   1242 		DU = DEFUID;
   1243 	if (cgetstr(bp,"ff", &FF) == -1)
   1244 		FF = DEFFF;
   1245 	if (cgetnum(bp, "pw", &PW) < 0)
   1246 		PW = DEFWIDTH;
   1247 	(void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
   1248 	if (cgetnum(bp, "pl", &PL) < 0)
   1249 		PL = DEFLENGTH;
   1250 	(void)snprintf(&length[2], sizeof(length) - 2, "%ld", PL);
   1251 	if (cgetnum(bp,"px", &PX) < 0)
   1252 		PX = 0;
   1253 	(void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%ld", PX);
   1254 	if (cgetnum(bp, "py", &PY) < 0)
   1255 		PY = 0;
   1256 	(void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%ld", PY);
   1257 	cgetstr(bp, "rm", &RM);
   1258 	if ((s = checkremote()) != NULL)
   1259 		syslog(LOG_WARNING, "%s", s);
   1260 
   1261 	cgetstr(bp, "af", &AF);
   1262 	cgetstr(bp, "of", &OF);
   1263 	cgetstr(bp, "if", &IF);
   1264 	cgetstr(bp, "rf", &RF);
   1265 	cgetstr(bp, "tf", &TF);
   1266 	cgetstr(bp, "nf", &NF);
   1267 	cgetstr(bp, "df", &DF);
   1268 	cgetstr(bp, "gf", &GF);
   1269 	cgetstr(bp, "vf", &VF);
   1270 	cgetstr(bp, "cf", &CF);
   1271 	cgetstr(bp, "tr", &TR);
   1272 
   1273 	RS = (cgetcap(bp, "rs", ':') != NULL);
   1274 	SF = (cgetcap(bp, "sf", ':') != NULL);
   1275 	SH = (cgetcap(bp, "sh", ':') != NULL);
   1276 	SB = (cgetcap(bp, "sb", ':') != NULL);
   1277 	HL = (cgetcap(bp, "hl", ':') != NULL);
   1278 	RW = (cgetcap(bp, "rw", ':') != NULL);
   1279 
   1280 	cgetnum(bp, "br", &BR);
   1281 	if (cgetnum(bp, "fc", &FC) < 0)
   1282 		FC = 0;
   1283 	if (cgetnum(bp, "fs", &FS) < 0)
   1284 		FS = 0;
   1285 	if (cgetnum(bp, "xc", &XC) < 0)
   1286 		XC = 0;
   1287 	if (cgetnum(bp, "xs", &XS) < 0)
   1288 		XS = 0;
   1289 	cgetstr(bp, "ms", &MS);
   1290 
   1291 	tof = (cgetcap(bp, "fo", ':') == NULL);
   1292 }
   1293 
   1294 /*
   1295  * Acquire line printer or remote connection.
   1296  */
   1297 static void
   1298 openpr()
   1299 {
   1300 	int i, nofile;
   1301 	char *cp;
   1302 	extern int rflag;
   1303 
   1304 	if (!remote && *LP) {
   1305 		if ((cp = strchr(LP, '@')))
   1306 			opennet(cp);
   1307 		else
   1308 			opentty();
   1309 	} else if (remote) {
   1310 		openrem();
   1311 	} else {
   1312 		syslog(LOG_ERR, "%s: no line printer device or host name",
   1313 			printer);
   1314 		exit(1);
   1315 	}
   1316 
   1317 	/*
   1318 	 * Start up an output filter, if needed.
   1319 	 */
   1320 	if ((!remote || rflag) && OF) {
   1321 		int p[2];
   1322 
   1323 		pipe(p);
   1324 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
   1325 			dup2(p[0], 0);		/* pipe is std in */
   1326 			dup2(pfd, 1);		/* printer is std out */
   1327 			closelog();
   1328 			nofile = sysconf(_SC_OPEN_MAX);
   1329 			for (i = 3; i < nofile; i++)
   1330 				(void)close(i);
   1331 			if ((cp = strrchr(OF, '/')) == NULL)
   1332 				cp = OF;
   1333 			else
   1334 				cp++;
   1335 			execl(OF, cp, width, length, 0);
   1336 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
   1337 			exit(1);
   1338 		}
   1339 		(void)close(p[0]);		/* close input side */
   1340 		ofd = p[1];			/* use pipe for output */
   1341 	} else {
   1342 		ofd = pfd;
   1343 		ofilter = 0;
   1344 	}
   1345 }
   1346 
   1347 /*
   1348  * Printer connected directly to the network
   1349  * or to a terminal server on the net
   1350  */
   1351 static void
   1352 opennet(cp)
   1353 	char *cp;
   1354 {
   1355 	int i;
   1356 	int resp, port;
   1357 	char save_ch;
   1358 
   1359 	save_ch = *cp;
   1360 	*cp = '\0';
   1361 	port = atoi(LP);
   1362 	if (port <= 0) {
   1363 		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
   1364 		exit(1);
   1365 	}
   1366 	*cp++ = save_ch;
   1367 
   1368 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1369 		resp = -1;
   1370 		pfd = getport(cp, port);
   1371 		if (pfd < 0 && errno == ECONNREFUSED)
   1372 			resp = 1;
   1373 		else if (pfd >= 0) {
   1374 			/*
   1375 			 * need to delay a bit for rs232 lines
   1376 			 * to stabilize in case printer is
   1377 			 * connected via a terminal server
   1378 			 */
   1379 			delay(500);
   1380 			break;
   1381 		}
   1382 		if (i == 1) {
   1383 		   if (resp < 0)
   1384 			pstatus("waiting for %s to come up", LP);
   1385 		   else
   1386 			pstatus("waiting for access to printer on %s", LP);
   1387 		}
   1388 		sleep(i);
   1389 	}
   1390 	pstatus("sending to %s port %d", cp, port);
   1391 }
   1392 
   1393 /*
   1394  * Printer is connected to an RS232 port on this host
   1395  */
   1396 static void
   1397 opentty()
   1398 {
   1399 	int i;
   1400 
   1401 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
   1402 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
   1403 		if (pfd >= 0) {
   1404 			delay(500);
   1405 			break;
   1406 		}
   1407 		if (errno == ENOENT) {
   1408 			syslog(LOG_ERR, "%s: %m", LP);
   1409 			exit(1);
   1410 		}
   1411 		if (i == 1)
   1412 			pstatus("waiting for %s to become ready (offline ?)",
   1413 				printer);
   1414 		sleep(i);
   1415 	}
   1416 	if (isatty(pfd))
   1417 		setty();
   1418 	pstatus("%s is ready and printing", printer);
   1419 }
   1420 
   1421 /*
   1422  * Printer is on a remote host
   1423  */
   1424 static void
   1425 openrem()
   1426 {
   1427 	int i, n;
   1428 	int resp;
   1429 
   1430 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1431 		resp = -1;
   1432 		pfd = getport(RM, 0);
   1433 		if (pfd >= 0) {
   1434 			n = snprintf(line, sizeof(line), "\2%s\n", RP);
   1435 			if (write(pfd, line, n) == n &&
   1436 			    (resp = response()) == '\0')
   1437 				break;
   1438 			(void) close(pfd);
   1439 		}
   1440 		if (i == 1) {
   1441 			if (resp < 0)
   1442 				pstatus("waiting for %s to come up", RM);
   1443 			else {
   1444 				pstatus("waiting for queue to be enabled on %s",
   1445 					RM);
   1446 				i = 256;
   1447 			}
   1448 		}
   1449 		sleep(i);
   1450 	}
   1451 	pstatus("sending to %s", RM);
   1452 }
   1453 
   1454 static void
   1455 alarmer(s)
   1456 	int s;
   1457 {
   1458 	/* nothing */
   1459 }
   1460 
   1461 #if !defined(__NetBSD__)
   1462 struct bauds {
   1463 	int	baud;
   1464 	int	speed;
   1465 } bauds[] = {
   1466 	50,	B50,
   1467 	75,	B75,
   1468 	110,	B110,
   1469 	134,	B134,
   1470 	150,	B150,
   1471 	200,	B200,
   1472 	300,	B300,
   1473 	600,	B600,
   1474 	1200,	B1200,
   1475 	1800,	B1800,
   1476 	2400,	B2400,
   1477 	4800,	B4800,
   1478 	9600,	B9600,
   1479 	19200,	B19200,
   1480 	38400,	B38400,
   1481 	57600,	B57600,
   1482 	115200,	B115200,
   1483 	0,	0
   1484 };
   1485 #endif
   1486 
   1487 /*
   1488  * setup tty lines.
   1489  */
   1490 static void
   1491 setty()
   1492 {
   1493 	struct info i;
   1494 	char **argv, **ap, *p, *val;
   1495 
   1496 	i.fd = pfd;
   1497 	i.set = i.wset = 0;
   1498 	if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
   1499 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
   1500 		exit(1);
   1501 	}
   1502 	if (tcgetattr(i.fd, &i.t) < 0) {
   1503 		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
   1504 		exit(1);
   1505 	}
   1506 	if (BR > 0) {
   1507 #if !defined(__NetBSD__)
   1508 		struct bauds *bp;
   1509 		for (bp = bauds; bp->baud; bp++)
   1510 			if (BR == bp->baud)
   1511 				break;
   1512 		if (!bp->baud) {
   1513 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
   1514 			exit(1);
   1515 		}
   1516 		cfsetspeed(&i.t, bp->speed);
   1517 #else
   1518 		cfsetspeed(&i.t, BR);
   1519 #endif
   1520 		i.set = 1;
   1521 	}
   1522 	if (MS) {
   1523 		if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
   1524 			syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
   1525 			exit(1);
   1526 		}
   1527 		if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
   1528 			syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
   1529 			       printer);
   1530 
   1531 		argv = (char **)calloc(256, sizeof(char *));
   1532 		if (argv == NULL) {
   1533 			syslog(LOG_ERR, "%s: calloc: %m", printer);
   1534 			exit(1);
   1535 		}
   1536 		p = strdup(MS);
   1537 		ap = argv;
   1538 		while ((val = strsep(&p, " \t,")) != NULL) {
   1539 			*ap++ = strdup(val);
   1540 		}
   1541 
   1542 		for (; *argv; ++argv) {
   1543 			if (ksearch(&argv, &i))
   1544 				continue;
   1545 			if (msearch(&argv, &i))
   1546 				continue;
   1547 			syslog(LOG_INFO, "%s: unknown stty flag: %s",
   1548 			       printer, *argv);
   1549 		}
   1550 	} else {
   1551 		if (FC) {
   1552 			sttyclearflags(&i.t, FC);
   1553 			i.set = 1;
   1554 		}
   1555 		if (FS) {
   1556 			sttysetflags(&i.t, FS);
   1557 			i.set = 1;
   1558 		}
   1559 		if (XC) {
   1560 			sttyclearlflags(&i.t, XC);
   1561 			i.set = 1;
   1562 		}
   1563 		if (XS) {
   1564 			sttysetlflags(&i.t, XS);
   1565 			i.set = 1;
   1566 		}
   1567 	}
   1568 
   1569 	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
   1570 		syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
   1571 		exit(1);
   1572 	}
   1573 	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
   1574 		syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
   1575 	return;
   1576 }
   1577 
   1578 #ifdef __STDC__
   1579 #include <stdarg.h>
   1580 #else
   1581 #include <varargs.h>
   1582 #endif
   1583 
   1584 static void
   1585 #ifdef __STDC__
   1586 pstatus(const char *msg, ...)
   1587 #else
   1588 pstatus(msg, va_alist)
   1589 	char *msg;
   1590         va_dcl
   1591 #endif
   1592 {
   1593 	int fd;
   1594 	char *buf;
   1595 	va_list ap;
   1596 #ifdef __STDC__
   1597 	va_start(ap, msg);
   1598 #else
   1599 	va_start(ap);
   1600 #endif
   1601 
   1602 	umask(0);
   1603 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
   1604 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
   1605 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
   1606 		exit(1);
   1607 	}
   1608 	ftruncate(fd, 0);
   1609 	(void)vasprintf(&buf, msg, ap);
   1610 	va_end(ap);
   1611 	/* XXX writev */
   1612 	(void)write(fd, buf, strlen(buf));
   1613 	(void)write(fd, "\n", 2);
   1614 	(void)close(fd);
   1615 	free(buf);
   1616 }
   1617