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