printjob.c revision 5445
11573Srgrimes/*
21573Srgrimes * Copyright (c) 1983, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes *
61573Srgrimes * Redistribution and use in source and binary forms, with or without
71573Srgrimes * modification, are permitted provided that the following conditions
81573Srgrimes * are met:
91573Srgrimes * 1. Redistributions of source code must retain the above copyright
101573Srgrimes *    notice, this list of conditions and the following disclaimer.
111573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer in the
131573Srgrimes *    documentation and/or other materials provided with the distribution.
141573Srgrimes * 3. All advertising materials mentioning features or use of this software
151573Srgrimes *    must display the following acknowledgement:
16251672Semaste *	This product includes software developed by the University of
171573Srgrimes *	California, Berkeley and its contributors.
181573Srgrimes * 4. Neither the name of the University nor the names of its contributors
191573Srgrimes *    may be used to endorse or promote products derived from this software
201573Srgrimes *    without specific prior written permission.
211573Srgrimes *
221573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321573Srgrimes * SUCH DAMAGE.
3350476Speter */
341573Srgrimes
3587738Sru#ifndef lint
361573Srgrimesstatic char copyright[] =
371573Srgrimes"@(#) Copyright (c) 1983, 1993\n\
381573Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3987027Sfenner#endif /* not lint */
4087738Sru
4187738Sru#ifndef lint
4287738Srustatic char sccsid[] = "@(#)printjob.c	8.2 (Berkeley) 4/16/94";
4389585Syar#endif /* not lint */
4487738Sru
4559460Sphantom
4659460Sphantom/*
471573Srgrimes * printjob -- print jobs in the queue.
4883206Sasmodai *
4983206Sasmodai *	NOTE: the lock file is used to pass information to lpq and lprm.
5087738Sru *	it does not need to be removed because file locks are dynamic.
51103012Stjr */
5287738Sru
53103012Stjr#include <sys/param.h>
5487027Sfenner#include <sys/wait.h>
5587027Sfenner#include <sys/stat.h>
56103012Stjr#include <sys/types.h>
5783206Sasmodai
5883206Sasmodai#include <pwd.h>
5983206Sasmodai#include <unistd.h>
601573Srgrimes#include <signal.h>
611573Srgrimes#include <sgtty.h>
621573Srgrimes#include <syslog.h>
631573Srgrimes#include <fcntl.h>
641573Srgrimes#include <dirent.h>
651573Srgrimes#include <errno.h>
661573Srgrimes#include <stdio.h>
671573Srgrimes#include <string.h>
681573Srgrimes#include <stdlib.h>
6987738Sru#include "lp.h"
701573Srgrimes#include "lp.local.h"
711573Srgrimes#include "pathnames.h"
7273152Sobrien#include "extern.h"
7373152Sobrien
7473152Sobrien#define DORETURN	0	/* absorb fork error */
7573152Sobrien#define DOABORT		1	/* abort if dofork fails */
7673152Sobrien
7787738Sru/*
7873152Sobrien * Error tokens
7973152Sobrien */
8087027Sfenner#define REPRINT		-2
8187027Sfenner#define ERROR		-1
8287027Sfenner#define	OK		0
8387027Sfenner#define	FATALERR	1
8487027Sfenner#define	NOACCT		2
8587738Sru#define	FILTERERR	3
8687027Sfenner#define	ACCESS		4
8787027Sfenner
881573Srgrimesstatic dev_t	 fdev;		/* device of file pointed to by symlink */
891573Srgrimesstatic ino_t	 fino;		/* inode of file pointed to by symlink */
901573Srgrimesstatic FILE	*cfp;		/* control file */
911573Srgrimesstatic int	 child;		/* id of any filters */
921573Srgrimesstatic int	 lfd;		/* lock file descriptor */
9387738Srustatic int	 ofd;		/* output filter file descriptor */
941573Srgrimesstatic int	 ofilter;	/* id of output filter, if any */
951573Srgrimesstatic int	 pfd;		/* prstatic inter file descriptor */
961573Srgrimesstatic int	 pid;		/* pid of lpd process */
971573Srgrimesstatic int	 prchild;	/* id of pr process */
981573Srgrimesstatic int	 remote;	/* true if sending files to remote */
991573Srgrimesstatic char	 title[80];	/* ``pr'' title */
1001573Srgrimesstatic int	 tof;		/* true if at top of form */
1011573Srgrimes
1021573Srgrimesstatic char	class[32];		/* classification field */
1031573Srgrimesstatic char	fromhost[32];		/* user's host machine */
1041573Srgrimes				/* indentation size in static characters */
1051573Srgrimesstatic char	indent[10] = "-i0";
1061573Srgrimesstatic char	jobname[100];		/* job or file name */
1071573Srgrimesstatic char	length[10] = "-l";	/* page length in lines */
1081573Srgrimesstatic char	logname[32];		/* user's login name */
1091573Srgrimesstatic char	pxlength[10] = "-y";	/* page length in pixels */
1101573Srgrimesstatic char	pxwidth[10] = "-x";	/* page width in pixels */
1111573Srgrimesstatic char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
11287738Srustatic char	width[10] = "-w";	/* page width in static characters */
1131573Srgrimes
1141573Srgrimesstatic void       abortpr __P((int));
1151573Srgrimesstatic void       banner __P((char *, char *));
1161573Srgrimesstatic int        dofork __P((int));
1171573Srgrimesstatic int        dropit __P((int));
1181573Srgrimesstatic void       init __P((void));
1191573Srgrimesstatic void       openpr __P((void));
1201573Srgrimesstatic int        print __P((int, char *));
12187738Srustatic int        printit __P((char *));
1221573Srgrimesstatic void       pstatus __P((const char *, ...));
1231573Srgrimesstatic char       response __P((void));
1241573Srgrimesstatic void       scan_out __P((int, char *, int));
1251573Srgrimesstatic char      *scnline __P((int, char *, int));
1261573Srgrimesstatic int        sendfile __P((int, char *));
1271573Srgrimesstatic int        sendit __P((char *));
1281573Srgrimesstatic void       sendmail __P((char *, int));
1291573Srgrimesstatic void       setty __P((void));
1301573Srgrimes
1311573Srgrimesvoid
1321573Srgrimesprintjob()
1331573Srgrimes{
1341573Srgrimes	struct stat stb;
1351573Srgrimes	register struct queue *q, **qp;
1361573Srgrimes	struct queue **queue;
13787738Sru	register int i, nitems;
13887738Sru	long pidoff;
1391573Srgrimes	int count = 0;
1401573Srgrimes
1411573Srgrimes	init();					/* set up capabilities */
1421573Srgrimes	(void) write(1, "", 1);			/* ack that daemon is started */
1431573Srgrimes	(void) close(2);			/* set up log file */
1441573Srgrimes	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
1451573Srgrimes		syslog(LOG_ERR, "%s: %m", LF);
1461573Srgrimes		(void) open(_PATH_DEVNULL, O_WRONLY);
1471573Srgrimes	}
1481573Srgrimes	setgid(getegid());
1491573Srgrimes	pid = getpid();				/* for use with lprm */
1501573Srgrimes	setpgrp(0, pid);
1511573Srgrimes	signal(SIGHUP, abortpr);
1521573Srgrimes	signal(SIGINT, abortpr);
1531573Srgrimes	signal(SIGQUIT, abortpr);
1541573Srgrimes	signal(SIGTERM, abortpr);
1551573Srgrimes
1561573Srgrimes	(void) mktemp(tempfile);
1571573Srgrimes
1581573Srgrimes	/*
15987027Sfenner	 * uses short form file names
16087027Sfenner	 */
16187027Sfenner	if (chdir(SD) < 0) {
16287738Sru		syslog(LOG_ERR, "%s: %m", SD);
16387027Sfenner		exit(1);
16487738Sru	}
16587738Sru	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
1661573Srgrimes		exit(0);		/* printing disabled */
1671573Srgrimes	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
1681573Srgrimes	if (lfd < 0) {
1691573Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1701573Srgrimes		exit(1);
1711573Srgrimes	}
17287027Sfenner	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
17373152Sobrien		if (errno == EWOULDBLOCK)	/* active deamon present */
17473152Sobrien			exit(0);
17587027Sfenner		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
17687027Sfenner		exit(1);
17787027Sfenner	}
17887738Sru	ftruncate(lfd, 0);
17987027Sfenner	/*
18087027Sfenner	 * write process id for others to know
18187027Sfenner	 */
18273152Sobrien	sprintf(line, "%u\n", pid);
18373152Sobrien	pidoff = i = strlen(line);
1841573Srgrimes	if (write(lfd, line, i) != i) {
18573152Sobrien		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1861573Srgrimes		exit(1);
18783328Sru	}
18883328Sru	/*
18982975Sache	 * search the spool directory for work and sort by queue order.
19083328Sru	 */
191140613Sache	if ((nitems = getq(&queue)) < 0) {
192140613Sache		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
1931573Srgrimes		exit(1);
19460075Sphantom	}
19582975Sache	if (nitems == 0)		/* no work to do */
19683328Sru		exit(0);
19783328Sru	if (stb.st_mode & 01) {		/* reset queue flag */
19883328Sru		if (fchmod(lfd, stb.st_mode & 0776) < 0)
199140613Sache			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
200140613Sache	}
2011573Srgrimes	openpr();			/* open printer or remote */
2021573Srgrimesagain:
2031573Srgrimes	/*
2041573Srgrimes	 * we found something to do now do it --
205104751Stjr	 *    write the name of the current control file into the lock file
206158774Smaxim	 *    so the spool queue program can tell what we're working on
207104751Stjr	 */
2081573Srgrimes	for (qp = queue; nitems--; free((char *) q)) {
2091573Srgrimes		q = *qp++;
2101573Srgrimes		if (stat(q->q_name, &stb) < 0)
2111573Srgrimes			continue;
2121573Srgrimes	restart:
21373088Sru		(void) lseek(lfd, (off_t)pidoff, 0);
21473152Sobrien		(void) sprintf(line, "%s\n", q->q_name);
21573152Sobrien		i = strlen(line);
21687027Sfenner		if (write(lfd, line, i) != i)
21787027Sfenner			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
21887027Sfenner		if (!remote)
21987027Sfenner			i = printit(q->q_name);
22073152Sobrien		else
22173222Sru			i = sendit(q->q_name);
22273222Sru		/*
22387027Sfenner		 * Check to see if we are supposed to stop printing or
22473152Sobrien		 * if we are to rebuild the queue.
225		 */
226		if (fstat(lfd, &stb) == 0) {
227			/* stop printing before starting next job? */
228			if (stb.st_mode & 0100)
229				goto done;
230			/* rebuild queue (after lpc topq) */
231			if (stb.st_mode & 01) {
232				for (free((char *) q); nitems--; free((char *) q))
233					q = *qp++;
234				if (fchmod(lfd, stb.st_mode & 0776) < 0)
235					syslog(LOG_WARNING, "%s: %s: %m",
236						printer, LO);
237				break;
238			}
239		}
240		if (i == OK)		/* file ok and printed */
241			count++;
242		else if (i == REPRINT) { /* try reprinting the job */
243			syslog(LOG_INFO, "restarting %s", printer);
244			if (ofilter > 0) {
245				kill(ofilter, SIGCONT);	/* to be sure */
246				(void) close(ofd);
247				while ((i = wait(0)) > 0 && i != ofilter)
248					;
249				ofilter = 0;
250			}
251			(void) close(pfd);	/* close printer */
252			if (ftruncate(lfd, pidoff) < 0)
253				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
254			openpr();		/* try to reopen printer */
255			goto restart;
256		}
257	}
258	free((char *) queue);
259	/*
260	 * search the spool directory for more work.
261	 */
262	if ((nitems = getq(&queue)) < 0) {
263		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
264		exit(1);
265	}
266	if (nitems == 0) {		/* no more work to do */
267	done:
268		if (count > 0) {	/* Files actually printed */
269			if (!SF && !tof)
270				(void) write(ofd, FF, strlen(FF));
271			if (TR != NULL)		/* output trailer */
272				(void) write(ofd, TR, strlen(TR));
273		}
274		(void) unlink(tempfile);
275		exit(0);
276	}
277	goto again;
278}
279
280char	fonts[4][50];	/* fonts for troff */
281
282char ifonts[4][40] = {
283	_PATH_VFONTR,
284	_PATH_VFONTI,
285	_PATH_VFONTB,
286	_PATH_VFONTS,
287};
288
289/*
290 * The remaining part is the reading of the control file (cf)
291 * and performing the various actions.
292 */
293static int
294printit(file)
295	char *file;
296{
297	register int i;
298	char *cp;
299	int bombed = OK;
300
301	/*
302	 * open control file; ignore if no longer there.
303	 */
304	if ((cfp = fopen(file, "r")) == NULL) {
305		syslog(LOG_INFO, "%s: %s: %m", printer, file);
306		return(OK);
307	}
308	/*
309	 * Reset troff fonts.
310	 */
311	for (i = 0; i < 4; i++)
312		strcpy(fonts[i], ifonts[i]);
313	sprintf(&width[2], "%d", PW);
314	strcpy(indent+2, "0");
315
316	/*
317	 *      read the control file for work to do
318	 *
319	 *      file format -- first character in the line is a command
320	 *      rest of the line is the argument.
321	 *      valid commands are:
322	 *
323	 *		S -- "stat info" for symbolic link protection
324	 *		J -- "job name" on banner page
325	 *		C -- "class name" on banner page
326	 *              L -- "literal" user's name to print on banner
327	 *		T -- "title" for pr
328	 *		H -- "host name" of machine where lpr was done
329	 *              P -- "person" user's login name
330	 *              I -- "indent" amount to indent output
331	 *              f -- "file name" name of text file to print
332	 *		l -- "file name" text file with control chars
333	 *		p -- "file name" text file to print with pr(1)
334	 *		t -- "file name" troff(1) file to print
335	 *		n -- "file name" ditroff(1) file to print
336	 *		d -- "file name" dvi file to print
337	 *		g -- "file name" plot(1G) file to print
338	 *		v -- "file name" plain raster file to print
339	 *		c -- "file name" cifplot file to print
340	 *		1 -- "R font file" for troff
341	 *		2 -- "I font file" for troff
342	 *		3 -- "B font file" for troff
343	 *		4 -- "S font file" for troff
344	 *		N -- "name" of file (used by lpq)
345	 *              U -- "unlink" name of file to remove
346	 *                    (after we print it. (Pass 2 only)).
347	 *		M -- "mail" to user when done printing
348	 *
349	 *      getline reads a line and expands tabs to blanks
350	 */
351
352	/* pass 1 */
353
354	while (getline(cfp))
355		switch (line[0]) {
356		case 'H':
357			strcpy(fromhost, line+1);
358			if (class[0] == '\0')
359				strncpy(class, line+1, sizeof(class)-1);
360			continue;
361
362		case 'P':
363			strncpy(logname, line+1, sizeof(logname)-1);
364			if (RS) {			/* restricted */
365				if (getpwnam(logname) == NULL) {
366					bombed = NOACCT;
367					sendmail(line+1, bombed);
368					goto pass2;
369				}
370			}
371			continue;
372
373		case 'S':
374			cp = line+1;
375			i = 0;
376			while (*cp >= '0' && *cp <= '9')
377				i = i * 10 + (*cp++ - '0');
378			fdev = i;
379			cp++;
380			i = 0;
381			while (*cp >= '0' && *cp <= '9')
382				i = i * 10 + (*cp++ - '0');
383			fino = i;
384			continue;
385
386		case 'J':
387			if (line[1] != '\0')
388				strncpy(jobname, line+1, sizeof(jobname)-1);
389			else
390				strcpy(jobname, " ");
391			continue;
392
393		case 'C':
394			if (line[1] != '\0')
395				strncpy(class, line+1, sizeof(class)-1);
396			else if (class[0] == '\0')
397				gethostname(class, sizeof(class));
398			continue;
399
400		case 'T':	/* header title for pr */
401			strncpy(title, line+1, sizeof(title)-1);
402			continue;
403
404		case 'L':	/* identification line */
405			if (!SH && !HL)
406				banner(line+1, jobname);
407			continue;
408
409		case '1':	/* troff fonts */
410		case '2':
411		case '3':
412		case '4':
413			if (line[1] != '\0')
414				strcpy(fonts[line[0]-'1'], line+1);
415			continue;
416
417		case 'W':	/* page width */
418			strncpy(width+2, line+1, sizeof(width)-3);
419			continue;
420
421		case 'I':	/* indent amount */
422			strncpy(indent+2, line+1, sizeof(indent)-3);
423			continue;
424
425		default:	/* some file to print */
426			switch (i = print(line[0], line+1)) {
427			case ERROR:
428				if (bombed == OK)
429					bombed = FATALERR;
430				break;
431			case REPRINT:
432				(void) fclose(cfp);
433				return(REPRINT);
434			case FILTERERR:
435			case ACCESS:
436				bombed = i;
437				sendmail(logname, bombed);
438			}
439			title[0] = '\0';
440			continue;
441
442		case 'N':
443		case 'U':
444		case 'M':
445			continue;
446		}
447
448	/* pass 2 */
449
450pass2:
451	fseek(cfp, 0L, 0);
452	while (getline(cfp))
453		switch (line[0]) {
454		case 'L':	/* identification line */
455			if (!SH && HL)
456				banner(line+1, jobname);
457			continue;
458
459		case 'M':
460			if (bombed < NOACCT)	/* already sent if >= NOACCT */
461				sendmail(line+1, bombed);
462			continue;
463
464		case 'U':
465			(void) unlink(line+1);
466		}
467	/*
468	 * clean-up in case another control file exists
469	 */
470	(void) fclose(cfp);
471	(void) unlink(file);
472	return(bombed == OK ? OK : ERROR);
473}
474
475/*
476 * Print a file.
477 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
478 * Return -1 if a non-recoverable error occured,
479 * 2 if the filter detected some errors (but printed the job anyway),
480 * 1 if we should try to reprint this job and
481 * 0 if all is well.
482 * Note: all filters take stdin as the file, stdout as the printer,
483 * stderr as the log file, and must not ignore SIGINT.
484 */
485static int
486print(format, file)
487	int format;
488	char *file;
489{
490	register int n;
491	register char *prog;
492	int fi, fo;
493	FILE *fp;
494	char *av[15], buf[BUFSIZ];
495	int pid, p[2], stopped = 0;
496	union wait status;
497	struct stat stb;
498
499	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
500		return(ERROR);
501	/*
502	 * Check to see if data file is a symbolic link. If so, it should
503	 * still point to the same file or someone is trying to print
504	 * something he shouldn't.
505	 */
506	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
507	    (stb.st_dev != fdev || stb.st_ino != fino))
508		return(ACCESS);
509	if (!SF && !tof) {		/* start on a fresh page */
510		(void) write(ofd, FF, strlen(FF));
511		tof = 1;
512	}
513	if (IF == NULL && (format == 'f' || format == 'l')) {
514		tof = 0;
515		while ((n = read(fi, buf, BUFSIZ)) > 0)
516			if (write(ofd, buf, n) != n) {
517				(void) close(fi);
518				return(REPRINT);
519			}
520		(void) close(fi);
521		return(OK);
522	}
523	switch (format) {
524	case 'p':	/* print file using 'pr' */
525		if (IF == NULL) {	/* use output filter */
526			prog = _PATH_PR;
527			av[0] = "pr";
528			av[1] = width;
529			av[2] = length;
530			av[3] = "-h";
531			av[4] = *title ? title : " ";
532			av[5] = "-F";
533			av[6] = 0;
534			fo = ofd;
535			goto start;
536		}
537		pipe(p);
538		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
539			dup2(fi, 0);		/* file is stdin */
540			dup2(p[1], 1);		/* pipe is stdout */
541			for (n = 3; n < NOFILE; n++)
542				(void) close(n);
543			execl(_PATH_PR, "pr", width, length,
544			    "-h", *title ? title : " ", "-F", 0);
545			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
546			exit(2);
547		}
548		(void) close(p[1]);		/* close output side */
549		(void) close(fi);
550		if (prchild < 0) {
551			prchild = 0;
552			(void) close(p[0]);
553			return(ERROR);
554		}
555		fi = p[0];			/* use pipe for input */
556	case 'f':	/* print plain text file */
557		prog = IF;
558		av[1] = width;
559		av[2] = length;
560		av[3] = indent;
561		n = 4;
562		break;
563	case 'l':	/* like 'f' but pass control characters */
564		prog = IF;
565		av[1] = "-c";
566		av[2] = width;
567		av[3] = length;
568		av[4] = indent;
569		n = 5;
570		break;
571	case 'r':	/* print a fortran text file */
572		prog = RF;
573		av[1] = width;
574		av[2] = length;
575		n = 3;
576		break;
577	case 't':	/* print troff output */
578	case 'n':	/* print ditroff output */
579	case 'd':	/* print tex output */
580		(void) unlink(".railmag");
581		if ((fo = creat(".railmag", FILMOD)) < 0) {
582			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
583			(void) unlink(".railmag");
584		} else {
585			for (n = 0; n < 4; n++) {
586				if (fonts[n][0] != '/')
587					(void) write(fo, _PATH_VFONT,
588					    sizeof(_PATH_VFONT) - 1);
589				(void) write(fo, fonts[n], strlen(fonts[n]));
590				(void) write(fo, "\n", 1);
591			}
592			(void) close(fo);
593		}
594		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
595		av[1] = pxwidth;
596		av[2] = pxlength;
597		n = 3;
598		break;
599	case 'c':	/* print cifplot output */
600		prog = CF;
601		av[1] = pxwidth;
602		av[2] = pxlength;
603		n = 3;
604		break;
605	case 'g':	/* print plot(1G) output */
606		prog = GF;
607		av[1] = pxwidth;
608		av[2] = pxlength;
609		n = 3;
610		break;
611	case 'v':	/* print raster output */
612		prog = VF;
613		av[1] = pxwidth;
614		av[2] = pxlength;
615		n = 3;
616		break;
617	default:
618		(void) close(fi);
619		syslog(LOG_ERR, "%s: illegal format character '%c'",
620			printer, format);
621		return(ERROR);
622	}
623	if ((av[0] = rindex(prog, '/')) != NULL)
624		av[0]++;
625	else
626		av[0] = prog;
627	av[n++] = "-n";
628	av[n++] = logname;
629	av[n++] = "-h";
630	av[n++] = fromhost;
631	av[n++] = AF;
632	av[n] = 0;
633	fo = pfd;
634	if (ofilter > 0) {		/* stop output filter */
635		write(ofd, "\031\1", 2);
636		while ((pid =
637		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
638			;
639		if (status.w_stopval != WSTOPPED) {
640			(void) close(fi);
641			syslog(LOG_WARNING, "%s: output filter died (%d)",
642				printer, status.w_retcode);
643			return(REPRINT);
644		}
645		stopped++;
646	}
647start:
648	if ((child = dofork(DORETURN)) == 0) {	/* child */
649		dup2(fi, 0);
650		dup2(fo, 1);
651		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
652		if (n >= 0)
653			dup2(n, 2);
654		for (n = 3; n < NOFILE; n++)
655			(void) close(n);
656		execv(prog, av);
657		syslog(LOG_ERR, "cannot execv %s", prog);
658		exit(2);
659	}
660	(void) close(fi);
661	if (child < 0)
662		status.w_retcode = 100;
663	else
664		while ((pid = wait((int *)&status)) > 0 && pid != child)
665			;
666	child = 0;
667	prchild = 0;
668	if (stopped) {		/* restart output filter */
669		if (kill(ofilter, SIGCONT) < 0) {
670			syslog(LOG_ERR, "cannot restart output filter");
671			exit(1);
672		}
673	}
674	tof = 0;
675
676	/* Copy filter output to "lf" logfile */
677	if (fp = fopen(tempfile, "r")) {
678		while (fgets(buf, sizeof(buf), fp))
679			fputs(buf, stderr);
680		fclose(fp);
681	}
682
683	if (!WIFEXITED(status)) {
684		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
685			printer, format, status.w_termsig);
686		return(ERROR);
687	}
688	switch (status.w_retcode) {
689	case 0:
690		tof = 1;
691		return(OK);
692	case 1:
693		return(REPRINT);
694	default:
695		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
696			printer, format, status.w_retcode);
697	case 2:
698		return(ERROR);
699	}
700}
701
702/*
703 * Send the daemon control file (cf) and any data files.
704 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
705 * 0 if all is well.
706 */
707static int
708sendit(file)
709	char *file;
710{
711	register int i, err = OK;
712	char *cp, last[BUFSIZ];
713
714	/*
715	 * open control file
716	 */
717	if ((cfp = fopen(file, "r")) == NULL)
718		return(OK);
719	/*
720	 *      read the control file for work to do
721	 *
722	 *      file format -- first character in the line is a command
723	 *      rest of the line is the argument.
724	 *      commands of interest are:
725	 *
726	 *            a-z -- "file name" name of file to print
727	 *              U -- "unlink" name of file to remove
728	 *                    (after we print it. (Pass 2 only)).
729	 */
730
731	/*
732	 * pass 1
733	 */
734	while (getline(cfp)) {
735	again:
736		if (line[0] == 'S') {
737			cp = line+1;
738			i = 0;
739			while (*cp >= '0' && *cp <= '9')
740				i = i * 10 + (*cp++ - '0');
741			fdev = i;
742			cp++;
743			i = 0;
744			while (*cp >= '0' && *cp <= '9')
745				i = i * 10 + (*cp++ - '0');
746			fino = i;
747			continue;
748		}
749		if (line[0] >= 'a' && line[0] <= 'z') {
750			strcpy(last, line);
751			while (i = getline(cfp))
752				if (strcmp(last, line))
753					break;
754			switch (sendfile('\3', last+1)) {
755			case OK:
756				if (i)
757					goto again;
758				break;
759			case REPRINT:
760				(void) fclose(cfp);
761				return(REPRINT);
762			case ACCESS:
763				sendmail(logname, ACCESS);
764			case ERROR:
765				err = ERROR;
766			}
767			break;
768		}
769	}
770	if (err == OK && sendfile('\2', file) > 0) {
771		(void) fclose(cfp);
772		return(REPRINT);
773	}
774	/*
775	 * pass 2
776	 */
777	fseek(cfp, 0L, 0);
778	while (getline(cfp))
779		if (line[0] == 'U')
780			(void) unlink(line+1);
781	/*
782	 * clean-up in case another control file exists
783	 */
784	(void) fclose(cfp);
785	(void) unlink(file);
786	return(err);
787}
788
789/*
790 * Send a data file to the remote machine and spool it.
791 * Return positive if we should try resending.
792 */
793static int
794sendfile(type, file)
795	int type;
796	char *file;
797{
798	register int f, i, amt;
799	struct stat stb;
800	char buf[BUFSIZ];
801	int sizerr, resp;
802
803	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
804		return(ERROR);
805	/*
806	 * Check to see if data file is a symbolic link. If so, it should
807	 * still point to the same file or someone is trying to print something
808	 * he shouldn't.
809	 */
810	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
811	    (stb.st_dev != fdev || stb.st_ino != fino))
812		return(ACCESS);
813	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
814	amt = strlen(buf);
815	for (i = 0;  ; i++) {
816		if (write(pfd, buf, amt) != amt ||
817		    (resp = response()) < 0 || resp == '\1') {
818			(void) close(f);
819			return(REPRINT);
820		} else if (resp == '\0')
821			break;
822		if (i == 0)
823			pstatus("no space on remote; waiting for queue to drain");
824		if (i == 10)
825			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
826				printer, RM);
827		sleep(5 * 60);
828	}
829	if (i)
830		pstatus("sending to %s", RM);
831	sizerr = 0;
832	for (i = 0; i < stb.st_size; i += BUFSIZ) {
833		amt = BUFSIZ;
834		if (i + amt > stb.st_size)
835			amt = stb.st_size - i;
836		if (sizerr == 0 && read(f, buf, amt) != amt)
837			sizerr = 1;
838		if (write(pfd, buf, amt) != amt) {
839			(void) close(f);
840			return(REPRINT);
841		}
842	}
843
844
845
846
847	(void) close(f);
848	if (sizerr) {
849		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
850		/* tell recvjob to ignore this file */
851		(void) write(pfd, "\1", 1);
852		return(ERROR);
853	}
854	if (write(pfd, "", 1) != 1 || response())
855		return(REPRINT);
856	return(OK);
857}
858
859/*
860 * Check to make sure there have been no errors and that both programs
861 * are in sync with eachother.
862 * Return non-zero if the connection was lost.
863 */
864static char
865response()
866{
867	char resp;
868
869	if (read(pfd, &resp, 1) != 1) {
870		syslog(LOG_INFO, "%s: lost connection", printer);
871		return(-1);
872	}
873	return(resp);
874}
875
876/*
877 * Banner printing stuff
878 */
879static void
880banner(name1, name2)
881	char *name1, *name2;
882{
883	time_t tvec;
884	extern char *ctime();
885
886	time(&tvec);
887	if (!SF && !tof)
888		(void) write(ofd, FF, strlen(FF));
889	if (SB) {	/* short banner only */
890		if (class[0]) {
891			(void) write(ofd, class, strlen(class));
892			(void) write(ofd, ":", 1);
893		}
894		(void) write(ofd, name1, strlen(name1));
895		(void) write(ofd, "  Job: ", 7);
896		(void) write(ofd, name2, strlen(name2));
897		(void) write(ofd, "  Date: ", 8);
898		(void) write(ofd, ctime(&tvec), 24);
899		(void) write(ofd, "\n", 1);
900	} else {	/* normal banner */
901		(void) write(ofd, "\n\n\n", 3);
902		scan_out(ofd, name1, '\0');
903		(void) write(ofd, "\n\n", 2);
904		scan_out(ofd, name2, '\0');
905		if (class[0]) {
906			(void) write(ofd,"\n\n\n",3);
907			scan_out(ofd, class, '\0');
908		}
909		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
910		(void) write(ofd, name2, strlen(name2));
911		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
912		(void) write(ofd, ctime(&tvec), 24);
913		(void) write(ofd, "\n", 1);
914	}
915	if (!SF)
916		(void) write(ofd, FF, strlen(FF));
917	tof = 1;
918}
919
920static char *
921scnline(key, p, c)
922	register int key;
923	register char *p;
924	int c;
925{
926	register scnwidth;
927
928	for (scnwidth = WIDTH; --scnwidth;) {
929		key <<= 1;
930		*p++ = key & 0200 ? c : BACKGND;
931	}
932	return (p);
933}
934
935#define TRC(q)	(((q)-' ')&0177)
936
937static void
938scan_out(scfd, scsp, dlm)
939	int scfd, dlm;
940	char *scsp;
941{
942	register char *strp;
943	register nchrs, j;
944	char outbuf[LINELEN+1], *sp, c, cc;
945	int d, scnhgt;
946	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
947
948	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
949		strp = &outbuf[0];
950		sp = scsp;
951		for (nchrs = 0; ; ) {
952			d = dropit(c = TRC(cc = *sp++));
953			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
954				for (j = WIDTH; --j;)
955					*strp++ = BACKGND;
956			else
957				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
958			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
959				break;
960			*strp++ = BACKGND;
961			*strp++ = BACKGND;
962		}
963		while (*--strp == BACKGND && strp >= outbuf)
964			;
965		strp++;
966		*strp++ = '\n';
967		(void) write(scfd, outbuf, strp-outbuf);
968	}
969}
970
971static int
972dropit(c)
973	int c;
974{
975	switch(c) {
976
977	case TRC('_'):
978	case TRC(';'):
979	case TRC(','):
980	case TRC('g'):
981	case TRC('j'):
982	case TRC('p'):
983	case TRC('q'):
984	case TRC('y'):
985		return (DROP);
986
987	default:
988		return (0);
989	}
990}
991
992/*
993 * sendmail ---
994 *   tell people about job completion
995 */
996static void
997sendmail(user, bombed)
998	char *user;
999	int bombed;
1000{
1001	register int i;
1002	int p[2], s;
1003	register char *cp;
1004	char buf[100];
1005	struct stat stb;
1006	FILE *fp;
1007
1008	pipe(p);
1009	if ((s = dofork(DORETURN)) == 0) {		/* child */
1010		dup2(p[0], 0);
1011		for (i = 3; i < NOFILE; i++)
1012			(void) close(i);
1013		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1014			cp++;
1015	else
1016			cp = _PATH_SENDMAIL;
1017		sprintf(buf, "%s@%s", user, fromhost);
1018		execl(_PATH_SENDMAIL, cp, buf, 0);
1019		exit(0);
1020	} else if (s > 0) {				/* parent */
1021		dup2(p[1], 1);
1022		printf("To: %s@%s\n", user, fromhost);
1023		printf("Subject: printer job\n\n");
1024		printf("Your printer job ");
1025		if (*jobname)
1026			printf("(%s) ", jobname);
1027		switch (bombed) {
1028		case OK:
1029			printf("\ncompleted successfully\n");
1030			break;
1031		default:
1032		case FATALERR:
1033			printf("\ncould not be printed\n");
1034			break;
1035		case NOACCT:
1036			printf("\ncould not be printed without an account on %s\n", host);
1037			break;
1038		case FILTERERR:
1039			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1040			    (fp = fopen(tempfile, "r")) == NULL) {
1041				printf("\nwas printed but had some errors\n");
1042				break;
1043			}
1044			printf("\nwas printed but had the following errors:\n");
1045			while ((i = getc(fp)) != EOF)
1046				putchar(i);
1047			(void) fclose(fp);
1048			break;
1049		case ACCESS:
1050			printf("\nwas not printed because it was not linked to the original file\n");
1051		}
1052		fflush(stdout);
1053		(void) close(1);
1054	}
1055	(void) close(p[0]);
1056	(void) close(p[1]);
1057	wait(&s);
1058}
1059
1060/*
1061 * dofork - fork with retries on failure
1062 */
1063static int
1064dofork(action)
1065	int action;
1066{
1067	register int i, pid;
1068
1069	for (i = 0; i < 20; i++) {
1070		if ((pid = fork()) < 0) {
1071			sleep((unsigned)(i*i));
1072			continue;
1073		}
1074		/*
1075		 * Child should run as daemon instead of root
1076		 */
1077		if (pid == 0)
1078			setuid(DU);
1079		return(pid);
1080	}
1081	syslog(LOG_ERR, "can't fork");
1082
1083	switch (action) {
1084	case DORETURN:
1085		return (-1);
1086	default:
1087		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1088		/*FALL THRU*/
1089	case DOABORT:
1090		exit(1);
1091	}
1092	/*NOTREACHED*/
1093}
1094
1095/*
1096 * Kill child processes to abort current job.
1097 */
1098static void
1099abortpr(signo)
1100	int signo;
1101{
1102	(void) unlink(tempfile);
1103	kill(0, SIGINT);
1104	if (ofilter > 0)
1105		kill(ofilter, SIGCONT);
1106	while (wait(NULL) > 0)
1107		;
1108	exit(0);
1109}
1110
1111static void
1112init()
1113{
1114	int status;
1115	char *s;
1116
1117	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1118		syslog(LOG_ERR, "can't open printer description file");
1119		exit(1);
1120	} else if (status == -1) {
1121		syslog(LOG_ERR, "unknown printer: %s", printer);
1122		exit(1);
1123	} else if (status == -3)
1124		fatal("potential reference loop detected in printcap file");
1125
1126	if (cgetstr(bp, "lp", &LP) == -1)
1127		LP = _PATH_DEFDEVLP;
1128	if (cgetstr(bp, "rp", &RP) == -1)
1129		RP = DEFLP;
1130	if (cgetstr(bp, "lo", &LO) == -1)
1131		LO = DEFLOCK;
1132	if (cgetstr(bp, "st", &ST) == -1)
1133		ST = DEFSTAT;
1134	if (cgetstr(bp, "lf", &LF) == -1)
1135		LF = _PATH_CONSOLE;
1136	if (cgetstr(bp, "sd", &SD) == -1)
1137		SD = _PATH_DEFSPOOL;
1138	if (cgetnum(bp, "du", &DU) < 0)
1139		DU = DEFUID;
1140	if (cgetstr(bp,"ff", &FF) == -1)
1141		FF = DEFFF;
1142	if (cgetnum(bp, "pw", &PW) < 0)
1143		PW = DEFWIDTH;
1144	sprintf(&width[2], "%d", PW);
1145	if (cgetnum(bp, "pl", &PL) < 0)
1146		PL = DEFLENGTH;
1147	sprintf(&length[2], "%d", PL);
1148	if (cgetnum(bp,"px", &PX) < 0)
1149		PX = 0;
1150	sprintf(&pxwidth[2], "%d", PX);
1151	if (cgetnum(bp, "py", &PY) < 0)
1152		PY = 0;
1153	sprintf(&pxlength[2], "%d", PY);
1154	cgetstr(bp, "rm", &RM);
1155	if (s = checkremote())
1156		syslog(LOG_WARNING, s);
1157
1158	cgetstr(bp, "af", &AF);
1159	cgetstr(bp, "of", &OF);
1160	cgetstr(bp, "if", &IF);
1161	cgetstr(bp, "rf", &RF);
1162	cgetstr(bp, "tf", &TF);
1163	cgetstr(bp, "nf", &NF);
1164	cgetstr(bp, "df", &DF);
1165	cgetstr(bp, "gf", &GF);
1166	cgetstr(bp, "vf", &VF);
1167	cgetstr(bp, "cf", &CF);
1168	cgetstr(bp, "tr", &TR);
1169
1170	RS = (cgetcap(bp, "rs", ':') != NULL);
1171	SF = (cgetcap(bp, "sf", ':') != NULL);
1172	SH = (cgetcap(bp, "sh", ':') != NULL);
1173	SB = (cgetcap(bp, "sb", ':') != NULL);
1174	HL = (cgetcap(bp, "hl", ':') != NULL);
1175	RW = (cgetcap(bp, "rw", ':') != NULL);
1176
1177	cgetnum(bp, "br", &BR);
1178	if (cgetnum(bp, "fc", &FC) < 0)
1179		FC = 0;
1180	if (cgetnum(bp, "fs", &FS) < 0)
1181		FS = 0;
1182	if (cgetnum(bp, "xc", &XC) < 0)
1183		XC = 0;
1184	if (cgetnum(bp, "xs", &XS) < 0)
1185		XS = 0;
1186
1187	tof = (cgetcap(bp, "fo", ':') == NULL);
1188}
1189
1190/*
1191 * Acquire line printer or remote connection.
1192 */
1193static void
1194openpr()
1195{
1196	register int i, n;
1197	int resp;
1198
1199	if (!sendtorem && *LP) {
1200		for (i = 1; ; i = i < 32 ? i << 1 : i) {
1201			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1202			if (pfd >= 0)
1203				break;
1204			if (errno == ENOENT) {
1205				syslog(LOG_ERR, "%s: %m", LP);
1206				exit(1);
1207			}
1208			if (i == 1)
1209				pstatus("waiting for %s to become ready (offline ?)", printer);
1210			sleep(i);
1211		}
1212		if (isatty(pfd))
1213			setty();
1214		pstatus("%s is ready and printing", printer);
1215	} else if (RM != NULL) {
1216		for (i = 1; ; i = i < 256 ? i << 1 : i) {
1217			resp = -1;
1218			pfd = getport(RM);
1219			if (pfd >= 0) {
1220				(void) sprintf(line, "\2%s\n", RP);
1221				n = strlen(line);
1222				if (write(pfd, line, n) == n &&
1223				    (resp = response()) == '\0')
1224					break;
1225				(void) close(pfd);
1226			}
1227			if (i == 1) {
1228				if (resp < 0)
1229					pstatus("waiting for %s to come up", RM);
1230				else {
1231					pstatus("waiting for queue to be enabled on %s", RM);
1232					i = 256;
1233				}
1234			}
1235			sleep(i);
1236		}
1237		pstatus("sending to %s", RM);
1238		remote = 1;
1239	} else {
1240		syslog(LOG_ERR, "%s: no line printer device or host name",
1241			printer);
1242		exit(1);
1243	}
1244	/*
1245	 * Start up an output filter, if needed.
1246	 */
1247	if (!remote && OF) {
1248		int p[2];
1249		char *cp;
1250
1251		pipe(p);
1252		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1253			dup2(p[0], 0);		/* pipe is std in */
1254			dup2(pfd, 1);		/* printer is std out */
1255			for (i = 3; i < NOFILE; i++)
1256				(void) close(i);
1257			if ((cp = rindex(OF, '/')) == NULL)
1258				cp = OF;
1259			else
1260				cp++;
1261			execl(OF, cp, width, length, 0);
1262			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1263			exit(1);
1264		}
1265		(void) close(p[0]);		/* close input side */
1266		ofd = p[1];			/* use pipe for output */
1267	} else {
1268		ofd = pfd;
1269		ofilter = 0;
1270	}
1271}
1272
1273struct bauds {
1274	int	baud;
1275	int	speed;
1276} bauds[] = {
1277	50,	B50,
1278	75,	B75,
1279	110,	B110,
1280	134,	B134,
1281	150,	B150,
1282	200,	B200,
1283	300,	B300,
1284	600,	B600,
1285	1200,	B1200,
1286	1800,	B1800,
1287	2400,	B2400,
1288	4800,	B4800,
1289	9600,	B9600,
1290	19200,	EXTA,
1291	38400,	EXTB,
1292	0,	0
1293};
1294
1295/*
1296 * setup tty lines.
1297 */
1298static void
1299setty()
1300{
1301	struct sgttyb ttybuf;
1302	register struct bauds *bp;
1303
1304	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1305		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1306		exit(1);
1307	}
1308	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1309		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
1310		exit(1);
1311	}
1312	if (BR > 0) {
1313		for (bp = bauds; bp->baud; bp++)
1314			if (BR == bp->baud)
1315				break;
1316		if (!bp->baud) {
1317			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1318			exit(1);
1319		}
1320		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1321	}
1322	ttybuf.sg_flags &= ~FC;
1323	ttybuf.sg_flags |= FS;
1324	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1325		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
1326		exit(1);
1327	}
1328	if (XC) {
1329		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1330			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
1331			exit(1);
1332		}
1333	}
1334	if (XS) {
1335		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1336			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
1337			exit(1);
1338		}
1339	}
1340}
1341
1342#if __STDC__
1343#include <stdarg.h>
1344#else
1345#include <varargs.h>
1346#endif
1347
1348void
1349#if __STDC__
1350pstatus(const char *msg, ...)
1351#else
1352pstatus(msg, va_alist)
1353	char *msg;
1354        va_dcl
1355#endif
1356{
1357	register int fd;
1358	char buf[BUFSIZ];
1359	va_list ap;
1360#if __STDC__
1361	va_start(ap, msg);
1362#else
1363	va_start(ap);
1364#endif
1365
1366	umask(0);
1367	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1368	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1369		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1370		exit(1);
1371	}
1372	ftruncate(fd, 0);
1373	(void)vsnprintf(buf, sizeof(buf), msg, ap);
1374	va_end(ap);
1375	strcat(buf, "\n");
1376	(void) write(fd, buf, strlen(buf));
1377	(void) close(fd);
1378}
1379