xref: /original-bsd/usr.sbin/lpr/lpd/printjob.c (revision 23a40993)
1 /*	printjob.c	4.10	83/06/29	*/
2 /*
3  * printjob -- print jobs in the queue.
4  *
5  *	NOTE: the lock file is used to pass information to lpq and lprm.
6  *	it does not need to be removed because file locks are dynamic.
7  */
8 
9 #include "lp.h"
10 
11 #define DORETURN	0		/* absorb fork error */
12 #define DOABORT		1		/* abort if dofork fails */
13 
14 static char	title[80];		/* ``pr'' title */
15 static FILE	*cfp;			/* control file */
16 static int	pfd;			/* printer file descriptor */
17 static int	ofd;			/* output filter file descriptor */
18 static int	lfd;			/* lock file descriptor */
19 static int	pid;			/* pid of lpd process */
20 static int	prchild;		/* id of pr process */
21 static int	child;			/* id of any filters */
22 static int	ofilter;		/* id of output filter, if any */
23 static int	tof;			/* true if at top of form */
24 static int	remote;			/* true if sending files to remote */
25 
26 static char	logname[32];		/* user's login name */
27 static char	jobname[32];		/* job or file name */
28 static char	class[32];		/* classification field */
29 static char	width[10] = "-w";	/* page width in characters */
30 static char	length[10] = "-l";	/* page length in lines */
31 static char	pxwidth[10] = "-x";	/* page width in pixels */
32 static char	pxlength[10] = "-y";	/* page length in pixels */
33 static char	indent[10] = "-i0";	/* indentation size in characters */
34 
35 printjob()
36 {
37 	struct stat stb;
38 	register struct queue *q, **qp;
39 	struct queue **queue;
40 	register int i, nitems;
41 	long pidoff;
42 	extern int onintr();
43 
44 	init();					/* set up capabilities */
45 	(void) write(1, "", 1);			/* ack that daemon is started */
46 	(void) close(1);			/* set up log file */
47 	(void) close(2);
48 	if (open(LF, O_WRONLY|O_APPEND) < 0)
49 		(void) open("/dev/null", O_WRONLY);
50 	dup(1);
51 	pid = getpid();				/* for use with lprm */
52 	setpgrp(0, pid);
53 	signal(SIGINT, onintr);
54 
55 	/*
56 	 * uses short form file names
57 	 */
58 	if (chdir(SD) < 0) {
59 		log("cannot chdir to %s", SD);
60 		exit(1);
61 	}
62 	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
63 		exit(0);		/* printing disabled */
64 	lfd = open(LO, O_WRONLY|O_CREAT|O_TRUNC, 0644);
65 	if (lfd < 0) {
66 		log("cannot create %s", LO);
67 		exit(1);
68 	}
69 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
70 		if (errno == EWOULDBLOCK)	/* active deamon present */
71 			exit(0);
72 		log("cannot lock %s", LO);
73 		exit(1);
74 	}
75 	ftruncate(lfd, 0);
76 	/*
77 	 * write process id for others to know
78 	 */
79 	sprintf(line, "%u\n", pid);
80 	pidoff = i = strlen(line);
81 	if (write(lfd, line, i) != i) {
82 		log("cannot write daemon pid");
83 		exit(1);
84 	}
85 	/*
86 	 * search the spool directory for work and sort by queue order.
87 	 */
88 	if ((nitems = getq(&queue)) < 0) {
89 		log("can't scan spool directory %s", SD);
90 		exit(1);
91 	}
92 	if (nitems == 0)		/* no work to do */
93 		exit(0);
94 	if (stb.st_mode & 01) {		/* reset queue flag */
95 		if (fchmod(lfd, stb.st_mode & 0776) < 0)
96 			log("cannot chmod %s", LO);
97 	}
98 	openpr();			/* open printer or remote */
99 again:
100 	/*
101 	 * we found something to do now do it --
102 	 *    write the name of the current control file into the lock file
103 	 *    so the spool queue program can tell what we're working on
104 	 */
105 	for (qp = queue; nitems--; free((char *) q)) {
106 		q = *qp++;
107 		if (stat(q->q_name, &stb) < 0)
108 			continue;
109 	restart:
110 		(void) lseek(lfd, pidoff, 0);
111 		(void) sprintf(line, "%s\n", q->q_name);
112 		i = strlen(line);
113 		if (write(lfd, line, i) != i)
114 			log("can't write (%d) control file name", errno);
115 		if (!remote)
116 			i = printit(q->q_name);
117 		else
118 			i = sendit(q->q_name);
119 		/*
120 		 * Check to see if we are supposed to stop printing or
121 		 * if we are to rebuild the queue.
122 		 */
123 		if (fstat(lfd, &stb) == 0) {
124 			if (stb.st_mode & 0100)
125 				goto done;
126 			if (stb.st_mode & 01) {
127 				for (free((char *) q); nitems--; free((char *) q))
128 					q = *qp++;
129 				if (fchmod(lfd, stb.st_mode & 0776) < 0)
130 					log("cannot chmod %s", LO);
131 				break;
132 			}
133 		}
134 		/*
135 		 * Check to see if we should try reprinting the job.
136 		 */
137 		if (i > 0) {
138 			log("restarting");
139 			if (ofilter > 0) {
140 				kill(ofilter, SIGCONT);	/* to be sure */
141 				(void) close(ofd);
142 				while ((i = wait(0)) > 0 && i != ofilter)
143 					;
144 				ofilter = 0;
145 			}
146 			(void) close(pfd);	/* close printer */
147 			(void) lseek(lfd, pidoff, 0);
148 			if (write(lfd, "\n", 1) != 1)
149 				log("can't write (%d) control file name", errno);
150 			openpr();		/* try to reopen printer */
151 			goto restart;
152 		}
153 	}
154 	free((char *) queue);
155 	/*
156 	 * search the spool directory for more work.
157 	 */
158 	if ((nitems = getq(&queue)) < 0) {
159 		log("can't scan spool directory %s", SD);
160 		exit(1);
161 	}
162 	if (nitems == 0) {		/* no more work to do */
163 	done:
164 		if (!SF && !tof)
165 			(void) write(ofd, FF, strlen(FF));
166 		if (TR != NULL)		/* output trailer */
167 			(void) write(ofd, TR, strlen(TR));
168 		exit(0);
169 	}
170 	goto again;
171 }
172 
173 char	fonts[4][50];	/* fonts for troff */
174 
175 static char ifonts[4][18] = {
176 	"/usr/lib/vfont/R",
177 	"/usr/lib/vfont/I",
178 	"/usr/lib/vfont/B",
179 	"/usr/lib/vfont/S"
180 };
181 
182 /*
183  * The remaining part is the reading of the control file (cf)
184  * and performing the various actions.
185  * Returns 0 if everthing was OK, 1 if we should try to reprint the job and
186  * -1 if a non-recoverable error occured.
187  */
188 static
189 printit(file)
190 	char *file;
191 {
192 	register int i;
193 	int bombed = 0;
194 
195 	/*
196 	 * open control file
197 	 */
198 	if ((cfp = fopen(file, "r")) == NULL) {
199 		log("control file (%s) open failure <errno = %d>", file, errno);
200 		return(0);
201 	}
202 	/*
203 	 * Reset troff fonts.
204 	 */
205 	for (i = 0; i < 4; i++)
206 		strcpy(fonts[i], ifonts[i]);
207 
208 	/*
209 	 *      read the control file for work to do
210 	 *
211 	 *      file format -- first character in the line is a command
212 	 *      rest of the line is the argument.
213 	 *      valid commands are:
214 	 *
215 	 *		J -- "job name" on banner page
216 	 *		C -- "class name" on banner page
217 	 *              L -- "literal" user's name to print on banner
218 	 *		T -- "title" for pr
219 	 *		H -- "host name" of machine where lpr was done
220 	 *              P -- "person" user's login name
221 	 *              I -- "indent" amount to indent output
222 	 *              f -- "file name" name of text file to print
223 	 *		l -- "file name" text file with control chars
224 	 *		p -- "file name" text file to print with pr(1)
225 	 *		t -- "file name" troff(1) file to print
226 	 *		n -- "file name" ditroff(1) file to print
227 	 *		d -- "file name" dvi file to print
228 	 *		g -- "file name" plot(1G) file to print
229 	 *		v -- "file name" plain raster file to print
230 	 *		c -- "file name" cifplot file to print
231 	 *		1 -- "R font file" for troff
232 	 *		2 -- "I font file" for troff
233 	 *		3 -- "B font file" for troff
234 	 *		4 -- "S font file" for troff
235 	 *		N -- "name" of file (used by lpq)
236 	 *              U -- "unlink" name of file to remove
237 	 *                    (after we print it. (Pass 2 only)).
238 	 *		M -- "mail" to user when done printing
239 	 *
240 	 *      getline reads a line and expands tabs to blanks
241 	 */
242 
243 	/* pass 1 */
244 
245 	while (getline(cfp))
246 		switch (line[0]) {
247 		case 'H':
248 			strcpy(host, line+1);
249 			if (class[0] == '\0')
250 				strcpy(class, line+1);
251 			continue;
252 
253 		case 'P':
254 			strcpy(logname, line+1);
255 			if (RS) {			/* restricted */
256 				if (getpwnam(logname) == (struct passwd *)0) {
257 					bombed = 2;
258 					sendmail(bombed);
259 					goto pass2;
260 				}
261 			}
262 			continue;
263 
264 		case 'J':
265 			if (line[1] != '\0')
266 				strcpy(jobname, line+1);
267 			else
268 				strcpy(jobname, " ");
269 			continue;
270 
271 		case 'C':
272 			if (line[1] != '\0')
273 				strcpy(class, line+1);
274 			else if (class[0] == '\0')
275 				gethostname(class, sizeof (class));
276 			continue;
277 
278 		case 'T':	/* header title for pr */
279 			strcpy(title, line+1);
280 			continue;
281 
282 		case 'L':	/* identification line */
283 			if (!SH)
284 				banner(line+1, jobname);
285 			continue;
286 
287 		case '1':	/* troff fonts */
288 		case '2':
289 		case '3':
290 		case '4':
291 			if (line[1] != '\0')
292 				strcpy(fonts[line[0]-'1'], line+1);
293 			continue;
294 
295 		case 'W':	/* page width */
296 			strcpy(width+2, line+1);
297 			continue;
298 
299 		case 'I':	/* indent amount */
300 			strcpy(indent+2, line+1);
301 			continue;
302 
303 		default:	/* some file to print */
304 			if ((i = print(line[0], line+1)) > 0) {
305 				(void) fclose(cfp);
306 				return(1);
307 			} else if (i < 0)
308 				bombed = 1;
309 			title[0] = '\0';
310 			continue;
311 
312 		case 'N':
313 		case 'U':
314 		case 'M':
315 			continue;
316 		}
317 
318 	/* pass 2 */
319 
320 pass2:
321 	fseek(cfp, 0L, 0);
322 	while (getline(cfp))
323 		switch (line[0]) {
324 		case 'M':
325 			if (bombed != 2)		/* already sent if 2 */
326 				sendmail(bombed);
327 			continue;
328 
329 		case 'U':
330 			(void) unlink(line+1);
331 		}
332 	/*
333 	 * clean-up incase another control file exists
334 	 */
335 	(void) fclose(cfp);
336 	(void) unlink(file);
337 	return(0);
338 }
339 
340 /*
341  * Print a file.
342  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
343  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
344  * 0 if all is well.
345  * Note: all filters take stdin as the file, stdout as the printer,
346  * stderr as the log file, and must not ignore SIGINT.
347  */
348 static
349 print(format, file)
350 	int format;
351 	char *file;
352 {
353 	register int n, fi, fo;
354 	register char *prog;
355 	char *av[15], buf[BUFSIZ];
356 	int pid, p[2], stopped = 0;
357 	union wait status;
358 
359 	if ((fi = open(file, O_RDONLY)) < 0) {
360 		log("%s: open failure <errno = %d>", file, errno);
361 		return(-1);
362 	}
363 	if (!SF && !tof) {		/* start on a fresh page */
364 		(void) write(ofd, FF, strlen(FF));
365 		tof = 1;
366 	}
367 	if (IF == NULL && (format == 'f' || format == 'l')) {
368 		tof = 0;
369 		while ((n = read(fi, buf, BUFSIZ)) > 0)
370 			if (write(ofd, buf, n) != n) {
371 				(void) close(fi);
372 				return(1);
373 			}
374 		(void) close(fi);
375 		return(0);
376 	}
377 	switch (format) {
378 	case 'p':	/* print file using 'pr' */
379 		if (IF == NULL) {	/* use output filter */
380 			prog = PR;
381 			av[0] = "pr";
382 			av[1] = width;
383 			av[2] = length;
384 			av[3] = "-h";
385 			av[4] = *title ? title : " ";
386 			av[5] = 0;
387 			fo = ofd;
388 			goto start;
389 		}
390 		pipe(p);
391 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
392 			dup2(fi, 0);		/* file is stdin */
393 			dup2(p[1], 1);		/* pipe is stdout */
394 			for (n = 3; n < NOFILE; n++)
395 				(void) close(n);
396 			execl(PR, "pr", width, length, "-h", *title ? title : " ", 0);
397 			log("cannot execl %s", PR);
398 			exit(2);
399 		}
400 		(void) close(p[1]);		/* close output side */
401 		(void) close(fi);
402 		if (prchild < 0) {
403 			prchild = 0;
404 			(void) close(p[0]);
405 			return(-1);
406 		}
407 		fi = p[0];			/* use pipe for input */
408 	case 'f':	/* print plain text file */
409 		prog = IF;
410 		av[1] = width;
411 		av[2] = length;
412 		av[3] = indent;
413 		n = 4;
414 		break;
415 	case 'l':	/* like 'f' but pass control characters */
416 		prog = IF;
417 		av[1] = "-l";
418 		av[2] = width;
419 		av[3] = length;
420 		av[4] = indent;
421 		n = 5;
422 		break;
423 	case 'r':	/* print a fortran text file */
424 		prog = RF;
425 		av[1] = width;
426 		av[2] = length;
427 		n = 3;
428 		break;
429 	case 't':	/* print troff output */
430 	case 'n':	/* print ditroff output */
431 	case 'd':	/* print tex output */
432 		(void) unlink(".railmag");
433 		if ((fo = creat(".railmag", FILMOD)) < 0) {
434 			log("cannot create .railmag");
435 			(void) unlink(".railmag");
436 		} else {
437 			for (n = 0; n < 4; n++) {
438 				if (fonts[n][0] != '/')
439 					(void) write(fo, "/usr/lib/vfont/", 15);
440 				(void) write(fo, fonts[n], strlen(fonts[n]));
441 				(void) write(fo, "\n", 1);
442 			}
443 			(void) close(fo);
444 		}
445 		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
446 		av[1] = pxwidth;
447 		av[2] = pxlength;
448 		n = 3;
449 		break;
450 	case 'c':	/* print cifplot output */
451 		prog = CF;
452 		av[1] = pxwidth;
453 		av[2] = pxlength;
454 		n = 3;
455 		break;
456 	case 'g':	/* print plot(1G) output */
457 		prog = GF;
458 		av[1] = pxwidth;
459 		av[2] = pxlength;
460 		n = 3;
461 		break;
462 	case 'v':	/* print raster output */
463 		prog = VF;
464 		av[1] = pxwidth;
465 		av[2] = pxlength;
466 		n = 3;
467 		break;
468 	default:
469 		(void) close(fi);
470 		log("illegal format character '%c'", format);
471 		return(-1);
472 	}
473 	if ((av[0] = rindex(prog, '/')) != NULL)
474 		av[0]++;
475 	else
476 		av[0] = prog;
477 	av[n++] = "-n";
478 	av[n++] = logname;
479 	av[n++] = "-h";
480 	av[n++] = host;
481 	av[n++] = AF;
482 	av[n] = 0;
483 	fo = pfd;
484 	if (ofilter > 0) {		/* stop output filter */
485 		write(ofd, "\031\1", 2);
486 		while ((pid = wait3(&status, WUNTRACED, 0)) > 0 && pid != ofilter)
487 			;
488 		if (status.w_stopval != WSTOPPED) {
489 			(void) close(fi);
490 			log("output filter died (%d)", status.w_retcode);
491 			return(1);
492 		}
493 		stopped++;
494 	}
495 start:
496 	if ((child = dofork(DORETURN)) == 0) {	/* child */
497 		dup2(fi, 0);
498 		dup2(fo, 1);
499 		for (n = 3; n < NOFILE; n++)
500 			(void) close(n);
501 		execv(prog, av);
502 		log("cannot execl %s", prog);
503 		exit(2);
504 	}
505 	(void) close(fi);
506 	if (child < 0)
507 		status.w_retcode = 100;
508 	else
509 		while ((pid = wait(&status)) > 0 && pid != child)
510 			;
511 	child = 0;
512 	prchild = 0;
513 	if (stopped) {		/* restart output filter */
514 		if (kill(ofilter, SIGCONT) < 0) {
515 			log("cannot restart output filter");
516 			exit(1);
517 		}
518 	}
519 	tof = 0;
520 	if (!WIFEXITED(status) || status.w_retcode > 1) {
521 		log("Daemon Filter '%c' Malfunction (%d)", format, status.w_retcode);
522 		return(-1);
523 	} else if (status.w_retcode == 1)
524 		return(1);
525 	tof = 1;
526 	return(0);
527 }
528 
529 /*
530  * Send the daemon control file (cf) and any data files.
531  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
532  * 0 if all is well.
533  */
534 static
535 sendit(file)
536 	char *file;
537 {
538 	register int linelen, err = 0;
539 	char last[132];
540 
541 	/*
542 	 * open control file
543 	 */
544 	if ((cfp = fopen(file, "r")) == NULL) {
545 		log("control file (%s) open failure <errno = %d>", file, errno);
546 		return(0);
547 	}
548 	/*
549 	 *      read the control file for work to do
550 	 *
551 	 *      file format -- first character in the line is a command
552 	 *      rest of the line is the argument.
553 	 *      commands of interest are:
554 	 *
555 	 *            a-z -- "file name" name of file to print
556 	 *              U -- "unlink" name of file to remove
557 	 *                    (after we print it. (Pass 2 only)).
558 	 */
559 
560 	/*
561 	 * pass 1
562 	 */
563 	while (getline(cfp)) {
564 	again:
565 		if (line[0] >= 'a' && line[0] <= 'z') {
566 			strcpy(last, line);
567 			while (linelen = getline(cfp))
568 				if (strcmp(last, line))
569 					break;
570 			if ((err = sendfile('\3', last+1)) > 0) {
571 				(void) fclose(cfp);
572 				return(1);
573 			} else if (err)
574 				break;
575 			if (linelen)
576 				goto again;
577 			break;
578 		}
579 	}
580 	if (!err && sendfile('\2', file) > 0) {
581 		(void) fclose(cfp);
582 		return(1);
583 	}
584 	/*
585 	 * pass 2
586 	 */
587 	fseek(cfp, 0L, 0);
588 	while (getline(cfp))
589 		if (line[0] == 'U')
590 			(void) unlink(line+1);
591 	/*
592 	 * clean-up incase another control file exists
593 	 */
594 	(void) fclose(cfp);
595 	(void) unlink(file);
596 	return(0);
597 }
598 
599 /*
600  * Send a data file to the remote machine and spool it.
601  * Return positive if we should try resending.
602  */
603 static
604 sendfile(type, file)
605 	char type, *file;
606 {
607 	register int f, i, amt;
608 	struct stat stb;
609 	char buf[BUFSIZ];
610 	int sizerr;
611 
612 	if ((f = open(file, O_RDONLY)) < 0 || fstat(f, &stb) < 0) {
613 		log("file (%s) open failure <errno = %d>", file, errno);
614 		return(-1);
615 	}
616 	(void) sprintf(buf, "%c%d %s\n", type, stb.st_size, file);
617 	amt = strlen(buf);
618 	if (write(pfd, buf, amt) != amt) {
619 		(void) close(f);
620 		return(1);
621 	}
622 	if (noresponse()) {
623 		(void) close(f);
624 		return(1);
625 	}
626 	sizerr = 0;
627 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
628 		amt = BUFSIZ;
629 		if (i + amt > stb.st_size)
630 			amt = stb.st_size - i;
631 		if (sizerr == 0 && read(f, buf, amt) != amt)
632 			sizerr = 1;
633 		if (write(pfd, buf, amt) != amt) {
634 			(void) close(f);
635 			return(1);
636 		}
637 	}
638 	(void) close(f);
639 	if (sizerr) {
640 		log("%s: changed size", file);
641 		(void) write(pfd, "\1", 1);  /* tell recvjob to ignore this file */
642 		return(-1);
643 	}
644 	if (write(pfd, "", 1) != 1)
645 		return(1);
646 	if (noresponse())
647 		return(1);
648 	return(0);
649 }
650 
651 /*
652  * Check to make sure there have been no errors and that both programs
653  * are in sync with eachother.
654  * Return non-zero if the connection was lost.
655  */
656 static
657 noresponse()
658 {
659 	char resp;
660 
661 	if (read(pfd, &resp, 1) != 1 || resp != '\0') {
662 		log("lost connection or error in recvjob");
663 		return(1);
664 	}
665 	return(0);
666 }
667 
668 /*
669  * Banner printing stuff
670  */
671 static
672 banner(name1, name2)
673 	char *name1, *name2;
674 {
675 	time_t tvec;
676 	extern char *ctime();
677 
678 	time(&tvec);
679 	if (!SF && !tof)
680 		(void) write(ofd, FF, strlen(FF));
681 	if (SB) {	/* short banner only */
682 		if (class[0]) {
683 			(void) write(ofd, class, strlen(class));
684 			(void) write(ofd, ":", 1);
685 		}
686 		(void) write(ofd, name1, strlen(name1));
687 		(void) write(ofd, "  Job: ", 7);
688 		(void) write(ofd, name2, strlen(name2));
689 		(void) write(ofd, "  Date: ", 8);
690 		(void) write(ofd, ctime(&tvec), 24);
691 		(void) write(ofd, "\n", 1);
692 	} else {	/* normal banner */
693 		(void) write(ofd, "\n\n\n", 3);
694 		scan_out(ofd, name1, '\0');
695 		(void) write(ofd, "\n\n", 2);
696 		scan_out(ofd, name2, '\0');
697 		if (class[0]) {
698 			(void) write(ofd,"\n\n\n",3);
699 			scan_out(ofd, class, '\0');
700 		}
701 		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
702 		(void) write(ofd, name2, strlen(name2));
703 		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
704 		(void) write(ofd, ctime(&tvec), 24);
705 		(void) write(ofd, "\n", 1);
706 	}
707 	if (!SF)
708 		(void) write(ofd, FF, strlen(FF));
709 	tof = 1;
710 }
711 
712 static char *
713 scnline(key, p, c)
714 	register char key, *p;
715 	char c;
716 {
717 	register scnwidth;
718 
719 	for (scnwidth = WIDTH; --scnwidth;) {
720 		key <<= 1;
721 		*p++ = key & 0200 ? c : BACKGND;
722 	}
723 	return (p);
724 }
725 
726 #define TRC(q)	(((q)-' ')&0177)
727 
728 static
729 scan_out(scfd, scsp, dlm)
730 	int scfd;
731 	char *scsp, dlm;
732 {
733 	register char *strp;
734 	register nchrs, j;
735 	char outbuf[LINELEN+1], *sp, c, cc;
736 	int d, scnhgt;
737 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
738 
739 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
740 		strp = &outbuf[0];
741 		sp = scsp;
742 		for (nchrs = 0; ; ) {
743 			d = dropit(c = TRC(cc = *sp++));
744 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
745 				for (j = WIDTH; --j;)
746 					*strp++ = BACKGND;
747 			else
748 				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
749 			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
750 				break;
751 			*strp++ = BACKGND;
752 			*strp++ = BACKGND;
753 		}
754 		while (*--strp == BACKGND && strp >= outbuf)
755 			;
756 		strp++;
757 		*strp++ = '\n';
758 		(void) write(scfd, outbuf, strp-outbuf);
759 	}
760 }
761 
762 static
763 dropit(c)
764 	char c;
765 {
766 	switch(c) {
767 
768 	case TRC('_'):
769 	case TRC(';'):
770 	case TRC(','):
771 	case TRC('g'):
772 	case TRC('j'):
773 	case TRC('p'):
774 	case TRC('q'):
775 	case TRC('y'):
776 		return (DROP);
777 
778 	default:
779 		return (0);
780 	}
781 }
782 
783 /*
784  * sendmail ---
785  *   tell people about job completion
786  */
787 static
788 sendmail(bombed)
789 	int bombed;
790 {
791 	static int p[2];
792 	register int i;
793 	int stat;
794 	register char *cp;
795 	char buf[100];
796 
797 	pipe(p);
798 	if ((stat = dofork(DORETURN)) == 0) {		/* child */
799 		dup2(p[0], 0);
800 		for (i = 3; i < NOFILE; i++)
801 			(void) close(i);
802 		if ((cp = rindex(MAIL, '/')) != NULL)
803 			cp++;
804 		else
805 			cp = MAIL;
806 		sprintf(buf, "%s@%s", line+1, host);
807 		execl(MAIL, cp, buf, 0);
808 		exit(0);
809 	} else if (stat > 0) {				/* parent */
810 		dup2(p[1], 1);
811 		printf("To: %s\n", line+1);
812 		printf("Subject: printer job\n\n");
813 		printf("Your printer job ");
814 		if (*jobname)
815 			printf("(%s) ", jobname);
816 		switch (bombed) {
817 		case 0:
818 			printf("\ncompleted successfully\n");
819 			break;
820 		default:
821 		case 1:
822 			printf("\ncould not be printed\n");
823 			break;
824 		case 2:
825 			printf("\ncould not be printed without an account on %s\n", host);
826 			break;
827 		}
828 		fflush(stdout);
829 		(void) close(1);
830 	}
831 	(void) close(p[0]);
832 	(void) close(p[1]);
833 	wait(&stat);
834 }
835 
836 /*
837  * dofork - fork with retries on failure
838  */
839 static
840 dofork(action)
841 	int action;
842 {
843 	register int i, pid;
844 
845 	for (i = 0; i < 20; i++) {
846 		if ((pid = fork()) < 0) {
847 			sleep((unsigned)(i*i));
848 			continue;
849 		}
850 		/*
851 		 * Child should run as daemon instead of root
852 		 */
853 		if (pid == 0)
854 			setuid(DU);
855 		return(pid);
856 	}
857 	log("can't fork");
858 
859 	switch (action) {
860 	case DORETURN:
861 		return (-1);
862 	default:
863 		log("bad action (%d) to dofork", action);
864 		/*FALL THRU*/
865 	case DOABORT:
866 		exit(1);
867 	}
868 	/*NOTREACHED*/
869 }
870 
871 /*
872  * Cleanup child processes when a SIGINT is caught.
873  */
874 static
875 onintr()
876 {
877 	kill(0, SIGINT);
878 	if (ofilter > 0)
879 		kill(ofilter, SIGCONT);
880 	while (wait(0) > 0)
881 		;
882 	exit(0);
883 }
884 
885 static
886 init()
887 {
888 	int status;
889 
890 	if ((status = pgetent(line, printer)) < 0)
891 		fatal("can't open printer description file");
892 	else if (status == 0)
893 		fatal("unknown printer");
894 	if ((LP = pgetstr("lp", &bp)) == NULL)
895 		LP = DEFDEVLP;
896 	if ((RP = pgetstr("rp", &bp)) == NULL)
897 		RP = DEFLP;
898 	if ((LO = pgetstr("lo", &bp)) == NULL)
899 		LO = DEFLOCK;
900 	if ((ST = pgetstr("st", &bp)) == NULL)
901 		ST = DEFSTAT;
902 	if ((LF = pgetstr("lf", &bp)) == NULL)
903 		LF = DEFLOGF;
904 	if ((SD = pgetstr("sd", &bp)) == NULL)
905 		SD = DEFSPOOL;
906 	if ((DU = pgetnum("du")) < 0)
907 		DU = DEFUID;
908 	if ((FF = pgetstr("ff", &bp)) == NULL)
909 		FF = DEFFF;
910 	if ((PW = pgetnum("pw")) < 0)
911 		PW = DEFWIDTH;
912 	sprintf(&width[2], "%d", PW);
913 	if ((PL = pgetnum("pl")) < 0)
914 		PL = DEFLENGTH;
915 	sprintf(&length[2], "%d", PL);
916 	if ((PX = pgetnum("px")) < 0)
917 		PX = 0;
918 	sprintf(&pxwidth[2], "%d", PX);
919 	if ((PY = pgetnum("py")) < 0)
920 		PY = 0;
921 	sprintf(&pxlength[2], "%d", PY);
922 	RM = pgetstr("rm", &bp);
923 	AF = pgetstr("af", &bp);
924 	OF = pgetstr("of", &bp);
925 	IF = pgetstr("if", &bp);
926 	RF = pgetstr("rf", &bp);
927 	TF = pgetstr("tf", &bp);
928 	NF = pgetstr("nf", &bp);
929 	DF = pgetstr("df", &bp);
930 	GF = pgetstr("gf", &bp);
931 	VF = pgetstr("vf", &bp);
932 	CF = pgetstr("cf", &bp);
933 	TR = pgetstr("tr", &bp);
934 	RS = pgetflag("rs");
935 	SF = pgetflag("sf");
936 	SH = pgetflag("sh");
937 	SB = pgetflag("sb");
938 	RW = pgetflag("rw");
939 	BR = pgetnum("br");
940 	if ((FC = pgetnum("fc")) < 0)
941 		FC = 0;
942 	if ((FS = pgetnum("fs")) < 0)
943 		FS = 0;
944 	if ((XC = pgetnum("xc")) < 0)
945 		XC = 0;
946 	if ((XS = pgetnum("xs")) < 0)
947 		XS = 0;
948 	tof = !pgetflag("fo");
949 }
950 
951 /*
952  * Acquire line printer or remote connection.
953  */
954 static
955 openpr()
956 {
957 	register int i, n;
958 
959 	if (*LP) {
960 		for (i = 1; ; i = i < 32 ? i << 1 : i) {
961 			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
962 			if (pfd >= 0)
963 				break;
964 			if (errno == ENOENT) {
965 				log("cannot open %s", LP);
966 				exit(1);
967 			}
968 			if (i == 1)
969 				status("waiting for %s to become ready (offline ?)", printer);
970 			sleep(i);
971 		}
972 		if (isatty(pfd))
973 			setty();
974 		status("%s is ready and printing", printer);
975 	} else if (RM != NULL) {
976 		for (i = 1; ; i = i < 512 ? i << 1 : i) {
977 			pfd = getport(RM);
978 			if (pfd >= 0) {
979 				(void) sprintf(line, "\2%s\n", RP);
980 				n = strlen(line);
981 				if (write(pfd, line, n) != n)
982 					break;
983 				if (noresponse())
984 					(void) close(pfd);
985 				else
986 					break;
987 			}
988 			if (i == 1)
989 				status("waiting for %s to come up", RM);
990 			sleep(i);
991 		}
992 		status("sending to %s", RM);
993 		remote = 1;
994 	} else {
995 		log("no line printer device or remote machine name");
996 		exit(1);
997 	}
998 	/*
999 	 * Start up an output filter, if needed.
1000 	 */
1001 	if (OF) {
1002 		int p[2];
1003 		char *cp;
1004 
1005 		pipe(p);
1006 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1007 			dup2(p[0], 0);		/* pipe is std in */
1008 			dup2(pfd, 1);		/* printer is std out */
1009 			for (i = 3; i < NOFILE; i++)
1010 				(void) close(i);
1011 			if ((cp = rindex(OF, '/')) == NULL)
1012 				cp = OF;
1013 			else
1014 				cp++;
1015 			execl(OF, cp, width, length, 0);
1016 			log("can't execl output filter %s", OF);
1017 			exit(1);
1018 		}
1019 		(void) close(p[0]);		/* close input side */
1020 		ofd = p[1];			/* use pipe for output */
1021 	} else {
1022 		ofd = pfd;
1023 		ofilter = 0;
1024 	}
1025 }
1026 
1027 struct bauds {
1028 	int	baud;
1029 	int	speed;
1030 } bauds[] = {
1031 	50,	B50,
1032 	75,	B75,
1033 	110,	B110,
1034 	134,	B134,
1035 	150,	B150,
1036 	200,	B200,
1037 	300,	B300,
1038 	600,	B600,
1039 	1200,	B1200,
1040 	1800,	B1800,
1041 	2400,	B2400,
1042 	4800,	B4800,
1043 	9600,	B9600,
1044 	19200,	EXTA,
1045 	38400,	EXTB,
1046 	0,	0
1047 };
1048 
1049 /*
1050  * setup tty lines.
1051  */
1052 static
1053 setty()
1054 {
1055 	struct sgttyb ttybuf;
1056 	register struct bauds *bp;
1057 
1058 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1059 		log("cannot set exclusive-use");
1060 		exit(1);
1061 	}
1062 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1063 		log("cannot get tty parameters");
1064 		exit(1);
1065 	}
1066 	if (BR > 0) {
1067 		for (bp = bauds; bp->baud; bp++)
1068 			if (BR == bp->baud)
1069 				break;
1070 		if (!bp->baud) {
1071 			log("illegal baud rate %d", BR);
1072 			exit(1);
1073 		}
1074 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1075 	}
1076 	ttybuf.sg_flags &= ~FC;
1077 	ttybuf.sg_flags |= FS;
1078 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1079 		log("cannot set tty parameters");
1080 		exit(1);
1081 	}
1082 	if (XC) {
1083 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1084 			log("cannot set local tty parameters");
1085 			exit(1);
1086 		}
1087 	}
1088 	if (XS) {
1089 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1090 			log("cannot set local tty parameters");
1091 			exit(1);
1092 		}
1093 	}
1094 }
1095 
1096 /*VARARGS1*/
1097 static
1098 status(msg, a1, a2, a3)
1099 	char *msg;
1100 {
1101 	register int fd;
1102 	char buf[BUFSIZ];
1103 
1104 	umask(0);
1105 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1106 	if (fd < 0 || flock(fd, LOCK_EX) < 0)
1107 		fatal("cannot create status file");
1108 	ftruncate(fd, 0);
1109 	sprintf(buf, msg, a1, a2, a3);
1110 	strcat(buf, "\n");
1111 	(void) write(fd, buf, strlen(buf));
1112 	(void) close(fd);
1113 }
1114