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