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