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