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