xref: /netbsd/usr.sbin/lpr/lpd/printjob.c (revision 78876854)
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
42 static 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 
94 static dev_t	fdev;		/* device of file pointed to by symlink */
95 static ino_t	fino;		/* inode of file pointed to by symlink */
96 static FILE	*cfp;		/* control file */
97 static int	child;		/* id of any filters */
98 static int	lfd;		/* lock file descriptor */
99 static int	ofd;		/* output filter file descriptor */
100 static int	ofilter;	/* id of output filter, if any */
101 static int	pfd;		/* printer file descriptor */
102 static int	pid;		/* pid of lpd process */
103 static int	prchild;	/* id of pr process */
104 static char	title[80];	/* ``pr'' title */
105 static int	tof;		/* true if at top of form */
106 
107 static char	class[32];		/* classification field */
108 static char	fromhost[32];		/* user's host machine */
109 				/* indentation size in static characters */
110 static char	indent[10] = "-i0";
111 static char	jobname[100];		/* job or file name */
112 static char	length[10] = "-l";	/* page length in lines */
113 static char	logname[32];		/* user's login name */
114 static char	pxlength[10] = "-y";	/* page length in pixels */
115 static char	pxwidth[10] = "-x";	/* page width in pixels */
116 static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
117 static char	tempremote[] = "remoteXXXXXX"; /* file name for remote filter */
118 static char	width[10] = "-w";	/* page width in static characters */
119 
120 __dead static void	abortpr(int);
121 static void	banner(char *, char *);
122 static int	dofork(int);
123 static int	dropit(int);
124 static void	init(void);
125 static void	setup_ofilter(int);
126 static void	close_ofilter(void);
127 static void	openpr(void);
128 static void	opennet(void);
129 static void	opentty(void);
130 static void	openrem(void);
131 static int	print(int, char *);
132 static int	printit(char *);
133 static void	pstatus(const char *, ...)
134 	__attribute__((__format__(__printf__, 1, 2)));
135 static char	response(void);
136 static void	scan_out(int, char *, int);
137 static char	*scnline(int, char *, int);
138 static int	sendfile(int, char *);
139 static int	sendit(char *);
140 static void	sendmail(char *, int);
141 static void	setty(void);
142 static void	alarmer(int);
143 
144 void
printjob(void)145 printjob(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 */
235 again:
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
324 char	fonts[4][FONTLEN];	/* fonts for troff */
325 
326 char 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  */
337 static int
printit(char * file)338 printit(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 
503 pass2:
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  */
540 static int
print(int format,char * file)541 print(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 	}
729 start:
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  */
798 static int
sendit(char * file)799 sendit(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  */
884 static int
sendfile(int type,char * file)885 sendfile(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  */
988 static char
response(void)989 response(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  */
1012 static void
banner(char * name1,char * name2)1013 banner(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 
1051 static char *
scnline(int key,char * p,int c)1052 scnline(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 
1065 static void
scan_out(int scfd,char * scsp,int dlm)1066 scan_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 
1099 static int
dropit(int c)1100 dropit(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  */
1123 static void
sendmail(char * user,int bombed)1124 sendmail(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  */
1203 static int
dofork(int action)1204 dofork(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  */
1248 static void
abortpr(int signo)1249 abortpr(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 
1261 static void
init(void)1262 init(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  */
1323 static void
setup_ofilter(int check_rflag)1324 setup_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  */
1360 static void
close_ofilter(void)1361 close_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  */
1378 static void
openpr(void)1379 openpr(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  */
1404 static void
opennet(void)1405 opennet(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  */
1438 static void
opentty(void)1439 opentty(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  */
1466 static void
openrem(void)1467 openrem(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 
1496 static void
alarmer(int s)1497 alarmer(int s)
1498 {
1499 	/* nothing */
1500 }
1501 
1502 #if !defined(__NetBSD__)
1503 struct 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  */
1531 static void
setty(void)1532 setty(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 
1621 static void
pstatus(const char * msg,...)1622 pstatus(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