Home | History | Annotate | Line # | Download | only in lpd
printjob.c revision 1.33
      1 /*	$NetBSD: printjob.c,v 1.33 2002/07/09 01:12:35 hubertf 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.33 2002/07/09 01:12:35 hubertf Exp $");
     49 #endif
     50 #endif /* not lint */
     51 
     52 
     53 /*
     54  * printjob -- print jobs in the queue.
     55  *
     56  *	NOTE: the lock file is used to pass information to lpq and lprm.
     57  *	it does not need to be removed because file locks are dynamic.
     58  */
     59 
     60 #include <sys/param.h>
     61 #include <sys/wait.h>
     62 #include <sys/stat.h>
     63 #include <sys/types.h>
     64 #include <sys/file.h>
     65 
     66 #include <pwd.h>
     67 #include <unistd.h>
     68 #include <signal.h>
     69 #include <termios.h>
     70 #include <syslog.h>
     71 #include <fcntl.h>
     72 #include <dirent.h>
     73 #include <errno.h>
     74 #include <stdio.h>
     75 #include <string.h>
     76 #include <stdlib.h>
     77 #include <ctype.h>
     78 #include "lp.h"
     79 #include "lp.local.h"
     80 #include "pathnames.h"
     81 #include "extern.h"
     82 
     83 #define DORETURN	0	/* absorb fork error */
     84 #define DOABORT		1	/* abort if dofork fails */
     85 
     86 /*
     87  * Error tokens
     88  */
     89 #define REPRINT		-2
     90 #define ERROR		-1
     91 #define	OK		0
     92 #define	FATALERR	1
     93 #define	NOACCT		2
     94 #define	FILTERERR	3
     95 #define	ACCESS		4
     96 
     97 static dev_t	fdev;		/* device of file pointed to by symlink */
     98 static ino_t	fino;		/* inode of file pointed to by symlink */
     99 static FILE	*cfp;		/* control file */
    100 static int	child;		/* id of any filters */
    101 static int	lfd;		/* lock file descriptor */
    102 static int	ofd;		/* output filter file descriptor */
    103 static int	ofilter;	/* id of output filter, if any */
    104 static int	pfd;		/* printer file descriptor */
    105 static int	pid;		/* pid of lpd process */
    106 static int	prchild;	/* id of pr process */
    107 static char	title[80];	/* ``pr'' title */
    108 static int	tof;		/* true if at top of form */
    109 
    110 static char	class[32];		/* classification field */
    111 static char	fromhost[32];		/* user's host machine */
    112 				/* indentation size in static characters */
    113 static char	indent[10] = "-i0";
    114 static char	jobname[100];		/* job or file name */
    115 static char	length[10] = "-l";	/* page length in lines */
    116 static char	logname[32];		/* user's login name */
    117 static char	pxlength[10] = "-y";	/* page length in pixels */
    118 static char	pxwidth[10] = "-x";	/* page width in pixels */
    119 static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
    120 static char	width[10] = "-w";	/* page width in static characters */
    121 
    122 static void	abortpr __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(int c)
   1044 {
   1045 	switch(c) {
   1046 
   1047 	case TRC('_'):
   1048 	case TRC(';'):
   1049 	case TRC(','):
   1050 	case TRC('g'):
   1051 	case TRC('j'):
   1052 	case TRC('p'):
   1053 	case TRC('q'):
   1054 	case TRC('y'):
   1055 		return (DROP);
   1056 
   1057 	default:
   1058 		return (0);
   1059 	}
   1060 }
   1061 
   1062 /*
   1063  * sendmail ---
   1064  *   tell people about job completion
   1065  */
   1066 static void
   1067 sendmail(char *user, int bombed)
   1068 {
   1069 	int i, p[2], s, nofile;
   1070 	char *cp = NULL; /* XXX gcc */
   1071 	struct stat stb;
   1072 	FILE *fp;
   1073 
   1074 	if (user[0] == '-' || user[0] == '/' || !isprint(user[0]))
   1075 		return;
   1076 	pipe(p);
   1077 	if ((s = dofork(DORETURN)) == 0) {		/* child */
   1078 		dup2(p[0], 0);
   1079 		closelog();
   1080 		nofile = sysconf(_SC_OPEN_MAX);
   1081 		for (i = 3; i < nofile; i++)
   1082 			(void)close(i);
   1083 		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
   1084 			cp++;
   1085 		else
   1086 			cp = _PATH_SENDMAIL;
   1087 		execl(_PATH_SENDMAIL, cp, "-t", 0);
   1088 		_exit(0);
   1089 	} else if (s > 0) {				/* parent */
   1090 		dup2(p[1], 1);
   1091 		printf("To: %s@%s\n", user, fromhost);
   1092 		printf("Subject: %s printer job \"%s\"\n", printer,
   1093 			*jobname ? jobname : "<unknown>");
   1094 		printf("Reply-To: root@%s\n\n", host);
   1095 		printf("Your printer job ");
   1096 		if (*jobname)
   1097 			printf("(%s) ", jobname);
   1098 		switch (bombed) {
   1099 		case OK:
   1100 			printf("\ncompleted successfully\n");
   1101 			cp = "OK";
   1102 			break;
   1103 		default:
   1104 		case FATALERR:
   1105 			printf("\ncould not be printed\n");
   1106 			cp = "FATALERR";
   1107 			break;
   1108 		case NOACCT:
   1109 			printf("\ncould not be printed without an account on %s\n", host);
   1110 			cp = "NOACCT";
   1111 			break;
   1112 		case FILTERERR:
   1113 			cp = "FILTERERR";
   1114 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
   1115 			    (fp = fopen(tempfile, "r")) == NULL) {
   1116 				printf("\nhad some errors and may not have printed\n");
   1117 				break;
   1118 			}
   1119 			printf("\nhad the following errors and may not have printed:\n");
   1120 			while ((i = getc(fp)) != EOF)
   1121 				putchar(i);
   1122 			(void)fclose(fp);
   1123 			break;
   1124 		case ACCESS:
   1125 			printf("\nwas not printed because it was not linked to the original file\n");
   1126 			cp = "ACCESS";
   1127 		}
   1128 		fflush(stdout);
   1129 		(void)close(1);
   1130 	} else {
   1131 		syslog(LOG_ERR, "fork for sendmail failed: %m");
   1132 	}
   1133 	(void)close(p[0]);
   1134 	(void)close(p[1]);
   1135 	if (s > 0) {
   1136 		wait(NULL);
   1137 		syslog(LOG_INFO, "mail sent to user %s about job %s on "
   1138 		    "printer %s (%s)", user, *jobname ? jobname : "<unknown>",
   1139 		    printer, cp);
   1140 	}
   1141 }
   1142 
   1143 /*
   1144  * dofork - fork with retries on failure
   1145  */
   1146 static int
   1147 dofork(int action)
   1148 {
   1149 	int i, pid;
   1150 	struct passwd *pw;
   1151 
   1152 	for (i = 0; i < 20; i++) {
   1153 		if ((pid = fork()) < 0) {
   1154 			sleep((unsigned)(i*i));
   1155 			continue;
   1156 		}
   1157 		/*
   1158 		 * Child should run as daemon instead of root
   1159 		 */
   1160 		if (pid == 0) {
   1161 			pw = getpwuid(DU);
   1162 			if (pw == 0) {
   1163 				syslog(LOG_ERR, "uid %ld not in password file",
   1164 				    DU);
   1165 				break;
   1166 			}
   1167 			initgroups(pw->pw_name, pw->pw_gid);
   1168 			setgid(pw->pw_gid);
   1169 			setuid(DU);
   1170 		}
   1171 		return (pid);
   1172 	}
   1173 	syslog(LOG_ERR, "can't fork");
   1174 
   1175 	switch (action) {
   1176 	case DORETURN:
   1177 		return (-1);
   1178 	default:
   1179 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
   1180 		/*FALL THRU*/
   1181 	case DOABORT:
   1182 		exit(1);
   1183 	}
   1184 	/*NOTREACHED*/
   1185 }
   1186 
   1187 /*
   1188  * Kill child processes to abort current job.
   1189  */
   1190 static void
   1191 abortpr(int signo)
   1192 {
   1193 	(void)unlink(tempfile);
   1194 	kill(0, SIGINT);
   1195 	if (ofilter > 0)
   1196 		kill(ofilter, SIGCONT);
   1197 	while (wait(NULL) > 0)
   1198 		;
   1199 	exit(0);
   1200 }
   1201 
   1202 static void
   1203 init(void)
   1204 {
   1205 	int status;
   1206 	char *s;
   1207 
   1208 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
   1209 		syslog(LOG_ERR, "can't open printer description file");
   1210 		exit(1);
   1211 	} else if (status == -1) {
   1212 		syslog(LOG_ERR, "unknown printer: %s", printer);
   1213 		exit(1);
   1214 	} else if (status == -3)
   1215 		fatal("potential reference loop detected in printcap file");
   1216 
   1217 	if (cgetstr(bp, DEFLP, &LP) == -1)
   1218 		LP = _PATH_DEFDEVLP;
   1219 	if (cgetstr(bp, "rp", &RP) == -1)
   1220 		RP = DEFLP;
   1221 	if (cgetstr(bp, "lo", &LO) == -1)
   1222 		LO = DEFLOCK;
   1223 	if (cgetstr(bp, "st", &ST) == -1)
   1224 		ST = DEFSTAT;
   1225 	if (cgetstr(bp, "lf", &LF) == -1)
   1226 		LF = _PATH_CONSOLE;
   1227 	if (cgetstr(bp, "sd", &SD) == -1)
   1228 		SD = _PATH_DEFSPOOL;
   1229 	if (cgetnum(bp, "du", &DU) < 0)
   1230 		DU = DEFUID;
   1231 	if (cgetstr(bp,"ff", &FF) == -1)
   1232 		FF = DEFFF;
   1233 	if (cgetnum(bp, "pw", &PW) < 0)
   1234 		PW = DEFWIDTH;
   1235 	(void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
   1236 	if (cgetnum(bp, "pl", &PL) < 0)
   1237 		PL = DEFLENGTH;
   1238 	(void)snprintf(&length[2], sizeof(length) - 2, "%ld", PL);
   1239 	if (cgetnum(bp,"px", &PX) < 0)
   1240 		PX = 0;
   1241 	(void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%ld", PX);
   1242 	if (cgetnum(bp, "py", &PY) < 0)
   1243 		PY = 0;
   1244 	(void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%ld", PY);
   1245 	cgetstr(bp, "rm", &RM);
   1246 	if ((s = checkremote()) != NULL)
   1247 		syslog(LOG_WARNING, "%s", s);
   1248 
   1249 	cgetstr(bp, "af", &AF);
   1250 	cgetstr(bp, "of", &OF);
   1251 	cgetstr(bp, "if", &IF);
   1252 	cgetstr(bp, "rf", &RF);
   1253 	cgetstr(bp, "tf", &TF);
   1254 	cgetstr(bp, "nf", &NF);
   1255 	cgetstr(bp, "df", &DF);
   1256 	cgetstr(bp, "gf", &GF);
   1257 	cgetstr(bp, "vf", &VF);
   1258 	cgetstr(bp, "cf", &CF);
   1259 	cgetstr(bp, "tr", &TR);
   1260 
   1261 	RS = (cgetcap(bp, "rs", ':') != NULL);
   1262 	SF = (cgetcap(bp, "sf", ':') != NULL);
   1263 	SH = (cgetcap(bp, "sh", ':') != NULL);
   1264 	SB = (cgetcap(bp, "sb", ':') != NULL);
   1265 	HL = (cgetcap(bp, "hl", ':') != NULL);
   1266 	RW = (cgetcap(bp, "rw", ':') != NULL);
   1267 
   1268 	cgetnum(bp, "br", &BR);
   1269 	if (cgetnum(bp, "fc", &FC) < 0)
   1270 		FC = 0;
   1271 	if (cgetnum(bp, "fs", &FS) < 0)
   1272 		FS = 0;
   1273 	if (cgetnum(bp, "xc", &XC) < 0)
   1274 		XC = 0;
   1275 	if (cgetnum(bp, "xs", &XS) < 0)
   1276 		XS = 0;
   1277 	cgetstr(bp, "ms", &MS);
   1278 
   1279 	tof = (cgetcap(bp, "fo", ':') == NULL);
   1280 }
   1281 
   1282 /*
   1283  * Acquire line printer or remote connection.
   1284  */
   1285 static void
   1286 openpr(void)
   1287 {
   1288 	int i, nofile;
   1289 	char *cp;
   1290 	extern int rflag;
   1291 
   1292 	if (!remote && *LP) {
   1293 		if ((cp = strchr(LP, '@')))
   1294 			opennet(cp);
   1295 		else
   1296 			opentty();
   1297 	} else if (remote) {
   1298 		openrem();
   1299 	} else {
   1300 		syslog(LOG_ERR, "%s: no line printer device or host name",
   1301 			printer);
   1302 		exit(1);
   1303 	}
   1304 
   1305 	/*
   1306 	 * Start up an output filter, if needed.
   1307 	 */
   1308 	if ((!remote || rflag) && OF) {
   1309 		int p[2];
   1310 
   1311 		pipe(p);
   1312 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
   1313 			dup2(p[0], 0);		/* pipe is std in */
   1314 			dup2(pfd, 1);		/* printer is std out */
   1315 			closelog();
   1316 			nofile = sysconf(_SC_OPEN_MAX);
   1317 			for (i = 3; i < nofile; i++)
   1318 				(void)close(i);
   1319 			if ((cp = strrchr(OF, '/')) == NULL)
   1320 				cp = OF;
   1321 			else
   1322 				cp++;
   1323 			execl(OF, cp, width, length, 0);
   1324 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
   1325 			exit(1);
   1326 		}
   1327 		(void)close(p[0]);		/* close input side */
   1328 		ofd = p[1];			/* use pipe for output */
   1329 	} else {
   1330 		ofd = pfd;
   1331 		ofilter = 0;
   1332 	}
   1333 }
   1334 
   1335 /*
   1336  * Printer connected directly to the network
   1337  * or to a terminal server on the net
   1338  */
   1339 static void
   1340 opennet(char *cp)
   1341 {
   1342 	int i;
   1343 	int resp, port;
   1344 	char save_ch;
   1345 
   1346 	save_ch = *cp;
   1347 	*cp = '\0';
   1348 	port = atoi(LP);
   1349 	if (port <= 0) {
   1350 		syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
   1351 		exit(1);
   1352 	}
   1353 	*cp++ = save_ch;
   1354 
   1355 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1356 		resp = -1;
   1357 		pfd = getport(cp, port);
   1358 		if (pfd < 0 && errno == ECONNREFUSED)
   1359 			resp = 1;
   1360 		else if (pfd >= 0) {
   1361 			/*
   1362 			 * need to delay a bit for rs232 lines
   1363 			 * to stabilize in case printer is
   1364 			 * connected via a terminal server
   1365 			 */
   1366 			delay(500);
   1367 			break;
   1368 		}
   1369 		if (i == 1) {
   1370 		   if (resp < 0)
   1371 			pstatus("waiting for %s to come up", LP);
   1372 		   else
   1373 			pstatus("waiting for access to printer on %s", LP);
   1374 		}
   1375 		sleep(i);
   1376 	}
   1377 	pstatus("sending to %s port %d", cp, port);
   1378 }
   1379 
   1380 /*
   1381  * Printer is connected to an RS232 port on this host
   1382  */
   1383 static void
   1384 opentty(void)
   1385 {
   1386 	int i;
   1387 
   1388 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
   1389 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
   1390 		if (pfd >= 0) {
   1391 			delay(500);
   1392 			break;
   1393 		}
   1394 		if (errno == ENOENT) {
   1395 			syslog(LOG_ERR, "%s: %m", LP);
   1396 			exit(1);
   1397 		}
   1398 		if (i == 1)
   1399 			pstatus("waiting for %s to become ready (offline ?)",
   1400 				printer);
   1401 		sleep(i);
   1402 	}
   1403 	if (isatty(pfd))
   1404 		setty();
   1405 	pstatus("%s is ready and printing", printer);
   1406 }
   1407 
   1408 /*
   1409  * Printer is on a remote host
   1410  */
   1411 static void
   1412 openrem(void)
   1413 {
   1414 	int i, n;
   1415 	int resp;
   1416 
   1417 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1418 		resp = -1;
   1419 		pfd = getport(RM, 0);
   1420 		if (pfd >= 0) {
   1421 			n = snprintf(line, sizeof(line), "\2%s\n", RP);
   1422 			if (write(pfd, line, n) == n &&
   1423 			    (resp = response()) == '\0')
   1424 				break;
   1425 			(void) close(pfd);
   1426 		}
   1427 		if (i == 1) {
   1428 			if (resp < 0)
   1429 				pstatus("waiting for %s to come up", RM);
   1430 			else {
   1431 				pstatus("waiting for queue to be enabled on %s",
   1432 					RM);
   1433 				i = 256;
   1434 			}
   1435 		}
   1436 		sleep(i);
   1437 	}
   1438 	pstatus("sending to %s", RM);
   1439 }
   1440 
   1441 static void
   1442 alarmer(int s)
   1443 {
   1444 	/* nothing */
   1445 }
   1446 
   1447 #if !defined(__NetBSD__)
   1448 struct bauds {
   1449 	int	baud;
   1450 	int	speed;
   1451 } bauds[] = {
   1452 	50,	B50,
   1453 	75,	B75,
   1454 	110,	B110,
   1455 	134,	B134,
   1456 	150,	B150,
   1457 	200,	B200,
   1458 	300,	B300,
   1459 	600,	B600,
   1460 	1200,	B1200,
   1461 	1800,	B1800,
   1462 	2400,	B2400,
   1463 	4800,	B4800,
   1464 	9600,	B9600,
   1465 	19200,	B19200,
   1466 	38400,	B38400,
   1467 	57600,	B57600,
   1468 	115200,	B115200,
   1469 	0,	0
   1470 };
   1471 #endif
   1472 
   1473 /*
   1474  * setup tty lines.
   1475  */
   1476 static void
   1477 setty(void)
   1478 {
   1479 	struct info i;
   1480 	char **argv, **ap, *p, *val;
   1481 
   1482 	i.fd = pfd;
   1483 	i.set = i.wset = 0;
   1484 	if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
   1485 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
   1486 		exit(1);
   1487 	}
   1488 	if (tcgetattr(i.fd, &i.t) < 0) {
   1489 		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
   1490 		exit(1);
   1491 	}
   1492 	if (BR > 0) {
   1493 #if !defined(__NetBSD__)
   1494 		struct bauds *bp;
   1495 		for (bp = bauds; bp->baud; bp++)
   1496 			if (BR == bp->baud)
   1497 				break;
   1498 		if (!bp->baud) {
   1499 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
   1500 			exit(1);
   1501 		}
   1502 		cfsetspeed(&i.t, bp->speed);
   1503 #else
   1504 		cfsetspeed(&i.t, BR);
   1505 #endif
   1506 		i.set = 1;
   1507 	}
   1508 	if (MS) {
   1509 		if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
   1510 			syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
   1511 			exit(1);
   1512 		}
   1513 		if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
   1514 			syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
   1515 			       printer);
   1516 
   1517 		argv = (char **)calloc(256, sizeof(char *));
   1518 		if (argv == NULL) {
   1519 			syslog(LOG_ERR, "%s: calloc: %m", printer);
   1520 			exit(1);
   1521 		}
   1522 		p = strdup(MS);
   1523 		ap = argv;
   1524 		while ((val = strsep(&p, " \t,")) != NULL) {
   1525 			*ap++ = strdup(val);
   1526 		}
   1527 
   1528 		for (; *argv; ++argv) {
   1529 			if (ksearch(&argv, &i))
   1530 				continue;
   1531 			if (msearch(&argv, &i))
   1532 				continue;
   1533 			syslog(LOG_INFO, "%s: unknown stty flag: %s",
   1534 			       printer, *argv);
   1535 		}
   1536 	} else {
   1537 		if (FC) {
   1538 			sttyclearflags(&i.t, FC);
   1539 			i.set = 1;
   1540 		}
   1541 		if (FS) {
   1542 			sttysetflags(&i.t, FS);
   1543 			i.set = 1;
   1544 		}
   1545 		if (XC) {
   1546 			sttyclearlflags(&i.t, XC);
   1547 			i.set = 1;
   1548 		}
   1549 		if (XS) {
   1550 			sttysetlflags(&i.t, XS);
   1551 			i.set = 1;
   1552 		}
   1553 	}
   1554 
   1555 	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
   1556 		syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
   1557 		exit(1);
   1558 	}
   1559 	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
   1560 		syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
   1561 	return;
   1562 }
   1563 
   1564 #ifdef __STDC__
   1565 #include <stdarg.h>
   1566 #else
   1567 #include <varargs.h>
   1568 #endif
   1569 
   1570 static void
   1571 #ifdef __STDC__
   1572 pstatus(const char *msg, ...)
   1573 #else
   1574 pstatus(msg, va_alist)
   1575 	char *msg;
   1576         va_dcl
   1577 #endif
   1578 {
   1579 	int fd;
   1580 	char *buf;
   1581 	va_list ap;
   1582 
   1583 	umask(0);
   1584 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
   1585 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
   1586 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
   1587 		exit(1);
   1588 	}
   1589 	ftruncate(fd, 0);
   1590 #ifdef __STDC__
   1591 	va_start(ap, msg);
   1592 #else
   1593 	va_start(ap);
   1594 #endif
   1595 	(void)vasprintf(&buf, msg, ap);
   1596 	va_end(ap);
   1597 	/* XXX writev */
   1598 	(void)write(fd, buf, strlen(buf));
   1599 	(void)write(fd, "\n", 2);
   1600 	(void)close(fd);
   1601 	free(buf);
   1602 }
   1603