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