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