Home | History | Annotate | Line # | Download | only in lpd
printjob.c revision 1.3
      1 /*
      2  * Copyright (c) 1983 Regents of the University of California.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #ifndef lint
     35 /*static char sccsid[] = "from: @(#)printjob.c	5.13 (Berkeley) 3/2/91";*/
     36 static char rcsid[] = "$Id: printjob.c,v 1.3 1994/03/07 05:38:21 cgd Exp $";
     37 #endif /* not lint */
     38 
     39 /*
     40  * printjob -- print jobs in the queue.
     41  *
     42  *	NOTE: the lock file is used to pass information to lpq and lprm.
     43  *	it does not need to be removed because file locks are dynamic.
     44  */
     45 
     46 #include "lp.h"
     47 #include "pathnames.h"
     48 
     49 #define DORETURN	0	/* absorb fork error */
     50 #define DOABORT		1	/* abort if dofork fails */
     51 
     52 /*
     53  * Error tokens
     54  */
     55 #define REPRINT		-2
     56 #define ERROR		-1
     57 #define	OK		0
     58 #define	FATALERR	1
     59 #define	NOACCT		2
     60 #define	FILTERERR	3
     61 #define	ACCESS		4
     62 
     63 char	title[80];		/* ``pr'' title */
     64 FILE	*cfp;			/* control file */
     65 int	pfd;			/* printer file descriptor */
     66 int	ofd;			/* output filter file descriptor */
     67 int	lfd;			/* lock file descriptor */
     68 int	pid;			/* pid of lpd process */
     69 int	prchild;		/* id of pr process */
     70 int	child;			/* id of any filters */
     71 int	ofilter;		/* id of output filter, if any */
     72 int	tof;			/* true if at top of form */
     73 int	remote;			/* true if sending files to remote */
     74 dev_t	fdev;			/* device of file pointed to by symlink */
     75 ino_t	fino;			/* inode of file pointed to by symlink */
     76 
     77 char	fromhost[32];		/* user's host machine */
     78 char	logname[32];		/* user's login name */
     79 char	jobname[100];		/* job or file name */
     80 char	class[32];		/* classification field */
     81 char	width[10] = "-w";	/* page width in characters */
     82 char	length[10] = "-l";	/* page length in lines */
     83 char	pxwidth[10] = "-x";	/* page width in pixels */
     84 char	pxlength[10] = "-y";	/* page length in pixels */
     85 char	indent[10] = "-i0";	/* indentation size in characters */
     86 char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
     87 
     88 printjob()
     89 {
     90 	struct stat stb;
     91 	register struct queue *q, **qp;
     92 	struct queue **queue;
     93 	register int i, nitems;
     94 	long pidoff;
     95 	int count = 0;
     96 	void abortpr();
     97 
     98 	init();					/* set up capabilities */
     99 	(void) write(1, "", 1);			/* ack that daemon is started */
    100 	(void) close(2);			/* set up log file */
    101 	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
    102 		syslog(LOG_ERR, "%s: %m", LF);
    103 		(void) open(_PATH_DEVNULL, O_WRONLY);
    104 	}
    105 	setgid(getegid());
    106 	pid = getpid();				/* for use with lprm */
    107 	setpgrp(0, pid);
    108 	signal(SIGHUP, abortpr);
    109 	signal(SIGINT, abortpr);
    110 	signal(SIGQUIT, abortpr);
    111 	signal(SIGTERM, abortpr);
    112 
    113 	(void) mktemp(tempfile);
    114 
    115 	/*
    116 	 * uses short form file names
    117 	 */
    118 	if (chdir(SD) < 0) {
    119 		syslog(LOG_ERR, "%s: %m", SD);
    120 		exit(1);
    121 	}
    122 	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
    123 		exit(0);		/* printing disabled */
    124 	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
    125 	if (lfd < 0) {
    126 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
    127 		exit(1);
    128 	}
    129 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
    130 		if (errno == EWOULDBLOCK)	/* active deamon present */
    131 			exit(0);
    132 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
    133 		exit(1);
    134 	}
    135 	ftruncate(lfd, 0);
    136 	/*
    137 	 * write process id for others to know
    138 	 */
    139 	sprintf(line, "%u\n", pid);
    140 	pidoff = i = strlen(line);
    141 	if (write(lfd, line, i) != i) {
    142 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
    143 		exit(1);
    144 	}
    145 	/*
    146 	 * search the spool directory for work and sort by queue order.
    147 	 */
    148 	if ((nitems = getq(&queue)) < 0) {
    149 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
    150 		exit(1);
    151 	}
    152 	if (nitems == 0)		/* no work to do */
    153 		exit(0);
    154 	if (stb.st_mode & 01) {		/* reset queue flag */
    155 		if (fchmod(lfd, stb.st_mode & 0776) < 0)
    156 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
    157 	}
    158 	openpr();			/* open printer or remote */
    159 again:
    160 	/*
    161 	 * we found something to do now do it --
    162 	 *    write the name of the current control file into the lock file
    163 	 *    so the spool queue program can tell what we're working on
    164 	 */
    165 	for (qp = queue; nitems--; free((char *) q)) {
    166 		q = *qp++;
    167 		if (stat(q->q_name, &stb) < 0)
    168 			continue;
    169 	restart:
    170 		(void) lseek(lfd, pidoff, 0);
    171 		(void) sprintf(line, "%s\n", q->q_name);
    172 		i = strlen(line);
    173 		if (write(lfd, line, i) != i)
    174 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
    175 		if (!remote)
    176 			i = printit(q->q_name);
    177 		else
    178 			i = sendit(q->q_name);
    179 		/*
    180 		 * Check to see if we are supposed to stop printing or
    181 		 * if we are to rebuild the queue.
    182 		 */
    183 		if (fstat(lfd, &stb) == 0) {
    184 			/* stop printing before starting next job? */
    185 			if (stb.st_mode & 0100)
    186 				goto done;
    187 			/* rebuild queue (after lpc topq) */
    188 			if (stb.st_mode & 01) {
    189 				for (free((char *) q); nitems--; free((char *) q))
    190 					q = *qp++;
    191 				if (fchmod(lfd, stb.st_mode & 0776) < 0)
    192 					syslog(LOG_WARNING, "%s: %s: %m",
    193 						printer, LO);
    194 				break;
    195 			}
    196 		}
    197 		if (i == OK)		/* file ok and printed */
    198 			count++;
    199 		else if (i == REPRINT) { /* try reprinting the job */
    200 			syslog(LOG_INFO, "restarting %s", printer);
    201 			if (ofilter > 0) {
    202 				kill(ofilter, SIGCONT);	/* to be sure */
    203 				(void) close(ofd);
    204 				while ((i = wait(0)) > 0 && i != ofilter)
    205 					;
    206 				ofilter = 0;
    207 			}
    208 			(void) close(pfd);	/* close printer */
    209 			if (ftruncate(lfd, pidoff) < 0)
    210 				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
    211 			openpr();		/* try to reopen printer */
    212 			goto restart;
    213 		}
    214 	}
    215 	free((char *) queue);
    216 	/*
    217 	 * search the spool directory for more work.
    218 	 */
    219 	if ((nitems = getq(&queue)) < 0) {
    220 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
    221 		exit(1);
    222 	}
    223 	if (nitems == 0) {		/* no more work to do */
    224 	done:
    225 		if (count > 0) {	/* Files actually printed */
    226 			if (!SF && !tof)
    227 				(void) write(ofd, FF, strlen(FF));
    228 			if (TR != NULL)		/* output trailer */
    229 				(void) write(ofd, TR, strlen(TR));
    230 		}
    231 		(void) unlink(tempfile);
    232 		exit(0);
    233 	}
    234 	goto again;
    235 }
    236 
    237 char	fonts[4][50];	/* fonts for troff */
    238 
    239 char ifonts[4][40] = {
    240 	_PATH_VFONTR,
    241 	_PATH_VFONTI,
    242 	_PATH_VFONTB,
    243 	_PATH_VFONTS,
    244 };
    245 
    246 /*
    247  * The remaining part is the reading of the control file (cf)
    248  * and performing the various actions.
    249  */
    250 printit(file)
    251 	char *file;
    252 {
    253 	register int i;
    254 	char *cp;
    255 	int bombed = OK;
    256 
    257 	/*
    258 	 * open control file; ignore if no longer there.
    259 	 */
    260 	if ((cfp = fopen(file, "r")) == NULL) {
    261 		syslog(LOG_INFO, "%s: %s: %m", printer, file);
    262 		return(OK);
    263 	}
    264 	/*
    265 	 * Reset troff fonts.
    266 	 */
    267 	for (i = 0; i < 4; i++)
    268 		strcpy(fonts[i], ifonts[i]);
    269 	sprintf(&width[2], "%d", PW);
    270 	strcpy(indent+2, "0");
    271 
    272 	/*
    273 	 *      read the control file for work to do
    274 	 *
    275 	 *      file format -- first character in the line is a command
    276 	 *      rest of the line is the argument.
    277 	 *      valid commands are:
    278 	 *
    279 	 *		S -- "stat info" for symbolic link protection
    280 	 *		J -- "job name" on banner page
    281 	 *		C -- "class name" on banner page
    282 	 *              L -- "literal" user's name to print on banner
    283 	 *		T -- "title" for pr
    284 	 *		H -- "host name" of machine where lpr was done
    285 	 *              P -- "person" user's login name
    286 	 *              I -- "indent" amount to indent output
    287 	 *              f -- "file name" name of text file to print
    288 	 *		l -- "file name" text file with control chars
    289 	 *		p -- "file name" text file to print with pr(1)
    290 	 *		t -- "file name" troff(1) file to print
    291 	 *		n -- "file name" ditroff(1) file to print
    292 	 *		d -- "file name" dvi file to print
    293 	 *		g -- "file name" plot(1G) file to print
    294 	 *		v -- "file name" plain raster file to print
    295 	 *		c -- "file name" cifplot file to print
    296 	 *		1 -- "R font file" for troff
    297 	 *		2 -- "I font file" for troff
    298 	 *		3 -- "B font file" for troff
    299 	 *		4 -- "S font file" for troff
    300 	 *		N -- "name" of file (used by lpq)
    301 	 *              U -- "unlink" name of file to remove
    302 	 *                    (after we print it. (Pass 2 only)).
    303 	 *		M -- "mail" to user when done printing
    304 	 *
    305 	 *      getline reads a line and expands tabs to blanks
    306 	 */
    307 
    308 	/* pass 1 */
    309 
    310 	while (getline(cfp))
    311 		switch (line[0]) {
    312 		case 'H':
    313 			strcpy(fromhost, line+1);
    314 			if (class[0] == '\0')
    315 				strncpy(class, line+1, sizeof(class)-1);
    316 			continue;
    317 
    318 		case 'P':
    319 			strncpy(logname, line+1, sizeof(logname)-1);
    320 			if (RS) {			/* restricted */
    321 				if (getpwnam(logname) == (struct passwd *)0) {
    322 					bombed = NOACCT;
    323 					sendmail(line+1, bombed);
    324 					goto pass2;
    325 				}
    326 			}
    327 			continue;
    328 
    329 		case 'S':
    330 			cp = line+1;
    331 			i = 0;
    332 			while (*cp >= '0' && *cp <= '9')
    333 				i = i * 10 + (*cp++ - '0');
    334 			fdev = i;
    335 			cp++;
    336 			i = 0;
    337 			while (*cp >= '0' && *cp <= '9')
    338 				i = i * 10 + (*cp++ - '0');
    339 			fino = i;
    340 			continue;
    341 
    342 		case 'J':
    343 			if (line[1] != '\0')
    344 				strncpy(jobname, line+1, sizeof(jobname)-1);
    345 			else
    346 				strcpy(jobname, " ");
    347 			continue;
    348 
    349 		case 'C':
    350 			if (line[1] != '\0')
    351 				strncpy(class, line+1, sizeof(class)-1);
    352 			else if (class[0] == '\0')
    353 				gethostname(class, sizeof(class));
    354 			continue;
    355 
    356 		case 'T':	/* header title for pr */
    357 			strncpy(title, line+1, sizeof(title)-1);
    358 			continue;
    359 
    360 		case 'L':	/* identification line */
    361 			if (!SH && !HL)
    362 				banner(line+1, jobname);
    363 			continue;
    364 
    365 		case '1':	/* troff fonts */
    366 		case '2':
    367 		case '3':
    368 		case '4':
    369 			if (line[1] != '\0')
    370 				strcpy(fonts[line[0]-'1'], line+1);
    371 			continue;
    372 
    373 		case 'W':	/* page width */
    374 			strncpy(width+2, line+1, sizeof(width)-3);
    375 			continue;
    376 
    377 		case 'I':	/* indent amount */
    378 			strncpy(indent+2, line+1, sizeof(indent)-3);
    379 			continue;
    380 
    381 		default:	/* some file to print */
    382 			switch (i = print(line[0], line+1)) {
    383 			case ERROR:
    384 				if (bombed == OK)
    385 					bombed = FATALERR;
    386 				break;
    387 			case REPRINT:
    388 				(void) fclose(cfp);
    389 				return(REPRINT);
    390 			case FILTERERR:
    391 			case ACCESS:
    392 				bombed = i;
    393 				sendmail(logname, bombed);
    394 			}
    395 			title[0] = '\0';
    396 			continue;
    397 
    398 		case 'N':
    399 		case 'U':
    400 		case 'M':
    401 			continue;
    402 		}
    403 
    404 	/* pass 2 */
    405 
    406 pass2:
    407 	fseek(cfp, 0L, 0);
    408 	while (getline(cfp))
    409 		switch (line[0]) {
    410 		case 'L':	/* identification line */
    411 			if (!SH && HL)
    412 				banner(line+1, jobname);
    413 			continue;
    414 
    415 		case 'M':
    416 			if (bombed < NOACCT)	/* already sent if >= NOACCT */
    417 				sendmail(line+1, bombed);
    418 			continue;
    419 
    420 		case 'U':
    421 			(void) unlink(line+1);
    422 		}
    423 	/*
    424 	 * clean-up in case another control file exists
    425 	 */
    426 	(void) fclose(cfp);
    427 	(void) unlink(file);
    428 	return(bombed == OK ? OK : ERROR);
    429 }
    430 
    431 /*
    432  * Print a file.
    433  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
    434  * Return -1 if a non-recoverable error occured,
    435  * 2 if the filter detected some errors (but printed the job anyway),
    436  * 1 if we should try to reprint this job and
    437  * 0 if all is well.
    438  * Note: all filters take stdin as the file, stdout as the printer,
    439  * stderr as the log file, and must not ignore SIGINT.
    440  */
    441 print(format, file)
    442 	int format;
    443 	char *file;
    444 {
    445 	register int n;
    446 	register char *prog;
    447 	int fi, fo;
    448 	FILE *fp;
    449 	char *av[15], buf[BUFSIZ];
    450 	int pid, p[2], stopped = 0;
    451 	union wait status;
    452 	struct stat stb;
    453 
    454 	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
    455 		return(ERROR);
    456 	/*
    457 	 * Check to see if data file is a symbolic link. If so, it should
    458 	 * still point to the same file or someone is trying to print
    459 	 * something he shouldn't.
    460 	 */
    461 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
    462 	    (stb.st_dev != fdev || stb.st_ino != fino))
    463 		return(ACCESS);
    464 	if (!SF && !tof) {		/* start on a fresh page */
    465 		(void) write(ofd, FF, strlen(FF));
    466 		tof = 1;
    467 	}
    468 	if (IF == NULL && (format == 'f' || format == 'l')) {
    469 		tof = 0;
    470 		while ((n = read(fi, buf, BUFSIZ)) > 0)
    471 			if (write(ofd, buf, n) != n) {
    472 				(void) close(fi);
    473 				return(REPRINT);
    474 			}
    475 		(void) close(fi);
    476 		return(OK);
    477 	}
    478 	switch (format) {
    479 	case 'p':	/* print file using 'pr' */
    480 		if (IF == NULL) {	/* use output filter */
    481 			prog = _PATH_PR;
    482 			av[0] = "pr";
    483 			av[1] = width;
    484 			av[2] = length;
    485 			av[3] = "-h";
    486 			av[4] = *title ? title : " ";
    487 			av[5] = 0;
    488 			fo = ofd;
    489 			goto start;
    490 		}
    491 		pipe(p);
    492 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
    493 			dup2(fi, 0);		/* file is stdin */
    494 			dup2(p[1], 1);		/* pipe is stdout */
    495 			for (n = 3; n < NOFILE; n++)
    496 				(void) close(n);
    497 			execl(_PATH_PR, "pr", width, length,
    498 			    "-h", *title ? title : " ", 0);
    499 			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
    500 			exit(2);
    501 		}
    502 		(void) close(p[1]);		/* close output side */
    503 		(void) close(fi);
    504 		if (prchild < 0) {
    505 			prchild = 0;
    506 			(void) close(p[0]);
    507 			return(ERROR);
    508 		}
    509 		fi = p[0];			/* use pipe for input */
    510 	case 'f':	/* print plain text file */
    511 		prog = IF;
    512 		av[1] = width;
    513 		av[2] = length;
    514 		av[3] = indent;
    515 		n = 4;
    516 		break;
    517 	case 'l':	/* like 'f' but pass control characters */
    518 		prog = IF;
    519 		av[1] = "-c";
    520 		av[2] = width;
    521 		av[3] = length;
    522 		av[4] = indent;
    523 		n = 5;
    524 		break;
    525 	case 'r':	/* print a fortran text file */
    526 		prog = RF;
    527 		av[1] = width;
    528 		av[2] = length;
    529 		n = 3;
    530 		break;
    531 	case 't':	/* print troff output */
    532 	case 'n':	/* print ditroff output */
    533 	case 'd':	/* print tex output */
    534 		(void) unlink(".railmag");
    535 		if ((fo = creat(".railmag", FILMOD)) < 0) {
    536 			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
    537 			(void) unlink(".railmag");
    538 		} else {
    539 			for (n = 0; n < 4; n++) {
    540 				if (fonts[n][0] != '/')
    541 					(void) write(fo, _PATH_VFONT, 15);
    542 				(void) write(fo, fonts[n], strlen(fonts[n]));
    543 				(void) write(fo, "\n", 1);
    544 			}
    545 			(void) close(fo);
    546 		}
    547 		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
    548 		av[1] = pxwidth;
    549 		av[2] = pxlength;
    550 		n = 3;
    551 		break;
    552 	case 'c':	/* print cifplot output */
    553 		prog = CF;
    554 		av[1] = pxwidth;
    555 		av[2] = pxlength;
    556 		n = 3;
    557 		break;
    558 	case 'g':	/* print plot(1G) output */
    559 		prog = GF;
    560 		av[1] = pxwidth;
    561 		av[2] = pxlength;
    562 		n = 3;
    563 		break;
    564 	case 'v':	/* print raster output */
    565 		prog = VF;
    566 		av[1] = pxwidth;
    567 		av[2] = pxlength;
    568 		n = 3;
    569 		break;
    570 	default:
    571 		(void) close(fi);
    572 		syslog(LOG_ERR, "%s: illegal format character '%c'",
    573 			printer, format);
    574 		return(ERROR);
    575 	}
    576 	if ((av[0] = rindex(prog, '/')) != NULL)
    577 		av[0]++;
    578 	else
    579 		av[0] = prog;
    580 	av[n++] = "-n";
    581 	av[n++] = logname;
    582 	av[n++] = "-h";
    583 	av[n++] = fromhost;
    584 	av[n++] = AF;
    585 	av[n] = 0;
    586 	fo = pfd;
    587 	if (ofilter > 0) {		/* stop output filter */
    588 		write(ofd, "\031\1", 2);
    589 		while ((pid =
    590 		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
    591 			;
    592 		if (status.w_stopval != WSTOPPED) {
    593 			(void) close(fi);
    594 			syslog(LOG_WARNING, "%s: output filter died (%d)",
    595 				printer, status.w_retcode);
    596 			return(REPRINT);
    597 		}
    598 		stopped++;
    599 	}
    600 start:
    601 	if ((child = dofork(DORETURN)) == 0) {	/* child */
    602 		dup2(fi, 0);
    603 		dup2(fo, 1);
    604 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
    605 		if (n >= 0)
    606 			dup2(n, 2);
    607 		for (n = 3; n < NOFILE; n++)
    608 			(void) close(n);
    609 		execv(prog, av);
    610 		syslog(LOG_ERR, "cannot execv %s", prog);
    611 		exit(2);
    612 	}
    613 	(void) close(fi);
    614 	if (child < 0)
    615 		status.w_retcode = 100;
    616 	else
    617 		while ((pid = wait((int *)&status)) > 0 && pid != child)
    618 			;
    619 	child = 0;
    620 	prchild = 0;
    621 	if (stopped) {		/* restart output filter */
    622 		if (kill(ofilter, SIGCONT) < 0) {
    623 			syslog(LOG_ERR, "cannot restart output filter");
    624 			exit(1);
    625 		}
    626 	}
    627 	tof = 0;
    628 
    629 	/* Copy filter output to "lf" logfile */
    630 	if (fp = fopen(tempfile, "r")) {
    631 		char tbuf[512];
    632 
    633 		while (fgets(buf, sizeof(buf), fp))
    634 			fputs(buf, stderr);
    635 		close(fp);
    636 	}
    637 
    638 	if (!WIFEXITED(status)) {
    639 		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
    640 			printer, format, status.w_termsig);
    641 		return(ERROR);
    642 	}
    643 	switch (status.w_retcode) {
    644 	case 0:
    645 		tof = 1;
    646 		return(OK);
    647 	case 1:
    648 		return(REPRINT);
    649 	default:
    650 		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
    651 			printer, format, status.w_retcode);
    652 	case 2:
    653 		return(ERROR);
    654 	}
    655 }
    656 
    657 /*
    658  * Send the daemon control file (cf) and any data files.
    659  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
    660  * 0 if all is well.
    661  */
    662 sendit(file)
    663 	char *file;
    664 {
    665 	register int i, err = OK;
    666 	char *cp, last[BUFSIZ];
    667 
    668 	/*
    669 	 * open control file
    670 	 */
    671 	if ((cfp = fopen(file, "r")) == NULL)
    672 		return(OK);
    673 	/*
    674 	 *      read the control file for work to do
    675 	 *
    676 	 *      file format -- first character in the line is a command
    677 	 *      rest of the line is the argument.
    678 	 *      commands of interest are:
    679 	 *
    680 	 *            a-z -- "file name" name of file to print
    681 	 *              U -- "unlink" name of file to remove
    682 	 *                    (after we print it. (Pass 2 only)).
    683 	 */
    684 
    685 	/*
    686 	 * pass 1
    687 	 */
    688 	while (getline(cfp)) {
    689 	again:
    690 		if (line[0] == 'S') {
    691 			cp = line+1;
    692 			i = 0;
    693 			while (*cp >= '0' && *cp <= '9')
    694 				i = i * 10 + (*cp++ - '0');
    695 			fdev = i;
    696 			cp++;
    697 			i = 0;
    698 			while (*cp >= '0' && *cp <= '9')
    699 				i = i * 10 + (*cp++ - '0');
    700 			fino = i;
    701 			continue;
    702 		}
    703 		if (line[0] >= 'a' && line[0] <= 'z') {
    704 			strcpy(last, line);
    705 			while (i = getline(cfp))
    706 				if (strcmp(last, line))
    707 					break;
    708 			switch (sendfile('\3', last+1)) {
    709 			case OK:
    710 				if (i)
    711 					goto again;
    712 				break;
    713 			case REPRINT:
    714 				(void) fclose(cfp);
    715 				return(REPRINT);
    716 			case ACCESS:
    717 				sendmail(logname, ACCESS);
    718 			case ERROR:
    719 				err = ERROR;
    720 			}
    721 			break;
    722 		}
    723 	}
    724 	if (err == OK && sendfile('\2', file) > 0) {
    725 		(void) fclose(cfp);
    726 		return(REPRINT);
    727 	}
    728 	/*
    729 	 * pass 2
    730 	 */
    731 	fseek(cfp, 0L, 0);
    732 	while (getline(cfp))
    733 		if (line[0] == 'U')
    734 			(void) unlink(line+1);
    735 	/*
    736 	 * clean-up in case another control file exists
    737 	 */
    738 	(void) fclose(cfp);
    739 	(void) unlink(file);
    740 	return(err);
    741 }
    742 
    743 /*
    744  * Send a data file to the remote machine and spool it.
    745  * Return positive if we should try resending.
    746  */
    747 sendfile(type, file)
    748 	char type, *file;
    749 {
    750 	register int f, i, amt;
    751 	struct stat stb;
    752 	char buf[BUFSIZ];
    753 	int sizerr, resp;
    754 
    755 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
    756 		return(ERROR);
    757 	/*
    758 	 * Check to see if data file is a symbolic link. If so, it should
    759 	 * still point to the same file or someone is trying to print something
    760 	 * he shouldn't.
    761 	 */
    762 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
    763 	    (stb.st_dev != fdev || stb.st_ino != fino))
    764 		return(ACCESS);
    765 	(void) sprintf(buf, "%c%d %s\n", type, stb.st_size, file);
    766 	amt = strlen(buf);
    767 	for (i = 0;  ; i++) {
    768 		if (write(pfd, buf, amt) != amt ||
    769 		    (resp = response()) < 0 || resp == '\1') {
    770 			(void) close(f);
    771 			return(REPRINT);
    772 		} else if (resp == '\0')
    773 			break;
    774 		if (i == 0)
    775 			status("no space on remote; waiting for queue to drain");
    776 		if (i == 10)
    777 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
    778 				printer, RM);
    779 		sleep(5 * 60);
    780 	}
    781 	if (i)
    782 		status("sending to %s", RM);
    783 	sizerr = 0;
    784 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
    785 		amt = BUFSIZ;
    786 		if (i + amt > stb.st_size)
    787 			amt = stb.st_size - i;
    788 		if (sizerr == 0 && read(f, buf, amt) != amt)
    789 			sizerr = 1;
    790 		if (write(pfd, buf, amt) != amt) {
    791 			(void) close(f);
    792 			return(REPRINT);
    793 		}
    794 	}
    795 	(void) close(f);
    796 	if (sizerr) {
    797 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
    798 		/* tell recvjob to ignore this file */
    799 		(void) write(pfd, "\1", 1);
    800 		return(ERROR);
    801 	}
    802 	if (write(pfd, "", 1) != 1 || response())
    803 		return(REPRINT);
    804 	return(OK);
    805 }
    806 
    807 /*
    808  * Check to make sure there have been no errors and that both programs
    809  * are in sync with eachother.
    810  * Return non-zero if the connection was lost.
    811  */
    812 response()
    813 {
    814 	char resp;
    815 
    816 	if (read(pfd, &resp, 1) != 1) {
    817 		syslog(LOG_INFO, "%s: lost connection", printer);
    818 		return(-1);
    819 	}
    820 	return(resp);
    821 }
    822 
    823 /*
    824  * Banner printing stuff
    825  */
    826 banner(name1, name2)
    827 	char *name1, *name2;
    828 {
    829 	time_t tvec;
    830 	extern char *ctime();
    831 
    832 	time(&tvec);
    833 	if (!SF && !tof)
    834 		(void) write(ofd, FF, strlen(FF));
    835 	if (SB) {	/* short banner only */
    836 		if (class[0]) {
    837 			(void) write(ofd, class, strlen(class));
    838 			(void) write(ofd, ":", 1);
    839 		}
    840 		(void) write(ofd, name1, strlen(name1));
    841 		(void) write(ofd, "  Job: ", 7);
    842 		(void) write(ofd, name2, strlen(name2));
    843 		(void) write(ofd, "  Date: ", 8);
    844 		(void) write(ofd, ctime(&tvec), 24);
    845 		(void) write(ofd, "\n", 1);
    846 	} else {	/* normal banner */
    847 		(void) write(ofd, "\n\n\n", 3);
    848 		scan_out(ofd, name1, '\0');
    849 		(void) write(ofd, "\n\n", 2);
    850 		scan_out(ofd, name2, '\0');
    851 		if (class[0]) {
    852 			(void) write(ofd,"\n\n\n",3);
    853 			scan_out(ofd, class, '\0');
    854 		}
    855 		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
    856 		(void) write(ofd, name2, strlen(name2));
    857 		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
    858 		(void) write(ofd, ctime(&tvec), 24);
    859 		(void) write(ofd, "\n", 1);
    860 	}
    861 	if (!SF)
    862 		(void) write(ofd, FF, strlen(FF));
    863 	tof = 1;
    864 }
    865 
    866 char *
    867 scnline(key, p, c)
    868 	register char key, *p;
    869 	char c;
    870 {
    871 	register scnwidth;
    872 
    873 	for (scnwidth = WIDTH; --scnwidth;) {
    874 		key <<= 1;
    875 		*p++ = key & 0200 ? c : BACKGND;
    876 	}
    877 	return (p);
    878 }
    879 
    880 #define TRC(q)	(((q)-' ')&0177)
    881 
    882 scan_out(scfd, scsp, dlm)
    883 	int scfd;
    884 	char *scsp, dlm;
    885 {
    886 	register char *strp;
    887 	register nchrs, j;
    888 	char outbuf[LINELEN+1], *sp, c, cc;
    889 	int d, scnhgt;
    890 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
    891 
    892 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
    893 		strp = &outbuf[0];
    894 		sp = scsp;
    895 		for (nchrs = 0; ; ) {
    896 			d = dropit(c = TRC(cc = *sp++));
    897 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
    898 				for (j = WIDTH; --j;)
    899 					*strp++ = BACKGND;
    900 			else
    901 				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
    902 			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
    903 				break;
    904 			*strp++ = BACKGND;
    905 			*strp++ = BACKGND;
    906 		}
    907 		while (*--strp == BACKGND && strp >= outbuf)
    908 			;
    909 		strp++;
    910 		*strp++ = '\n';
    911 		(void) write(scfd, outbuf, strp-outbuf);
    912 	}
    913 }
    914 
    915 dropit(c)
    916 	char c;
    917 {
    918 	switch(c) {
    919 
    920 	case TRC('_'):
    921 	case TRC(';'):
    922 	case TRC(','):
    923 	case TRC('g'):
    924 	case TRC('j'):
    925 	case TRC('p'):
    926 	case TRC('q'):
    927 	case TRC('y'):
    928 		return (DROP);
    929 
    930 	default:
    931 		return (0);
    932 	}
    933 }
    934 
    935 /*
    936  * sendmail ---
    937  *   tell people about job completion
    938  */
    939 sendmail(user, bombed)
    940 	char *user;
    941 	int bombed;
    942 {
    943 	register int i;
    944 	int p[2], s;
    945 	register char *cp;
    946 	char buf[100];
    947 	struct stat stb;
    948 	FILE *fp;
    949 
    950 	pipe(p);
    951 	if ((s = dofork(DORETURN)) == 0) {		/* child */
    952 		dup2(p[0], 0);
    953 		for (i = 3; i < NOFILE; i++)
    954 			(void) close(i);
    955 		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
    956 			cp++;
    957 		else
    958 			cp = _PATH_SENDMAIL;
    959 		sprintf(buf, "%s@%s", user, fromhost);
    960 		execl(_PATH_SENDMAIL, cp, buf, 0);
    961 		exit(0);
    962 	} else if (s > 0) {				/* parent */
    963 		dup2(p[1], 1);
    964 		printf("To: %s@%s\n", user, fromhost);
    965 		printf("Subject: printer job\n\n");
    966 		printf("Your printer job ");
    967 		if (*jobname)
    968 			printf("(%s) ", jobname);
    969 		switch (bombed) {
    970 		case OK:
    971 			printf("\ncompleted successfully\n");
    972 			break;
    973 		default:
    974 		case FATALERR:
    975 			printf("\ncould not be printed\n");
    976 			break;
    977 		case NOACCT:
    978 			printf("\ncould not be printed without an account on %s\n", host);
    979 			break;
    980 		case FILTERERR:
    981 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
    982 			    (fp = fopen(tempfile, "r")) == NULL) {
    983 				printf("\nwas printed but had some errors\n");
    984 				break;
    985 			}
    986 			printf("\nwas printed but had the following errors:\n");
    987 			while ((i = getc(fp)) != EOF)
    988 				putchar(i);
    989 			(void) fclose(fp);
    990 			break;
    991 		case ACCESS:
    992 			printf("\nwas not printed because it was not linked to the original file\n");
    993 		}
    994 		fflush(stdout);
    995 		(void) close(1);
    996 	}
    997 	(void) close(p[0]);
    998 	(void) close(p[1]);
    999 	wait(&s);
   1000 }
   1001 
   1002 /*
   1003  * dofork - fork with retries on failure
   1004  */
   1005 dofork(action)
   1006 	int action;
   1007 {
   1008 	register int i, pid;
   1009 
   1010 	for (i = 0; i < 20; i++) {
   1011 		if ((pid = fork()) < 0) {
   1012 			sleep((unsigned)(i*i));
   1013 			continue;
   1014 		}
   1015 		/*
   1016 		 * Child should run as daemon instead of root
   1017 		 */
   1018 		if (pid == 0)
   1019 			setuid(DU);
   1020 		return(pid);
   1021 	}
   1022 	syslog(LOG_ERR, "can't fork");
   1023 
   1024 	switch (action) {
   1025 	case DORETURN:
   1026 		return (-1);
   1027 	default:
   1028 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
   1029 		/*FALL THRU*/
   1030 	case DOABORT:
   1031 		exit(1);
   1032 	}
   1033 	/*NOTREACHED*/
   1034 }
   1035 
   1036 /*
   1037  * Kill child processes to abort current job.
   1038  */
   1039 void
   1040 abortpr()
   1041 {
   1042 	(void) unlink(tempfile);
   1043 	kill(0, SIGINT);
   1044 	if (ofilter > 0)
   1045 		kill(ofilter, SIGCONT);
   1046 	while (wait(NULL) > 0)
   1047 		;
   1048 	exit(0);
   1049 }
   1050 
   1051 init()
   1052 {
   1053 	int status;
   1054 	char *s;
   1055 
   1056 	if ((status = pgetent(line, printer)) < 0) {
   1057 		syslog(LOG_ERR, "can't open printer description file");
   1058 		exit(1);
   1059 	} else if (status == 0) {
   1060 		syslog(LOG_ERR, "unknown printer: %s", printer);
   1061 		exit(1);
   1062 	}
   1063 	if ((LP = pgetstr("lp", &bp)) == NULL)
   1064 		LP = _PATH_DEFDEVLP;
   1065 	if ((RP = pgetstr("rp", &bp)) == NULL)
   1066 		RP = DEFLP;
   1067 	if ((LO = pgetstr("lo", &bp)) == NULL)
   1068 		LO = DEFLOCK;
   1069 	if ((ST = pgetstr("st", &bp)) == NULL)
   1070 		ST = DEFSTAT;
   1071 	if ((LF = pgetstr("lf", &bp)) == NULL)
   1072 		LF = _PATH_CONSOLE;
   1073 	if ((SD = pgetstr("sd", &bp)) == NULL)
   1074 		SD = _PATH_DEFSPOOL;
   1075 	if ((DU = pgetnum("du")) < 0)
   1076 		DU = DEFUID;
   1077 	if ((FF = pgetstr("ff", &bp)) == NULL)
   1078 		FF = DEFFF;
   1079 	if ((PW = pgetnum("pw")) < 0)
   1080 		PW = DEFWIDTH;
   1081 	sprintf(&width[2], "%d", PW);
   1082 	if ((PL = pgetnum("pl")) < 0)
   1083 		PL = DEFLENGTH;
   1084 	sprintf(&length[2], "%d", PL);
   1085 	if ((PX = pgetnum("px")) < 0)
   1086 		PX = 0;
   1087 	sprintf(&pxwidth[2], "%d", PX);
   1088 	if ((PY = pgetnum("py")) < 0)
   1089 		PY = 0;
   1090 	sprintf(&pxlength[2], "%d", PY);
   1091 	RM = pgetstr("rm", &bp);
   1092 	if (s = checkremote())
   1093 		syslog(LOG_WARNING, s);
   1094 
   1095 	AF = pgetstr("af", &bp);
   1096 	OF = pgetstr("of", &bp);
   1097 	IF = pgetstr("if", &bp);
   1098 	RF = pgetstr("rf", &bp);
   1099 	TF = pgetstr("tf", &bp);
   1100 	NF = pgetstr("nf", &bp);
   1101 	DF = pgetstr("df", &bp);
   1102 	GF = pgetstr("gf", &bp);
   1103 	VF = pgetstr("vf", &bp);
   1104 	CF = pgetstr("cf", &bp);
   1105 	TR = pgetstr("tr", &bp);
   1106 	RS = pgetflag("rs");
   1107 	SF = pgetflag("sf");
   1108 	SH = pgetflag("sh");
   1109 	SB = pgetflag("sb");
   1110 	HL = pgetflag("hl");
   1111 	RW = pgetflag("rw");
   1112 	BR = pgetnum("br");
   1113 	if ((FC = pgetnum("fc")) < 0)
   1114 		FC = 0;
   1115 	if ((FS = pgetnum("fs")) < 0)
   1116 		FS = 0;
   1117 	if ((XC = pgetnum("xc")) < 0)
   1118 		XC = 0;
   1119 	if ((XS = pgetnum("xs")) < 0)
   1120 		XS = 0;
   1121 	tof = !pgetflag("fo");
   1122 }
   1123 
   1124 /*
   1125  * Acquire line printer or remote connection.
   1126  */
   1127 openpr()
   1128 {
   1129 	register int i, n;
   1130 	int resp;
   1131 
   1132 	if (!sendtorem && *LP) {
   1133 		for (i = 1; ; i = i < 32 ? i << 1 : i) {
   1134 			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
   1135 			if (pfd >= 0)
   1136 				break;
   1137 			if (errno == ENOENT) {
   1138 				syslog(LOG_ERR, "%s: %m", LP);
   1139 				exit(1);
   1140 			}
   1141 			if (i == 1)
   1142 				status("waiting for %s to become ready (offline ?)", printer);
   1143 			sleep(i);
   1144 		}
   1145 		if (isatty(pfd))
   1146 			setty();
   1147 		status("%s is ready and printing", printer);
   1148 	} else if (RM != NULL) {
   1149 		for (i = 1; ; i = i < 256 ? i << 1 : i) {
   1150 			resp = -1;
   1151 			pfd = getport(RM);
   1152 			if (pfd >= 0) {
   1153 				(void) sprintf(line, "\2%s\n", RP);
   1154 				n = strlen(line);
   1155 				if (write(pfd, line, n) == n &&
   1156 				    (resp = response()) == '\0')
   1157 					break;
   1158 				(void) close(pfd);
   1159 			}
   1160 			if (i == 1) {
   1161 				if (resp < 0)
   1162 					status("waiting for %s to come up", RM);
   1163 				else {
   1164 					status("waiting for queue to be enabled on %s", RM);
   1165 					i = 256;
   1166 				}
   1167 			}
   1168 			sleep(i);
   1169 		}
   1170 		status("sending to %s", RM);
   1171 		remote = 1;
   1172 	} else {
   1173 		syslog(LOG_ERR, "%s: no line printer device or host name",
   1174 			printer);
   1175 		exit(1);
   1176 	}
   1177 	/*
   1178 	 * Start up an output filter, if needed.
   1179 	 */
   1180 	if (!remote && OF) {
   1181 		int p[2];
   1182 		char *cp;
   1183 
   1184 		pipe(p);
   1185 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
   1186 			dup2(p[0], 0);		/* pipe is std in */
   1187 			dup2(pfd, 1);		/* printer is std out */
   1188 			for (i = 3; i < NOFILE; i++)
   1189 				(void) close(i);
   1190 			if ((cp = rindex(OF, '/')) == NULL)
   1191 				cp = OF;
   1192 			else
   1193 				cp++;
   1194 			execl(OF, cp, width, length, 0);
   1195 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
   1196 			exit(1);
   1197 		}
   1198 		(void) close(p[0]);		/* close input side */
   1199 		ofd = p[1];			/* use pipe for output */
   1200 	} else {
   1201 		ofd = pfd;
   1202 		ofilter = 0;
   1203 	}
   1204 }
   1205 
   1206 struct bauds {
   1207 	int	baud;
   1208 	int	speed;
   1209 } bauds[] = {
   1210 	50,	B50,
   1211 	75,	B75,
   1212 	110,	B110,
   1213 	134,	B134,
   1214 	150,	B150,
   1215 	200,	B200,
   1216 	300,	B300,
   1217 	600,	B600,
   1218 	1200,	B1200,
   1219 	1800,	B1800,
   1220 	2400,	B2400,
   1221 	4800,	B4800,
   1222 	9600,	B9600,
   1223 	19200,	EXTA,
   1224 	38400,	EXTB,
   1225 	0,	0
   1226 };
   1227 
   1228 /*
   1229  * setup tty lines.
   1230  */
   1231 setty()
   1232 {
   1233 	struct sgttyb ttybuf;
   1234 	register struct bauds *bp;
   1235 
   1236 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
   1237 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
   1238 		exit(1);
   1239 	}
   1240 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
   1241 		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
   1242 		exit(1);
   1243 	}
   1244 	if (BR > 0) {
   1245 		for (bp = bauds; bp->baud; bp++)
   1246 			if (BR == bp->baud)
   1247 				break;
   1248 		if (!bp->baud) {
   1249 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
   1250 			exit(1);
   1251 		}
   1252 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
   1253 	}
   1254 	ttybuf.sg_flags &= ~FC;
   1255 	ttybuf.sg_flags |= FS;
   1256 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
   1257 		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
   1258 		exit(1);
   1259 	}
   1260 	if (XC) {
   1261 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
   1262 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
   1263 			exit(1);
   1264 		}
   1265 	}
   1266 	if (XS) {
   1267 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
   1268 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
   1269 			exit(1);
   1270 		}
   1271 	}
   1272 }
   1273 
   1274 /*VARARGS1*/
   1275 status(msg, a1, a2, a3)
   1276 	char *msg;
   1277 {
   1278 	register int fd;
   1279 	char buf[BUFSIZ];
   1280 
   1281 	umask(0);
   1282 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
   1283 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
   1284 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
   1285 		exit(1);
   1286 	}
   1287 	ftruncate(fd, 0);
   1288 	sprintf(buf, msg, a1, a2, a3);
   1289 	strcat(buf, "\n");
   1290 	(void) write(fd, buf, strlen(buf));
   1291 	(void) close(fd);
   1292 }
   1293