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