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