printjob.c revision 10530
1/*
2 * Copyright (c) 1983, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by the University of
17 *	California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36static char copyright[] =
37"@(#) Copyright (c) 1983, 1993\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42static char sccsid[] = "@(#)printjob.c	8.2 (Berkeley) 4/16/94";
43#endif /* not lint */
44
45
46/*
47 * printjob -- print jobs in the queue.
48 *
49 *	NOTE: the lock file is used to pass information to lpq and lprm.
50 *	it does not need to be removed because file locks are dynamic.
51 */
52
53#include <sys/param.h>
54#include <sys/wait.h>
55#include <sys/stat.h>
56#include <sys/types.h>
57
58#include <pwd.h>
59#include <unistd.h>
60#include <signal.h>
61#include <sgtty.h>
62#include <syslog.h>
63#include <fcntl.h>
64#include <dirent.h>
65#include <errno.h>
66#include <stdio.h>
67#include <string.h>
68#include <stdlib.h>
69#include "lp.h"
70#include "lp.local.h"
71#include "pathnames.h"
72#include "extern.h"
73
74#define DORETURN	0	/* absorb fork error */
75#define DOABORT		1	/* abort if dofork fails */
76
77/*
78 * Error tokens
79 */
80#define REPRINT		-2
81#define ERROR		-1
82#define	OK		0
83#define	FATALERR	1
84#define	NOACCT		2
85#define	FILTERERR	3
86#define	ACCESS		4
87
88static dev_t	 fdev;		/* device of file pointed to by symlink */
89static ino_t	 fino;		/* inode of file pointed to by symlink */
90static FILE	*cfp;		/* control file */
91static int	 child;		/* id of any filters */
92static int	 lfd;		/* lock file descriptor */
93static int	 ofd;		/* output filter file descriptor */
94static int	 ofilter;	/* id of output filter, if any */
95static int	 pfd;		/* prstatic inter file descriptor */
96static int	 pid;		/* pid of lpd process */
97static int	 prchild;	/* id of pr process */
98static int	 remote;	/* true if sending files to remote */
99static char	 title[80];	/* ``pr'' title */
100static int	 tof;		/* true if at top of form */
101
102static char	class[32];		/* classification field */
103static char	fromhost[32];		/* user's host machine */
104				/* indentation size in static characters */
105static char	indent[10] = "-i0";
106static char	jobname[100];		/* job or file name */
107static char	length[10] = "-l";	/* page length in lines */
108static char	logname[32];		/* user's login name */
109static char	pxlength[10] = "-y";	/* page length in pixels */
110static char	pxwidth[10] = "-x";	/* page width in pixels */
111static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
112static char	width[10] = "-w";	/* page width in static characters */
113
114static void       abortpr __P((int));
115static void       banner __P((char *, char *));
116static int        dofork __P((int));
117static int        dropit __P((int));
118static void       init __P((void));
119static void       openpr __P((void));
120static int        print __P((int, char *));
121static int        printit __P((char *));
122static void       pstatus __P((const char *, ...));
123static char       response __P((void));
124static void       scan_out __P((int, char *, int));
125static char      *scnline __P((int, char *, int));
126static int        sendfile __P((int, char *));
127static int        sendit __P((char *));
128static void       sendmail __P((char *, int));
129static void       setty __P((void));
130
131void
132printjob()
133{
134	struct stat stb;
135	register struct queue *q, **qp;
136	struct queue **queue;
137	register int i, nitems;
138	long pidoff;
139	int count = 0;
140
141	init();					/* set up capabilities */
142	(void) write(1, "", 1);			/* ack that daemon is started */
143	(void) close(2);			/* set up log file */
144	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
145		syslog(LOG_ERR, "%s: %m", LF);
146		(void) open(_PATH_DEVNULL, O_WRONLY);
147	}
148	setgid(getegid());
149	pid = getpid();				/* for use with lprm */
150	setpgrp(0, pid);
151	signal(SIGHUP, abortpr);
152	signal(SIGINT, abortpr);
153	signal(SIGQUIT, abortpr);
154	signal(SIGTERM, abortpr);
155
156	(void) mktemp(tempfile);
157
158	/*
159	 * uses short form file names
160	 */
161	if (chdir(SD) < 0) {
162		syslog(LOG_ERR, "%s: %m", SD);
163		exit(1);
164	}
165	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
166		exit(0);		/* printing disabled */
167	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
168	if (lfd < 0) {
169		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
170		exit(1);
171	}
172	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
173		if (errno == EWOULDBLOCK)	/* active deamon present */
174			exit(0);
175		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
176		exit(1);
177	}
178	ftruncate(lfd, 0);
179	/*
180	 * write process id for others to know
181	 */
182	sprintf(line, "%u\n", pid);
183	pidoff = i = strlen(line);
184	if (write(lfd, line, i) != i) {
185		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
186		exit(1);
187	}
188	/*
189	 * search the spool directory for work and sort by queue order.
190	 */
191	if ((nitems = getq(&queue)) < 0) {
192		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
193		exit(1);
194	}
195	if (nitems == 0)		/* no work to do */
196		exit(0);
197	if (stb.st_mode & 01) {		/* reset queue flag */
198		if (fchmod(lfd, stb.st_mode & 0776) < 0)
199			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
200	}
201	openpr();			/* open printer or remote */
202again:
203	/*
204	 * we found something to do now do it --
205	 *    write the name of the current control file into the lock file
206	 *    so the spool queue program can tell what we're working on
207	 */
208	for (qp = queue; nitems--; free((char *) q)) {
209		q = *qp++;
210		if (stat(q->q_name, &stb) < 0)
211			continue;
212	restart:
213		(void) lseek(lfd, (off_t)pidoff, 0);
214		(void) sprintf(line, "%s\n", q->q_name);
215		i = strlen(line);
216		if (write(lfd, line, i) != i)
217			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
218		if (!remote)
219			i = printit(q->q_name);
220		else
221			i = sendit(q->q_name);
222		/*
223		 * Check to see if we are supposed to stop printing or
224		 * 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			closelog();
542			for (n = 3; n < NOFILE; n++)
543				(void) close(n);
544			execl(_PATH_PR, "pr", width, length,
545			    "-h", *title ? title : " ", "-F", 0);
546			openlog("lpd", LOG_PID, LOG_LPR);
547			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
548			exit(2);
549		}
550		(void) close(p[1]);		/* close output side */
551		(void) close(fi);
552		if (prchild < 0) {
553			prchild = 0;
554			(void) close(p[0]);
555			return(ERROR);
556		}
557		fi = p[0];			/* use pipe for input */
558	case 'f':	/* print plain text file */
559		prog = IF;
560		av[1] = width;
561		av[2] = length;
562		av[3] = indent;
563		n = 4;
564		break;
565	case 'l':	/* like 'f' but pass control characters */
566		prog = IF;
567		av[1] = "-c";
568		av[2] = width;
569		av[3] = length;
570		av[4] = indent;
571		n = 5;
572		break;
573	case 'r':	/* print a fortran text file */
574		prog = RF;
575		av[1] = width;
576		av[2] = length;
577		n = 3;
578		break;
579	case 't':	/* print troff output */
580	case 'n':	/* print ditroff output */
581	case 'd':	/* print tex output */
582		(void) unlink(".railmag");
583		if ((fo = creat(".railmag", FILMOD)) < 0) {
584			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
585			(void) unlink(".railmag");
586		} else {
587			for (n = 0; n < 4; n++) {
588				if (fonts[n][0] != '/')
589					(void) write(fo, _PATH_VFONT,
590					    sizeof(_PATH_VFONT) - 1);
591				(void) write(fo, fonts[n], strlen(fonts[n]));
592				(void) write(fo, "\n", 1);
593			}
594			(void) close(fo);
595		}
596		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
597		av[1] = pxwidth;
598		av[2] = pxlength;
599		n = 3;
600		break;
601	case 'c':	/* print cifplot output */
602		prog = CF;
603		av[1] = pxwidth;
604		av[2] = pxlength;
605		n = 3;
606		break;
607	case 'g':	/* print plot(1G) output */
608		prog = GF;
609		av[1] = pxwidth;
610		av[2] = pxlength;
611		n = 3;
612		break;
613	case 'v':	/* print raster output */
614		prog = VF;
615		av[1] = pxwidth;
616		av[2] = pxlength;
617		n = 3;
618		break;
619	default:
620		(void) close(fi);
621		syslog(LOG_ERR, "%s: illegal format character '%c'",
622			printer, format);
623		return(ERROR);
624	}
625	if ((av[0] = rindex(prog, '/')) != NULL)
626		av[0]++;
627	else
628		av[0] = prog;
629	av[n++] = "-n";
630	av[n++] = logname;
631	av[n++] = "-h";
632	av[n++] = fromhost;
633	av[n++] = AF;
634	av[n] = 0;
635	fo = pfd;
636	if (ofilter > 0) {		/* stop output filter */
637		write(ofd, "\031\1", 2);
638		while ((pid =
639		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
640			;
641		if (status.w_stopval != WSTOPPED) {
642			(void) close(fi);
643			syslog(LOG_WARNING, "%s: output filter died (%d)",
644				printer, status.w_retcode);
645			return(REPRINT);
646		}
647		stopped++;
648	}
649start:
650	if ((child = dofork(DORETURN)) == 0) {	/* child */
651		dup2(fi, 0);
652		dup2(fo, 1);
653		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
654		if (n >= 0)
655			dup2(n, 2);
656		closelog();
657		for (n = 3; n < NOFILE; n++)
658			(void) close(n);
659		execv(prog, av);
660		openlog("lpd", LOG_PID, LOG_LPR);
661		syslog(LOG_ERR, "cannot execv %s", prog);
662		exit(2);
663	}
664	(void) close(fi);
665	if (child < 0)
666		status.w_retcode = 100;
667	else
668		while ((pid = wait((int *)&status)) > 0 && pid != child)
669			;
670	child = 0;
671	prchild = 0;
672	if (stopped) {		/* restart output filter */
673		if (kill(ofilter, SIGCONT) < 0) {
674			syslog(LOG_ERR, "cannot restart output filter");
675			exit(1);
676		}
677	}
678	tof = 0;
679
680	/* Copy filter output to "lf" logfile */
681	if (fp = fopen(tempfile, "r")) {
682		while (fgets(buf, sizeof(buf), fp))
683			fputs(buf, stderr);
684		fclose(fp);
685	}
686
687	if (!WIFEXITED(status)) {
688		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
689			printer, format, status.w_termsig);
690		return(ERROR);
691	}
692	switch (status.w_retcode) {
693	case 0:
694		tof = 1;
695		return(OK);
696	case 1:
697		return(REPRINT);
698	default:
699		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
700			printer, format, status.w_retcode);
701	case 2:
702		return(ERROR);
703	}
704}
705
706/*
707 * Send the daemon control file (cf) and any data files.
708 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
709 * 0 if all is well.
710 */
711static int
712sendit(file)
713	char *file;
714{
715	register int i, err = OK;
716	char *cp, last[BUFSIZ];
717
718	/*
719	 * open control file
720	 */
721	if ((cfp = fopen(file, "r")) == NULL)
722		return(OK);
723	/*
724	 *      read the control file for work to do
725	 *
726	 *      file format -- first character in the line is a command
727	 *      rest of the line is the argument.
728	 *      commands of interest are:
729	 *
730	 *            a-z -- "file name" name of file to print
731	 *              U -- "unlink" name of file to remove
732	 *                    (after we print it. (Pass 2 only)).
733	 */
734
735	/*
736	 * pass 1
737	 */
738	while (getline(cfp)) {
739	again:
740		if (line[0] == 'S') {
741			cp = line+1;
742			i = 0;
743			while (*cp >= '0' && *cp <= '9')
744				i = i * 10 + (*cp++ - '0');
745			fdev = i;
746			cp++;
747			i = 0;
748			while (*cp >= '0' && *cp <= '9')
749				i = i * 10 + (*cp++ - '0');
750			fino = i;
751			continue;
752		}
753		if (line[0] >= 'a' && line[0] <= 'z') {
754			strcpy(last, line);
755			while (i = getline(cfp))
756				if (strcmp(last, line))
757					break;
758			switch (sendfile('\3', last+1)) {
759			case OK:
760				if (i)
761					goto again;
762				break;
763			case REPRINT:
764				(void) fclose(cfp);
765				return(REPRINT);
766			case ACCESS:
767				sendmail(logname, ACCESS);
768			case ERROR:
769				err = ERROR;
770			}
771			break;
772		}
773	}
774	if (err == OK && sendfile('\2', file) > 0) {
775		(void) fclose(cfp);
776		return(REPRINT);
777	}
778	/*
779	 * pass 2
780	 */
781	fseek(cfp, 0L, 0);
782	while (getline(cfp))
783		if (line[0] == 'U')
784			(void) unlink(line+1);
785	/*
786	 * clean-up in case another control file exists
787	 */
788	(void) fclose(cfp);
789	(void) unlink(file);
790	return(err);
791}
792
793/*
794 * Send a data file to the remote machine and spool it.
795 * Return positive if we should try resending.
796 */
797static int
798sendfile(type, file)
799	int type;
800	char *file;
801{
802	register int f, i, amt;
803	struct stat stb;
804	char buf[BUFSIZ];
805	int sizerr, resp;
806
807	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
808		return(ERROR);
809	/*
810	 * Check to see if data file is a symbolic link. If so, it should
811	 * still point to the same file or someone is trying to print something
812	 * he shouldn't.
813	 */
814	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
815	    (stb.st_dev != fdev || stb.st_ino != fino))
816		return(ACCESS);
817	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
818	amt = strlen(buf);
819	for (i = 0;  ; i++) {
820		if (write(pfd, buf, amt) != amt ||
821		    (resp = response()) < 0 || resp == '\1') {
822			(void) close(f);
823			return(REPRINT);
824		} else if (resp == '\0')
825			break;
826		if (i == 0)
827			pstatus("no space on remote; waiting for queue to drain");
828		if (i == 10)
829			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
830				printer, RM);
831		sleep(5 * 60);
832	}
833	if (i)
834		pstatus("sending to %s", RM);
835	sizerr = 0;
836	for (i = 0; i < stb.st_size; i += BUFSIZ) {
837		amt = BUFSIZ;
838		if (i + amt > stb.st_size)
839			amt = stb.st_size - i;
840		if (sizerr == 0 && read(f, buf, amt) != amt)
841			sizerr = 1;
842		if (write(pfd, buf, amt) != amt) {
843			(void) close(f);
844			return(REPRINT);
845		}
846	}
847
848
849
850
851	(void) close(f);
852	if (sizerr) {
853		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
854		/* tell recvjob to ignore this file */
855		(void) write(pfd, "\1", 1);
856		return(ERROR);
857	}
858	if (write(pfd, "", 1) != 1 || response())
859		return(REPRINT);
860	return(OK);
861}
862
863/*
864 * Check to make sure there have been no errors and that both programs
865 * are in sync with eachother.
866 * Return non-zero if the connection was lost.
867 */
868static char
869response()
870{
871	char resp;
872
873	if (read(pfd, &resp, 1) != 1) {
874		syslog(LOG_INFO, "%s: lost connection", printer);
875		return(-1);
876	}
877	return(resp);
878}
879
880/*
881 * Banner printing stuff
882 */
883static void
884banner(name1, name2)
885	char *name1, *name2;
886{
887	time_t tvec;
888	extern char *ctime();
889
890	time(&tvec);
891	if (!SF && !tof)
892		(void) write(ofd, FF, strlen(FF));
893	if (SB) {	/* short banner only */
894		if (class[0]) {
895			(void) write(ofd, class, strlen(class));
896			(void) write(ofd, ":", 1);
897		}
898		(void) write(ofd, name1, strlen(name1));
899		(void) write(ofd, "  Job: ", 7);
900		(void) write(ofd, name2, strlen(name2));
901		(void) write(ofd, "  Date: ", 8);
902		(void) write(ofd, ctime(&tvec), 24);
903		(void) write(ofd, "\n", 1);
904	} else {	/* normal banner */
905		(void) write(ofd, "\n\n\n", 3);
906		scan_out(ofd, name1, '\0');
907		(void) write(ofd, "\n\n", 2);
908		scan_out(ofd, name2, '\0');
909		if (class[0]) {
910			(void) write(ofd,"\n\n\n",3);
911			scan_out(ofd, class, '\0');
912		}
913		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
914		(void) write(ofd, name2, strlen(name2));
915		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
916		(void) write(ofd, ctime(&tvec), 24);
917		(void) write(ofd, "\n", 1);
918	}
919	if (!SF)
920		(void) write(ofd, FF, strlen(FF));
921	tof = 1;
922}
923
924static char *
925scnline(key, p, c)
926	register int key;
927	register char *p;
928	int c;
929{
930	register scnwidth;
931
932	for (scnwidth = WIDTH; --scnwidth;) {
933		key <<= 1;
934		*p++ = key & 0200 ? c : BACKGND;
935	}
936	return (p);
937}
938
939#define TRC(q)	(((q)-' ')&0177)
940
941static void
942scan_out(scfd, scsp, dlm)
943	int scfd, dlm;
944	char *scsp;
945{
946	register char *strp;
947	register nchrs, j;
948	char outbuf[LINELEN+1], *sp, c, cc;
949	int d, scnhgt;
950	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
951
952	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
953		strp = &outbuf[0];
954		sp = scsp;
955		for (nchrs = 0; ; ) {
956			d = dropit(c = TRC(cc = *sp++));
957			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
958				for (j = WIDTH; --j;)
959					*strp++ = BACKGND;
960			else
961				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
962			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
963				break;
964			*strp++ = BACKGND;
965			*strp++ = BACKGND;
966		}
967		while (*--strp == BACKGND && strp >= outbuf)
968			;
969		strp++;
970		*strp++ = '\n';
971		(void) write(scfd, outbuf, strp-outbuf);
972	}
973}
974
975static int
976dropit(c)
977	int c;
978{
979	switch(c) {
980
981	case TRC('_'):
982	case TRC(';'):
983	case TRC(','):
984	case TRC('g'):
985	case TRC('j'):
986	case TRC('p'):
987	case TRC('q'):
988	case TRC('y'):
989		return (DROP);
990
991	default:
992		return (0);
993	}
994}
995
996/*
997 * sendmail ---
998 *   tell people about job completion
999 */
1000static void
1001sendmail(user, bombed)
1002	char *user;
1003	int bombed;
1004{
1005	register int i;
1006	int p[2], s;
1007	register char *cp;
1008	char buf[100];
1009	struct stat stb;
1010	FILE *fp;
1011
1012	pipe(p);
1013	if ((s = dofork(DORETURN)) == 0) {		/* child */
1014		dup2(p[0], 0);
1015		closelog();
1016		for (i = 3; i < NOFILE; i++)
1017			(void) close(i);
1018		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1019			cp++;
1020	else
1021			cp = _PATH_SENDMAIL;
1022		sprintf(buf, "%s@%s", user, fromhost);
1023		execl(_PATH_SENDMAIL, cp, buf, 0);
1024		openlog("lpd", LOG_PID, LOG_LPR);
1025		syslog(LOG_ERR, "cannot execl %s", _PATH_SENDMAIL);
1026		exit(0);
1027	} else if (s > 0) {				/* parent */
1028		dup2(p[1], 1);
1029		printf("To: %s@%s\n", user, fromhost);
1030		printf("Subject: printer job\n\n");
1031		printf("Your printer job ");
1032		if (*jobname)
1033			printf("(%s) ", jobname);
1034		switch (bombed) {
1035		case OK:
1036			printf("\ncompleted successfully\n");
1037			break;
1038		default:
1039		case FATALERR:
1040			printf("\ncould not be printed\n");
1041			break;
1042		case NOACCT:
1043			printf("\ncould not be printed without an account on %s\n", host);
1044			break;
1045		case FILTERERR:
1046			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1047			    (fp = fopen(tempfile, "r")) == NULL) {
1048				printf("\nwas printed but had some errors\n");
1049				break;
1050			}
1051			printf("\nwas printed but had the following errors:\n");
1052			while ((i = getc(fp)) != EOF)
1053				putchar(i);
1054			(void) fclose(fp);
1055			break;
1056		case ACCESS:
1057			printf("\nwas not printed because it was not linked to the original file\n");
1058		}
1059		fflush(stdout);
1060		(void) close(1);
1061	}
1062	(void) close(p[0]);
1063	(void) close(p[1]);
1064	wait(&s);
1065}
1066
1067/*
1068 * dofork - fork with retries on failure
1069 */
1070static int
1071dofork(action)
1072	int action;
1073{
1074	register int i, pid;
1075	struct passwd *pwd;
1076
1077	for (i = 0; i < 20; i++) {
1078		if ((pid = fork()) < 0) {
1079			sleep((unsigned)(i*i));
1080			continue;
1081		}
1082		/*
1083		 * Child should run as daemon instead of root
1084		 */
1085		if (pid == 0) {
1086			if ((pwd = getpwuid(DU)) == NULL) {
1087				syslog(LOG_ERR, "Can't lookup default uid in password file");
1088				break;
1089			}
1090			initgroups(pwd->pw_name, pwd->pw_gid);
1091			setgid(pwd->pw_gid);
1092			setuid(DU);
1093		}
1094		return(pid);
1095	}
1096	syslog(LOG_ERR, "can't fork");
1097
1098	switch (action) {
1099	case DORETURN:
1100		return (-1);
1101	default:
1102		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1103		/*FALL THRU*/
1104	case DOABORT:
1105		exit(1);
1106	}
1107	/*NOTREACHED*/
1108}
1109
1110/*
1111 * Kill child processes to abort current job.
1112 */
1113static void
1114abortpr(signo)
1115	int signo;
1116{
1117	(void) unlink(tempfile);
1118	kill(0, SIGINT);
1119	if (ofilter > 0)
1120		kill(ofilter, SIGCONT);
1121	while (wait(NULL) > 0)
1122		;
1123	exit(0);
1124}
1125
1126static void
1127init()
1128{
1129	int status;
1130	char *s;
1131
1132	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1133		syslog(LOG_ERR, "can't open printer description file");
1134		exit(1);
1135	} else if (status == -1) {
1136		syslog(LOG_ERR, "unknown printer: %s", printer);
1137		exit(1);
1138	} else if (status == -3)
1139		fatal("potential reference loop detected in printcap file");
1140
1141	if (cgetstr(bp, "lp", &LP) == -1)
1142		LP = _PATH_DEFDEVLP;
1143	if (cgetstr(bp, "rp", &RP) == -1)
1144		RP = DEFLP;
1145	if (cgetstr(bp, "lo", &LO) == -1)
1146		LO = DEFLOCK;
1147	if (cgetstr(bp, "st", &ST) == -1)
1148		ST = DEFSTAT;
1149	if (cgetstr(bp, "lf", &LF) == -1)
1150		LF = _PATH_CONSOLE;
1151	if (cgetstr(bp, "sd", &SD) == -1)
1152		SD = _PATH_DEFSPOOL;
1153	if (cgetnum(bp, "du", &DU) < 0)
1154		DU = DEFUID;
1155	if (cgetstr(bp,"ff", &FF) == -1)
1156		FF = DEFFF;
1157	if (cgetnum(bp, "pw", &PW) < 0)
1158		PW = DEFWIDTH;
1159	sprintf(&width[2], "%d", PW);
1160	if (cgetnum(bp, "pl", &PL) < 0)
1161		PL = DEFLENGTH;
1162	sprintf(&length[2], "%d", PL);
1163	if (cgetnum(bp,"px", &PX) < 0)
1164		PX = 0;
1165	sprintf(&pxwidth[2], "%d", PX);
1166	if (cgetnum(bp, "py", &PY) < 0)
1167		PY = 0;
1168	sprintf(&pxlength[2], "%d", PY);
1169	cgetstr(bp, "rm", &RM);
1170	if (s = checkremote())
1171		syslog(LOG_WARNING, s);
1172
1173	cgetstr(bp, "af", &AF);
1174	cgetstr(bp, "of", &OF);
1175	cgetstr(bp, "if", &IF);
1176	cgetstr(bp, "rf", &RF);
1177	cgetstr(bp, "tf", &TF);
1178	cgetstr(bp, "nf", &NF);
1179	cgetstr(bp, "df", &DF);
1180	cgetstr(bp, "gf", &GF);
1181	cgetstr(bp, "vf", &VF);
1182	cgetstr(bp, "cf", &CF);
1183	cgetstr(bp, "tr", &TR);
1184
1185	RS = (cgetcap(bp, "rs", ':') != NULL);
1186	SF = (cgetcap(bp, "sf", ':') != NULL);
1187	SH = (cgetcap(bp, "sh", ':') != NULL);
1188	SB = (cgetcap(bp, "sb", ':') != NULL);
1189	HL = (cgetcap(bp, "hl", ':') != NULL);
1190	RW = (cgetcap(bp, "rw", ':') != NULL);
1191
1192	cgetnum(bp, "br", &BR);
1193	if (cgetnum(bp, "fc", &FC) < 0)
1194		FC = 0;
1195	if (cgetnum(bp, "fs", &FS) < 0)
1196		FS = 0;
1197	if (cgetnum(bp, "xc", &XC) < 0)
1198		XC = 0;
1199	if (cgetnum(bp, "xs", &XS) < 0)
1200		XS = 0;
1201
1202	tof = (cgetcap(bp, "fo", ':') == NULL);
1203}
1204
1205/*
1206 * Acquire line printer or remote connection.
1207 */
1208static void
1209openpr()
1210{
1211	register int i, n;
1212	int resp;
1213
1214	if (!sendtorem && *LP) {
1215		for (i = 1; ; i = i < 32 ? i << 1 : i) {
1216			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1217			if (pfd >= 0)
1218				break;
1219			if (errno == ENOENT) {
1220				syslog(LOG_ERR, "%s: %m", LP);
1221				exit(1);
1222			}
1223			if (i == 1)
1224				pstatus("waiting for %s to become ready (offline ?)", printer);
1225			sleep(i);
1226		}
1227		if (isatty(pfd))
1228			setty();
1229		pstatus("%s is ready and printing", printer);
1230	} else if (RM != NULL) {
1231		for (i = 1; ; i = i < 256 ? i << 1 : i) {
1232			resp = -1;
1233			pfd = getport(RM);
1234			if (pfd >= 0) {
1235				(void) sprintf(line, "\2%s\n", RP);
1236				n = strlen(line);
1237				if (write(pfd, line, n) == n &&
1238				    (resp = response()) == '\0')
1239					break;
1240				(void) close(pfd);
1241			}
1242			if (i == 1) {
1243				if (resp < 0)
1244					pstatus("waiting for %s to come up", RM);
1245				else {
1246					pstatus("waiting for queue to be enabled on %s", RM);
1247					i = 256;
1248				}
1249			}
1250			sleep(i);
1251		}
1252		pstatus("sending to %s", RM);
1253		remote = 1;
1254	} else {
1255		syslog(LOG_ERR, "%s: no line printer device or host name",
1256			printer);
1257		exit(1);
1258	}
1259	/*
1260	 * Start up an output filter, if needed.
1261	 */
1262	if (!remote && OF) {
1263		int p[2];
1264		char *cp;
1265
1266		pipe(p);
1267		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1268			dup2(p[0], 0);		/* pipe is std in */
1269			dup2(pfd, 1);		/* printer is std out */
1270			closelog();
1271			for (i = 3; i < NOFILE; i++)
1272				(void) close(i);
1273			if ((cp = rindex(OF, '/')) == NULL)
1274				cp = OF;
1275			else
1276				cp++;
1277			execl(OF, cp, width, length, 0);
1278			openlog("lpd", LOG_PID, LOG_LPR);
1279			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1280			exit(1);
1281		}
1282		(void) close(p[0]);		/* close input side */
1283		ofd = p[1];			/* use pipe for output */
1284	} else {
1285		ofd = pfd;
1286		ofilter = 0;
1287	}
1288}
1289
1290struct bauds {
1291	int	baud;
1292	int	speed;
1293} bauds[] = {
1294	50,	B50,
1295	75,	B75,
1296	110,	B110,
1297	134,	B134,
1298	150,	B150,
1299	200,	B200,
1300	300,	B300,
1301	600,	B600,
1302	1200,	B1200,
1303	1800,	B1800,
1304	2400,	B2400,
1305	4800,	B4800,
1306	9600,	B9600,
1307	19200,	EXTA,
1308	38400,	EXTB,
1309	57600,	B57600,
1310	115200,	B115200,
1311	0,	0
1312};
1313
1314/*
1315 * setup tty lines.
1316 */
1317static void
1318setty()
1319{
1320	struct sgttyb ttybuf;
1321	register struct bauds *bp;
1322
1323	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1324		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1325		exit(1);
1326	}
1327	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1328		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
1329		exit(1);
1330	}
1331	if (BR > 0) {
1332		for (bp = bauds; bp->baud; bp++)
1333			if (BR == bp->baud)
1334				break;
1335		if (!bp->baud) {
1336			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1337			exit(1);
1338		}
1339		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1340	}
1341	ttybuf.sg_flags &= ~FC;
1342	ttybuf.sg_flags |= FS;
1343	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1344		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
1345		exit(1);
1346	}
1347	if (XC) {
1348		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1349			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
1350			exit(1);
1351		}
1352	}
1353	if (XS) {
1354		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1355			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
1356			exit(1);
1357		}
1358	}
1359}
1360
1361#if __STDC__
1362#include <stdarg.h>
1363#else
1364#include <varargs.h>
1365#endif
1366
1367void
1368#if __STDC__
1369pstatus(const char *msg, ...)
1370#else
1371pstatus(msg, va_alist)
1372	char *msg;
1373        va_dcl
1374#endif
1375{
1376	register int fd;
1377	char buf[BUFSIZ];
1378	va_list ap;
1379#if __STDC__
1380	va_start(ap, msg);
1381#else
1382	va_start(ap);
1383#endif
1384
1385	umask(0);
1386	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1387	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1388		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1389		exit(1);
1390	}
1391	ftruncate(fd, 0);
1392	(void)vsnprintf(buf, sizeof(buf), msg, ap);
1393	va_end(ap);
1394	strcat(buf, "\n");
1395	(void) write(fd, buf, strlen(buf));
1396	(void) close(fd);
1397}
1398