1/*	$NetBSD: printjob.c,v 1.57 2019/02/03 03:19:30 mrg 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.57 2019/02/03 03:19:30 mrg 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		/* FALLTHROUGH */
609	case 'f':	/* print plain text file */
610		prog = IF;
611		av[1] = width;
612		av[2] = length;
613		av[3] = indent;
614		n = 4;
615		break;
616	case 'o':	/* print a postscript file */
617		if (PF == NULL) {
618			/* if PF is not set, handle it like an 'l' */
619			prog = IF;
620			av[1] = "-c";
621			av[2] = width;
622			av[3] = length;
623			av[4] = indent;
624			n = 5;
625			break;
626		} else {
627			prog = PF;
628			av[1] = pxwidth;
629			av[2] = pxlength;
630			n = 3;
631			break;
632		}
633	case 'l':	/* like 'f' but pass control characters */
634		prog = IF;
635		av[1] = "-c";
636		av[2] = width;
637		av[3] = length;
638		av[4] = indent;
639		n = 5;
640		break;
641	case 'r':	/* print a fortran text file */
642		prog = RF;
643		av[1] = width;
644		av[2] = length;
645		n = 3;
646		break;
647	case 't':	/* print troff output */
648	case 'n':	/* print ditroff output */
649	case 'd':	/* print tex output */
650		(void)unlink(".railmag");
651		if ((fo = creat(".railmag", FILMOD)) < 0) {
652			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
653			(void)unlink(".railmag");
654		} else {
655			for (n = 0; n < 4; n++) {
656				if (fonts[n][0] != '/')
657					(void)write(fo, _PATH_VFONT,
658					    sizeof(_PATH_VFONT) - 1);
659				(void)write(fo, fonts[n], strlen(fonts[n]));
660				(void)write(fo, "\n", 1);
661			}
662			(void)close(fo);
663		}
664		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
665		av[1] = pxwidth;
666		av[2] = pxlength;
667		n = 3;
668		break;
669	case 'c':	/* print cifplot output */
670		prog = CF;
671		av[1] = pxwidth;
672		av[2] = pxlength;
673		n = 3;
674		break;
675	case 'g':	/* print plot(1G) output */
676		prog = GF;
677		av[1] = pxwidth;
678		av[2] = pxlength;
679		n = 3;
680		break;
681	case 'v':	/* print raster output */
682		prog = VF;
683		av[1] = pxwidth;
684		av[2] = pxlength;
685		n = 3;
686		break;
687	default:
688		(void)close(fi);
689		syslog(LOG_ERR, "%s: illegal format character '%c'",
690			printer, format);
691		return(ERROR);
692	}
693	if (prog == NULL) {
694		(void)close(fi);
695		syslog(LOG_ERR,
696		    "%s: no filter found in printcap for format character '%c'",
697		    printer, format);
698		return (ERROR);
699	}
700	if ((av[0] = strrchr(prog, '/')) != NULL)
701		av[0]++;
702	else
703		av[0] = prog;
704	av[n++] = "-n";
705	av[n++] = logname;
706	if (*jobname != '\0' && strcmp(jobname, " ") != 0) {
707		av[n++] = "-j";
708		av[n++] = jobname;
709	}
710	av[n++] = "-h";
711	av[n++] = fromhost;
712	av[n++] = AF;
713	av[n] = 0;
714	fo = pfd;
715	if (ofilter > 0) {		/* stop output filter */
716		write(ofd, "\031\1", 2);
717		while ((child_pid =
718		    wait3(&status, WUNTRACED, 0)) > 0 && child_pid != ofilter)
719			;
720		if (WIFSTOPPED(status) == 0) {
721			(void)close(fi);
722			syslog(LOG_WARNING,
723			    "%s: output filter died (retcode=%d termsig=%d)",
724				printer, WEXITSTATUS(status), WTERMSIG(status));
725			return(REPRINT);
726		}
727		stopped++;
728	}
729start:
730	if ((child = dofork(DORETURN)) == 0) {	/* child */
731		dup2(fi, 0);
732		dup2(fo, 1);
733		unlink(tempfile);
734		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0664);
735		if (n >= 0)
736			dup2(n, 2);
737		closelog();
738		nofile = sysconf(_SC_OPEN_MAX);
739		for (n = 3; n < nofile; n++)
740			(void)close(n);
741		execv(prog, __UNCONST(av));
742		syslog(LOG_ERR, "cannot execv %s", prog);
743		exit(2);
744	}
745	if (child < 0) {
746		child = 0;
747		prchild = 0;
748		tof = 0;
749		syslog(LOG_ERR, "cannot start child process: %m");
750		return (ERROR);
751	}
752	(void)close(fi);
753	while ((child_pid = wait(&status)) > 0 && child_pid != child)
754		;
755	child = 0;
756	prchild = 0;
757	if (stopped) {		/* restart output filter */
758		if (kill(ofilter, SIGCONT) < 0) {
759			syslog(LOG_ERR, "cannot restart output filter");
760			exit(1);
761		}
762	}
763	tof = 0;
764
765	/* Copy filter output to "lf" logfile */
766	if ((fp = fopen(tempfile, "r")) != NULL) {
767		while (fgets(buf, sizeof(buf), fp))
768			fputs(buf, stderr);
769		fclose(fp);
770	}
771
772	if (!WIFEXITED(status)) {
773		syslog(LOG_WARNING,
774		    "%s: Daemon filter '%c' terminated (pid=%d) (termsig=%d)",
775			printer, format, (int)child_pid, WTERMSIG(status));
776		return(ERROR);
777	}
778	switch (WEXITSTATUS(status)) {
779	case 0:
780		tof = 1;
781		return(OK);
782	case 1:
783		return(REPRINT);
784	case 2:
785		return(ERROR);
786	default:
787		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
788			printer, format, WEXITSTATUS(status));
789		return(FILTERERR);
790	}
791}
792
793/*
794 * Send the daemon control file (cf) and any data files.
795 * Return -1 if a non-recoverable error occurred, 1 if a recoverable error and
796 * 0 if all is well.
797 */
798static int
799sendit(char *file)
800{
801	int i, err = OK;
802	char *cp, last[BUFSIZ];
803
804	/*
805	 * open control file
806	 */
807	if ((cfp = fopen(file, "r")) == NULL)
808		return(OK);
809	/*
810	 *      read the control file for work to do
811	 *
812	 *      file format -- first character in the line is a command
813	 *      rest of the line is the argument.
814	 *      commands of interest are:
815	 *
816	 *            a-z -- "file name" name of file to print
817	 *              U -- "unlink" name of file to remove
818	 *                    (after we print it. (Pass 2 only)).
819	 */
820
821	/*
822	 * pass 1
823	 */
824	while (get_line(cfp)) {
825	again:
826		if (line[0] == 'S') {
827			cp = line+1;
828			i = 0;
829			while (*cp >= '0' && *cp <= '9')
830				i = i * 10 + (*cp++ - '0');
831			fdev = i;
832			cp++;
833			i = 0;
834			while (*cp >= '0' && *cp <= '9')
835				i = i * 10 + (*cp++ - '0');
836			fino = i;
837			continue;
838		}
839		if (line[0] >= 'a' && line[0] <= 'z') {
840			strlcpy(last, line, sizeof(last));
841			while ((i = get_line(cfp)) != 0)
842				if (strcmp(last, line))
843					break;
844			switch (sendfile('\3', last+1)) {
845			case OK:
846				if (i)
847					goto again;
848				break;
849			case REPRINT:
850				(void)fclose(cfp);
851				return(REPRINT);
852			case ACCESS:
853				sendmail(logname, ACCESS);
854				/* FALLTHROUGH */
855			case ERROR:
856				err = ERROR;
857			}
858			break;
859		}
860	}
861	if (err == OK && sendfile('\2', file) > 0) {
862		(void)fclose(cfp);
863		return(REPRINT);
864	}
865	/*
866	 * pass 2
867	 */
868	fseek(cfp, 0L, 0);
869	while (get_line(cfp))
870		if (line[0] == 'U' && strchr(line+1, '/') == 0)
871			(void)unlink(line+1);
872	/*
873	 * clean-up in case another control file exists
874	 */
875	(void)fclose(cfp);
876	(void)unlink(file);
877	return(err);
878}
879
880/*
881 * Send a data file to the remote machine and spool it.
882 * Return positive if we should try resending.
883 */
884static int
885sendfile(int type, char *file)
886{
887	int f, i, amt;
888	struct stat stb;
889	char buf[BUFSIZ];
890	int sizerr, resp;
891	extern int rflag;
892	char *save_file;
893
894	save_file = file;
895	if (type == '\3' && rflag && (OF || IF)) {
896		int	save_pfd = pfd;
897
898		(void)unlink(tempremote);
899		pfd = open(tempremote, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0664);
900		if (pfd == -1) {
901			pfd = save_pfd;
902			return ERROR;
903		}
904		setup_ofilter(1);
905		switch (i = print('f', file)) {
906		case ERROR:
907		case REPRINT:
908		case FILTERERR:
909		case ACCESS:
910			return(i);
911		}
912		close_ofilter();
913		pfd = save_pfd;
914		file = tempremote;
915	}
916
917	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
918		return(ERROR);
919	/*
920	 * Check to see if data file is a symbolic link. If so, it should
921	 * still point to the same file or someone is trying to print something
922	 * he shouldn't.
923	 */
924	if (S_ISLNK(stb.st_mode) && fstat(f, &stb) == 0 &&
925	    (stb.st_dev != fdev || stb.st_ino != fino))
926		return(ACCESS);
927
928	amt = snprintf(buf, sizeof(buf), "%c%lld %s\n", type,
929	    (long long)stb.st_size, save_file);
930	for (i = 0; ; i++) {
931		if (write(pfd, buf, amt) != amt ||
932		    (resp = response()) < 0 || resp == '\1') {
933			(void)close(f);
934			return(REPRINT);
935		} else if (resp == '\0')
936			break;
937		if (i == 0)
938			pstatus("no space on remote; waiting for queue to drain");
939		if (i == 10)
940			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
941				printer, RM);
942		sleep(5 * 60);
943	}
944	if (i)
945		pstatus("sending to %s", RM);
946	sizerr = 0;
947	for (i = 0; i < stb.st_size; i += BUFSIZ) {
948		struct sigaction osa, nsa;
949
950		amt = BUFSIZ;
951		if (i + amt > stb.st_size)
952			amt = stb.st_size - i;
953		if (sizerr == 0 && read(f, buf, amt) != amt)
954			sizerr = 1;
955		nsa.sa_handler = alarmer;
956		sigemptyset(&nsa.sa_mask);
957		sigaddset(&nsa.sa_mask, SIGALRM);
958		nsa.sa_flags = 0;
959		(void)sigaction(SIGALRM, &nsa, &osa);
960		alarm(wait_time);
961		if (write(pfd, buf, amt) != amt) {
962			alarm(0);
963			(void)sigaction(SIGALRM, &osa, NULL);
964			(void)close(f);
965			return(REPRINT);
966		}
967		alarm(0);
968		(void)sigaction(SIGALRM, &osa, NULL);
969	}
970
971	(void)close(f);
972	if (sizerr) {
973		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
974		/* tell recvjob to ignore this file */
975		(void)write(pfd, "\1", 1);
976		return(ERROR);
977	}
978	if (write(pfd, "", 1) != 1 || response())
979		return(REPRINT);
980	return(OK);
981}
982
983/*
984 * Check to make sure there have been no errors and that both programs
985 * are in sync with eachother.
986 * Return non-zero if the connection was lost.
987 */
988static char
989response(void)
990{
991	struct sigaction osa, nsa;
992	char resp;
993
994	nsa.sa_handler = alarmer;
995	sigemptyset(&nsa.sa_mask);
996	sigaddset(&nsa.sa_mask, SIGALRM);
997	nsa.sa_flags = 0;
998	(void)sigaction(SIGALRM, &nsa, &osa);
999	alarm(wait_time);
1000	if (read(pfd, &resp, 1) != 1) {
1001		syslog(LOG_INFO, "%s: lost connection", printer);
1002		resp = -1;
1003	}
1004	alarm(0);
1005	(void)sigaction(SIGALRM, &osa, NULL);
1006	return (resp);
1007}
1008
1009/*
1010 * Banner printing stuff
1011 */
1012static void
1013banner(char *name1, char *name2)
1014{
1015	time_t tvec;
1016
1017	time(&tvec);
1018	if (!SF && !tof)
1019		(void)write(ofd, FF, strlen(FF));
1020	if (SB) {	/* short banner only */
1021		if (class[0]) {
1022			(void)write(ofd, class, strlen(class));
1023			(void)write(ofd, ":", 1);
1024		}
1025		(void)write(ofd, name1, strlen(name1));
1026		(void)write(ofd, "  Job: ", 7);
1027		(void)write(ofd, name2, strlen(name2));
1028		(void)write(ofd, "  Date: ", 8);
1029		(void)write(ofd, ctime(&tvec), 24);
1030		(void)write(ofd, "\n", 1);
1031	} else {	/* normal banner */
1032		(void)write(ofd, "\n\n\n", 3);
1033		scan_out(ofd, name1, '\0');
1034		(void)write(ofd, "\n\n", 2);
1035		scan_out(ofd, name2, '\0');
1036		if (class[0]) {
1037			(void)write(ofd,"\n\n\n",3);
1038			scan_out(ofd, class, '\0');
1039		}
1040		(void)write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
1041		(void)write(ofd, name2, strlen(name2));
1042		(void)write(ofd, "\n\t\t\t\t\tDate: ", 12);
1043		(void)write(ofd, ctime(&tvec), 24);
1044		(void)write(ofd, "\n", 1);
1045	}
1046	if (!SF)
1047		(void)write(ofd, FF, strlen(FF));
1048	tof = 1;
1049}
1050
1051static char *
1052scnline(int key, char *p, int c)
1053{
1054	int scnwidth;
1055
1056	for (scnwidth = WIDTH; --scnwidth;) {
1057		key <<= 1;
1058		*p++ = key & 0200 ? c : BACKGND;
1059	}
1060	return (p);
1061}
1062
1063#define TRC(q)	(((q)-' ')&0177)
1064
1065static void
1066scan_out(int scfd, char *scsp, int dlm)
1067{
1068	char *strp;
1069	int nchrs, j;
1070	char outbuf[LINELEN+1], *sp, c, cc;
1071	int d, scnhgt;
1072	extern const char scnkey[][HEIGHT];	/* in lpdchar.c */
1073
1074	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
1075		strp = &outbuf[0];
1076		sp = scsp;
1077		for (nchrs = 0; ; ) {
1078			d = dropit(c = TRC(cc = *sp++));
1079			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
1080				for (j = WIDTH; --j;)
1081					*strp++ = BACKGND;
1082			else
1083				strp = scnline(scnkey[(int)c][scnhgt-1-d],
1084				    strp, cc);
1085			if (*sp == dlm || *sp == '\0' ||
1086			    nchrs++ >= PW/(WIDTH+1)-1)
1087				break;
1088			*strp++ = BACKGND;
1089			*strp++ = BACKGND;
1090		}
1091		while (*--strp == BACKGND && strp >= outbuf)
1092			;
1093		strp++;
1094		*strp++ = '\n';
1095		(void)write(scfd, outbuf, strp-outbuf);
1096	}
1097}
1098
1099static int
1100dropit(int c)
1101{
1102	switch(c) {
1103
1104	case TRC('_'):
1105	case TRC(';'):
1106	case TRC(','):
1107	case TRC('g'):
1108	case TRC('j'):
1109	case TRC('p'):
1110	case TRC('q'):
1111	case TRC('y'):
1112		return (DROP);
1113
1114	default:
1115		return (0);
1116	}
1117}
1118
1119/*
1120 * sendmail ---
1121 *   tell people about job completion
1122 */
1123static void
1124sendmail(char *user, int bombed)
1125{
1126	int i, p[2], s, nofile;
1127	const char *cp = NULL; /* XXX gcc */
1128	struct stat stb;
1129	FILE *fp;
1130
1131	if (user[0] == '-' || user[0] == '/' || !isprint((unsigned char)user[0]))
1132		return;
1133	pipe(p);
1134	if ((s = dofork(DORETURN)) == 0) {		/* child */
1135		dup2(p[0], 0);
1136		closelog();
1137		nofile = sysconf(_SC_OPEN_MAX);
1138		for (i = 3; i < nofile; i++)
1139			(void)close(i);
1140		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
1141			cp++;
1142		else
1143			cp = _PATH_SENDMAIL;
1144		execl(_PATH_SENDMAIL, cp, "-t", NULL);
1145		_exit(0);
1146	} else if (s > 0) {				/* parent */
1147		dup2(p[1], 1);
1148		printf("To: %s@%s\n", user, fromhost);
1149		printf("Subject: %s printer job \"%s\"\n", printer,
1150			*jobname ? jobname : "<unknown>");
1151		printf("Reply-To: root@%s\n\n", host);
1152		printf("Your printer job ");
1153		if (*jobname)
1154			printf("(%s) ", jobname);
1155		switch (bombed) {
1156		case OK:
1157			printf("\ncompleted successfully\n");
1158			cp = "OK";
1159			break;
1160		default:
1161		case FATALERR:
1162			printf("\ncould not be printed\n");
1163			cp = "FATALERR";
1164			break;
1165		case NOACCT:
1166			printf("\ncould not be printed without an account on %s\n", host);
1167			cp = "NOACCT";
1168			break;
1169		case FILTERERR:
1170			cp = "FILTERERR";
1171			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1172			    (fp = fopen(tempfile, "r")) == NULL) {
1173				printf("\nhad some errors and may not have printed\n");
1174				break;
1175			}
1176			printf("\nhad the following errors and may not have printed:\n");
1177			while ((i = getc(fp)) != EOF)
1178				putchar(i);
1179			(void)fclose(fp);
1180			break;
1181		case ACCESS:
1182			printf("\nwas not printed because it was not linked to the original file\n");
1183			cp = "ACCESS";
1184		}
1185		fflush(stdout);
1186		(void)close(1);
1187	} else {
1188		syslog(LOG_ERR, "fork for sendmail failed: %m");
1189	}
1190	(void)close(p[0]);
1191	(void)close(p[1]);
1192	if (s > 0) {
1193		wait(NULL);
1194		syslog(LOG_INFO, "mail sent to user %s about job %s on "
1195		    "printer %s (%s)", user, *jobname ? jobname : "<unknown>",
1196		    printer, cp);
1197	}
1198}
1199
1200/*
1201 * dofork - fork with retries on failure
1202 */
1203static int
1204dofork(int action)
1205{
1206	int i, child_pid;
1207	struct passwd *pw;
1208
1209	for (i = 0; i < 20; i++) {
1210		if ((child_pid = fork()) < 0) {
1211			sleep((unsigned)(i*i));
1212			continue;
1213		}
1214		/*
1215		 * Child should run as daemon instead of root
1216		 */
1217		if (child_pid == 0) {
1218			pw = getpwuid(DU);
1219			if (pw == 0) {
1220				syslog(LOG_ERR, "uid %ld not in password file",
1221				    DU);
1222				break;
1223			}
1224			initgroups(pw->pw_name, pw->pw_gid);
1225			setgid(pw->pw_gid);
1226			setuid(DU);
1227			signal(SIGCHLD, SIG_DFL);
1228		}
1229		return (child_pid);
1230	}
1231	syslog(LOG_ERR, "can't fork");
1232
1233	switch (action) {
1234	case DORETURN:
1235		return (-1);
1236	default:
1237		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1238		/*FALL THRU*/
1239	case DOABORT:
1240		exit(1);
1241	}
1242	/*NOTREACHED*/
1243}
1244
1245/*
1246 * Kill child processes to abort current job.
1247 */
1248static void
1249abortpr(int signo)
1250{
1251	(void)unlink(tempfile);
1252	(void)unlink(tempremote);
1253	kill(0, SIGINT);
1254	if (ofilter > 0)
1255		kill(ofilter, SIGCONT);
1256	while (wait(NULL) > 0)
1257		;
1258	exit(0);
1259}
1260
1261static void
1262init(void)
1263{
1264	char *s;
1265
1266	getprintcap(printer);
1267
1268	FF = cgetstr(bp, "ff", &s) == -1 ? DEFFF : s;
1269
1270	if (cgetnum(bp, "du", &DU) < 0)
1271		DU = DEFUID;
1272	if (cgetnum(bp, "pw", &PW) < 0)
1273		PW = DEFWIDTH;
1274	(void)snprintf(&width[2], sizeof(width) - 2, "%ld", PW);
1275	if (cgetnum(bp, "pl", &PL) < 0)
1276		PL = DEFLENGTH;
1277	(void)snprintf(&length[2], sizeof(length) - 2, "%ld", PL);
1278	if (cgetnum(bp,"px", &PX) < 0)
1279		PX = 0;
1280	(void)snprintf(&pxwidth[2], sizeof(pxwidth) - 2, "%ld", PX);
1281	if (cgetnum(bp, "py", &PY) < 0)
1282		PY = 0;
1283	(void)snprintf(&pxlength[2], sizeof(pxlength) - 2, "%ld", PY);
1284
1285	AF = cgetstr(bp, "af", &s) == -1 ? NULL : s;
1286	OF = cgetstr(bp, "of", &s) == -1 ? NULL : s;
1287	IF = cgetstr(bp, "if", &s) == -1 ? NULL : s;
1288	RF = cgetstr(bp, "rf", &s) == -1 ? NULL : s;
1289	TF = cgetstr(bp, "tf", &s) == -1 ? NULL : s;
1290	NF = cgetstr(bp, "nf", &s) == -1 ? NULL : s;
1291	DF = cgetstr(bp, "df", &s) == -1 ? NULL : s;
1292	GF = cgetstr(bp, "gf", &s) == -1 ? NULL : s;
1293	VF = cgetstr(bp, "vf", &s) == -1 ? NULL : s;
1294	CF = cgetstr(bp, "cf", &s) == -1 ? NULL : s;
1295	PF = cgetstr(bp, "pf", &s) == -1 ? NULL : s;
1296	TR = cgetstr(bp, "tr", &s) == -1 ? NULL : s;
1297
1298	RS = (cgetcap(bp, "rs", ':') != NULL);
1299	SF = (cgetcap(bp, "sf", ':') != NULL);
1300	SH = (cgetcap(bp, "sh", ':') != NULL);
1301	SB = (cgetcap(bp, "sb", ':') != NULL);
1302	HL = (cgetcap(bp, "hl", ':') != NULL);
1303	RW = (cgetcap(bp, "rw", ':') != NULL);
1304
1305	cgetnum(bp, "br", &BR);
1306	if (cgetnum(bp, "fc", &FC) < 0)
1307		FC = 0;
1308	if (cgetnum(bp, "fs", &FS) < 0)
1309		FS = 0;
1310	if (cgetnum(bp, "xc", &XC) < 0)
1311		XC = 0;
1312	if (cgetnum(bp, "xs", &XS) < 0)
1313		XS = 0;
1314	MS = cgetstr(bp, "ms", &s) == -1 ? NULL : s;
1315
1316	tof = (cgetcap(bp, "fo", ':') == NULL);
1317}
1318
1319/*
1320 * Setup output filter - called once for local printer, or (if -r given to lpd)
1321 * once per file for remote printers
1322 */
1323static void
1324setup_ofilter(int check_rflag)
1325{
1326	extern int rflag;
1327
1328	if (OF && (!remote || (check_rflag && rflag))) {
1329		int p[2];
1330		int i, nofile;
1331		const char *cp;
1332
1333		pipe(p);
1334		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1335			dup2(p[0], 0);		/* pipe is std in */
1336			dup2(pfd, 1);		/* printer is std out */
1337			closelog();
1338			nofile = sysconf(_SC_OPEN_MAX);
1339			for (i = 3; i < nofile; i++)
1340				(void)close(i);
1341			if ((cp = strrchr(OF, '/')) == NULL)
1342				cp = OF;
1343			else
1344				cp++;
1345			execl(OF, cp, width, length, NULL);
1346			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1347			exit(1);
1348		}
1349		(void)close(p[0]);		/* close input side */
1350		ofd = p[1];			/* use pipe for output */
1351	} else {
1352		ofd = pfd;
1353		ofilter = 0;
1354	}
1355}
1356
1357/*
1358 * Close the output filter and reset ofd back to the main pfd descriptor
1359 */
1360static void
1361close_ofilter(void)
1362{
1363	int i;
1364
1365	if (ofilter) {
1366		kill(ofilter, SIGCONT);	/* to be sure */
1367		(void)close(ofd);
1368		ofd = pfd;
1369		while ((i = wait(NULL)) > 0 && i != ofilter)
1370			;
1371		ofilter = 0;
1372	}
1373}
1374
1375/*
1376 * Acquire line printer or remote connection.
1377 */
1378static void
1379openpr(void)
1380{
1381	if (!remote && *LP) {
1382		if (strchr(LP, '@') != NULL)
1383			opennet();
1384		else
1385			opentty();
1386	} else if (remote) {
1387		openrem();
1388	} else {
1389		syslog(LOG_ERR, "%s: no line printer device or host name",
1390			printer);
1391		exit(1);
1392	}
1393
1394	/*
1395	 * Start up an output filter, if needed.
1396	 */
1397	setup_ofilter(0);
1398}
1399
1400/*
1401 * Printer connected directly to the network
1402 * or to a terminal server on the net
1403 */
1404static void
1405opennet(void)
1406{
1407	int i;
1408	int resp;
1409
1410	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1411		resp = -1;
1412		pfd = getport(LP);
1413		if (pfd < 0 && errno == ECONNREFUSED)
1414			resp = 1;
1415		else if (pfd >= 0) {
1416			/*
1417			 * need to delay a bit for rs232 lines
1418			 * to stabilize in case printer is
1419			 * connected via a terminal server
1420			 */
1421			delay(500);
1422			break;
1423		}
1424		if (i == 1) {
1425		   if (resp < 0)
1426			pstatus("waiting for %s to come up", LP);
1427		   else
1428			pstatus("waiting for access to printer on %s", LP);
1429		}
1430		sleep(i);
1431	}
1432	pstatus("sending to %s", LP);
1433}
1434
1435/*
1436 * Printer is connected to an RS232 port on this host
1437 */
1438static void
1439opentty(void)
1440{
1441	int i;
1442
1443	for (i = 1; ; i = i < 32 ? i << 1 : i) {
1444		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1445		if (pfd >= 0) {
1446			delay(500);
1447			break;
1448		}
1449		if (errno == ENOENT) {
1450			syslog(LOG_ERR, "%s: %m", LP);
1451			exit(1);
1452		}
1453		if (i == 1)
1454			pstatus("waiting for %s to become ready (offline ?)",
1455				printer);
1456		sleep(i);
1457	}
1458	if (isatty(pfd))
1459		setty();
1460	pstatus("%s is ready and printing", printer);
1461}
1462
1463/*
1464 * Printer is on a remote host
1465 */
1466static void
1467openrem(void)
1468{
1469	int i, n;
1470	int resp;
1471
1472	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1473		resp = -1;
1474		pfd = getport(RM);
1475		if (pfd >= 0) {
1476			n = snprintf(line, sizeof(line), "\2%s\n", RP);
1477			if (write(pfd, line, n) == n &&
1478			    (resp = response()) == '\0')
1479				break;
1480			(void) close(pfd);
1481		}
1482		if (i == 1) {
1483			if (resp < 0)
1484				pstatus("waiting for %s to come up", RM);
1485			else {
1486				pstatus("waiting for queue to be enabled on %s",
1487					RM);
1488				i = 256;
1489			}
1490		}
1491		sleep(i);
1492	}
1493	pstatus("sending to %s", RM);
1494}
1495
1496static void
1497alarmer(int s)
1498{
1499	/* nothing */
1500}
1501
1502#if !defined(__NetBSD__)
1503struct bauds {
1504	int	baud;
1505	int	speed;
1506} bauds[] = {
1507	50,	B50,
1508	75,	B75,
1509	110,	B110,
1510	134,	B134,
1511	150,	B150,
1512	200,	B200,
1513	300,	B300,
1514	600,	B600,
1515	1200,	B1200,
1516	1800,	B1800,
1517	2400,	B2400,
1518	4800,	B4800,
1519	9600,	B9600,
1520	19200,	B19200,
1521	38400,	B38400,
1522	57600,	B57600,
1523	115200,	B115200,
1524	0,	0
1525};
1526#endif
1527
1528/*
1529 * setup tty lines.
1530 */
1531static void
1532setty(void)
1533{
1534	struct info i;
1535	char **argv, **ap, *p, *val;
1536
1537	i.fd = pfd;
1538	i.set = i.wset = 0;
1539	if (ioctl(i.fd, TIOCEXCL, (char *)0) < 0) {
1540		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1541		exit(1);
1542	}
1543	if (tcgetattr(i.fd, &i.t) < 0) {
1544		syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
1545		exit(1);
1546	}
1547	if (BR > 0) {
1548#if !defined(__NetBSD__)
1549		struct bauds *bp;
1550		for (bp = bauds; bp->baud; bp++)
1551			if (BR == bp->baud)
1552				break;
1553		if (!bp->baud) {
1554			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1555			exit(1);
1556		}
1557		cfsetspeed(&i.t, bp->speed);
1558#else
1559		cfsetspeed(&i.t, BR);
1560#endif
1561		i.set = 1;
1562	}
1563	if (MS) {
1564		if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) {
1565			syslog(LOG_ERR, "%s: ioctl(TIOCGETD): %m", printer);
1566			exit(1);
1567		}
1568		if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0)
1569			syslog(LOG_INFO, "%s: ioctl(TIOCGWINSZ): %m",
1570			       printer);
1571
1572		argv = (char **)calloc(256, sizeof(char *));
1573		if (argv == NULL) {
1574			syslog(LOG_ERR, "%s: calloc: %m", printer);
1575			exit(1);
1576		}
1577		p = strdup(MS);
1578		ap = argv;
1579		while ((val = strsep(&p, " \t,")) != NULL) {
1580			*ap++ = strdup(val);
1581		}
1582
1583		for (; *argv; ++argv) {
1584			if (ksearch(&argv, &i))
1585				continue;
1586			if (msearch(&argv, &i))
1587				continue;
1588			syslog(LOG_INFO, "%s: unknown stty flag: %s",
1589			       printer, *argv);
1590		}
1591	} else {
1592		if (FC) {
1593			sttyclearflags(&i.t, FC);
1594			i.set = 1;
1595		}
1596		if (FS) {
1597			sttysetflags(&i.t, FS);
1598			i.set = 1;
1599		}
1600		if (XC) {
1601			sttyclearlflags(&i.t, XC);
1602			i.set = 1;
1603		}
1604		if (XS) {
1605			sttysetlflags(&i.t, XS);
1606			i.set = 1;
1607		}
1608	}
1609
1610	if (i.set && tcsetattr(i.fd, TCSANOW, &i.t) < 0) {
1611		syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
1612		exit(1);
1613	}
1614	if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0)
1615		syslog(LOG_INFO, "%s: ioctl(TIOCSWINSZ): %m", printer);
1616	return;
1617}
1618
1619#include <stdarg.h>
1620
1621static void
1622pstatus(const char *msg, ...)
1623{
1624	int fd;
1625	char *buf;
1626	va_list ap;
1627	struct iovec iov[2];
1628
1629	umask(0);
1630	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1631	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1632		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1633		exit(1);
1634	}
1635	ftruncate(fd, 0);
1636	va_start(ap, msg);
1637	(void)vasprintf(&buf, msg, ap);
1638	va_end(ap);
1639
1640	iov[0].iov_base = buf;
1641	iov[0].iov_len = strlen(buf);
1642	iov[1].iov_base = __UNCONST("\n");
1643	iov[1].iov_len = 1;
1644	(void)writev(fd, iov, 2);
1645	(void)close(fd);
1646	free(buf);
1647}
1648