xref: /original-bsd/usr.sbin/lpr/lpd/printjob.c (revision b55621b9)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char copyright[] =
11 "@(#) Copyright (c) 1983, 1993\n\
12 	The Regents of the University of California.  All rights reserved.\n";
13 #endif /* not lint */
14 
15 #ifndef lint
16 static char sccsid[] = "@(#)printjob.c	8.5 (Berkeley) 04/28/95";
17 #endif /* not lint */
18 
19 
20 /*
21  * printjob -- print jobs in the queue.
22  *
23  *	NOTE: the lock file is used to pass information to lpq and lprm.
24  *	it does not need to be removed because file locks are dynamic.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/wait.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 
32 #include <pwd.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <sgtty.h>
36 #include <syslog.h>
37 #include <fcntl.h>
38 #include <dirent.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include "lp.h"
44 #include "lp.local.h"
45 #include "pathnames.h"
46 #include "extern.h"
47 
48 #define DORETURN	0	/* absorb fork error */
49 #define DOABORT		1	/* abort if dofork fails */
50 
51 /*
52  * Error tokens
53  */
54 #define REPRINT		-2
55 #define ERROR		-1
56 #define	OK		0
57 #define	FATALERR	1
58 #define	NOACCT		2
59 #define	FILTERERR	3
60 #define	ACCESS		4
61 
62 static dev_t	 fdev;		/* device of file pointed to by symlink */
63 static ino_t	 fino;		/* inode of file pointed to by symlink */
64 static FILE	*cfp;		/* control file */
65 static int	 child;		/* id of any filters */
66 static int	 lfd;		/* lock file descriptor */
67 static int	 ofd;		/* output filter file descriptor */
68 static int	 ofilter;	/* id of output filter, if any */
69 static int	 pfd;		/* prstatic inter file descriptor */
70 static int	 pid;		/* pid of lpd process */
71 static int	 prchild;	/* id of pr process */
72 static char	 title[80];	/* ``pr'' title */
73 static int	 tof;		/* true if at top of form */
74 
75 static char	class[32];		/* classification field */
76 static char	fromhost[32];		/* user's host machine */
77 				/* indentation size in static characters */
78 static char	indent[10] = "-i0";
79 static char	jobname[100];		/* job or file name */
80 static char	length[10] = "-l";	/* page length in lines */
81 static char	logname[32];		/* user's login name */
82 static char	pxlength[10] = "-y";	/* page length in pixels */
83 static char	pxwidth[10] = "-x";	/* page width in pixels */
84 static char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
85 static char	width[10] = "-w";	/* page width in static characters */
86 
87 static void       abortpr __P((int));
88 static void       banner __P((char *, char *));
89 static int        dofork __P((int));
90 static int        dropit __P((int));
91 static void       init __P((void));
92 static void       openpr __P((void));
93 static void       opennet __P((char *));
94 static void       opentty __P((void));
95 static void       openrem __P((void));
96 static int        print __P((int, char *));
97 static int        printit __P((char *));
98 static void       pstatus __P((const char *, ...));
99 static char       response __P((void));
100 static void       scan_out __P((int, char *, int));
101 static char      *scnline __P((int, char *, int));
102 static int        sendfile __P((int, char *));
103 static int        sendit __P((char *));
104 static void       sendmail __P((char *, int));
105 static void       setty __P((void));
106 
107 void
108 printjob()
109 {
110 	struct stat stb;
111 	register struct queue *q, **qp;
112 	struct queue **queue;
113 	register int i, nitems;
114 	off_t pidoff;
115 	int errcnt, count = 0;
116 
117 	init();					/* set up capabilities */
118 	(void) write(1, "", 1);			/* ack that daemon is started */
119 	(void) close(2);			/* set up log file */
120 	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
121 		syslog(LOG_ERR, "%s: %m", LF);
122 		(void) open(_PATH_DEVNULL, O_WRONLY);
123 	}
124 	setgid(getegid());
125 	pid = getpid();				/* for use with lprm */
126 	setpgrp(0, pid);
127 	signal(SIGHUP, abortpr);
128 	signal(SIGINT, abortpr);
129 	signal(SIGQUIT, abortpr);
130 	signal(SIGTERM, abortpr);
131 
132 	(void) mktemp(tempfile);
133 
134 	/*
135 	 * uses short form file names
136 	 */
137 	if (chdir(SD) < 0) {
138 		syslog(LOG_ERR, "%s: %m", SD);
139 		exit(1);
140 	}
141 	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
142 		exit(0);		/* printing disabled */
143 	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
144 	if (lfd < 0) {
145 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
146 		exit(1);
147 	}
148 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
149 		if (errno == EWOULDBLOCK)	/* active deamon present */
150 			exit(0);
151 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
152 		exit(1);
153 	}
154 	ftruncate(lfd, 0);
155 	/*
156 	 * write process id for others to know
157 	 */
158 	sprintf(line, "%u\n", pid);
159 	pidoff = i = strlen(line);
160 	if (write(lfd, line, i) != i) {
161 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
162 		exit(1);
163 	}
164 	/*
165 	 * search the spool directory for work and sort by queue order.
166 	 */
167 	if ((nitems = getq(&queue)) < 0) {
168 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
169 		exit(1);
170 	}
171 	if (nitems == 0)		/* no work to do */
172 		exit(0);
173 	if (stb.st_mode & 01) {		/* reset queue flag */
174 		if (fchmod(lfd, stb.st_mode & 0776) < 0)
175 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
176 	}
177 	openpr();			/* open printer or remote */
178 again:
179 	/*
180 	 * we found something to do now do it --
181 	 *    write the name of the current control file into the lock file
182 	 *    so the spool queue program can tell what we're working on
183 	 */
184 	for (qp = queue; nitems--; free((char *) q)) {
185 		q = *qp++;
186 		if (stat(q->q_name, &stb) < 0)
187 			continue;
188 		errcnt = 0;
189 	restart:
190 		(void) lseek(lfd, pidoff, 0);
191 		(void) sprintf(line, "%s\n", q->q_name);
192 		i = strlen(line);
193 		if (write(lfd, line, i) != i)
194 			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
195 		if (!remote)
196 			i = printit(q->q_name);
197 		else
198 			i = sendit(q->q_name);
199 		/*
200 		 * Check to see if we are supposed to stop printing or
201 		 * if we are to rebuild the queue.
202 		 */
203 		if (fstat(lfd, &stb) == 0) {
204 			/* stop printing before starting next job? */
205 			if (stb.st_mode & 0100)
206 				goto done;
207 			/* rebuild queue (after lpc topq) */
208 			if (stb.st_mode & 01) {
209 				for (free((char *) q); nitems--; free((char *) q))
210 					q = *qp++;
211 				if (fchmod(lfd, stb.st_mode & 0776) < 0)
212 					syslog(LOG_WARNING, "%s: %s: %m",
213 						printer, LO);
214 				break;
215 			}
216 		}
217 		if (i == OK)		/* file ok and printed */
218 			count++;
219 		else if (i == REPRINT && ++errcnt < 5) {
220 			/* try reprinting the job */
221 			syslog(LOG_INFO, "restarting %s", printer);
222 			if (ofilter > 0) {
223 				kill(ofilter, SIGCONT);	/* to be sure */
224 				(void) close(ofd);
225 				while ((i = wait(0)) > 0 && i != ofilter)
226 					;
227 				ofilter = 0;
228 			}
229 			(void) close(pfd);	/* close printer */
230 			if (ftruncate(lfd, pidoff) < 0)
231 				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
232 			openpr();		/* try to reopen printer */
233 			goto restart;
234 		} else {
235 			syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
236 				remote ? "sent to remote host" : "printed", q->q_name);
237 			if (i == REPRINT) {
238 				/* insure we don't attempt this job again */
239 				(void) unlink(q->q_name);
240 				q->q_name[0] = 'd';
241 				(void) unlink(q->q_name);
242 				if (logname[0])
243 					sendmail(logname, FATALERR);
244 			}
245 		}
246 	}
247 	free((char *) queue);
248 	/*
249 	 * search the spool directory for more work.
250 	 */
251 	if ((nitems = getq(&queue)) < 0) {
252 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
253 		exit(1);
254 	}
255 	if (nitems == 0) {		/* no more work to do */
256 	done:
257 		if (count > 0) {	/* Files actually printed */
258 			if (!SF && !tof)
259 				(void) write(ofd, FF, strlen(FF));
260 			if (TR != NULL)		/* output trailer */
261 				(void) write(ofd, TR, strlen(TR));
262 		}
263 		(void) unlink(tempfile);
264 		exit(0);
265 	}
266 	goto again;
267 }
268 
269 char	fonts[4][50];	/* fonts for troff */
270 
271 char ifonts[4][40] = {
272 	_PATH_VFONTR,
273 	_PATH_VFONTI,
274 	_PATH_VFONTB,
275 	_PATH_VFONTS,
276 };
277 
278 /*
279  * The remaining part is the reading of the control file (cf)
280  * and performing the various actions.
281  */
282 static int
283 printit(file)
284 	char *file;
285 {
286 	register int i;
287 	char *cp;
288 	int bombed = OK;
289 
290 	/*
291 	 * open control file; ignore if no longer there.
292 	 */
293 	if ((cfp = fopen(file, "r")) == NULL) {
294 		syslog(LOG_INFO, "%s: %s: %m", printer, file);
295 		return(OK);
296 	}
297 	/*
298 	 * Reset troff fonts.
299 	 */
300 	for (i = 0; i < 4; i++)
301 		strcpy(fonts[i], ifonts[i]);
302 	sprintf(&width[2], "%d", PW);
303 	strcpy(indent+2, "0");
304 
305 	/*
306 	 *      read the control file for work to do
307 	 *
308 	 *      file format -- first character in the line is a command
309 	 *      rest of the line is the argument.
310 	 *      valid commands are:
311 	 *
312 	 *		S -- "stat info" for symbolic link protection
313 	 *		J -- "job name" on banner page
314 	 *		C -- "class name" on banner page
315 	 *              L -- "literal" user's name to print on banner
316 	 *		T -- "title" for pr
317 	 *		H -- "host name" of machine where lpr was done
318 	 *              P -- "person" user's login name
319 	 *              I -- "indent" amount to indent output
320 	 *		R -- laser dpi "resolution"
321 	 *              f -- "file name" name of text file to print
322 	 *		l -- "file name" text file with control chars
323 	 *		p -- "file name" text file to print with pr(1)
324 	 *		t -- "file name" troff(1) file to print
325 	 *		n -- "file name" ditroff(1) file to print
326 	 *		d -- "file name" dvi file to print
327 	 *		g -- "file name" plot(1G) file to print
328 	 *		v -- "file name" plain raster file to print
329 	 *		c -- "file name" cifplot file to print
330 	 *		1 -- "R font file" for troff
331 	 *		2 -- "I font file" for troff
332 	 *		3 -- "B font file" for troff
333 	 *		4 -- "S font file" for troff
334 	 *		N -- "name" of file (used by lpq)
335 	 *              U -- "unlink" name of file to remove
336 	 *                    (after we print it. (Pass 2 only)).
337 	 *		M -- "mail" to user when done printing
338 	 *
339 	 *      getline reads a line and expands tabs to blanks
340 	 */
341 
342 	/* pass 1 */
343 
344 	while (getline(cfp))
345 		switch (line[0]) {
346 		case 'H':
347 			strcpy(fromhost, line+1);
348 			if (class[0] == '\0')
349 				strncpy(class, line+1, sizeof(class)-1);
350 			continue;
351 
352 		case 'P':
353 			strncpy(logname, line+1, sizeof(logname)-1);
354 			if (RS) {			/* restricted */
355 				if (getpwnam(logname) == NULL) {
356 					bombed = NOACCT;
357 					sendmail(line+1, bombed);
358 					goto pass2;
359 				}
360 			}
361 			continue;
362 
363 		case 'S':
364 			cp = line+1;
365 			i = 0;
366 			while (*cp >= '0' && *cp <= '9')
367 				i = i * 10 + (*cp++ - '0');
368 			fdev = i;
369 			cp++;
370 			i = 0;
371 			while (*cp >= '0' && *cp <= '9')
372 				i = i * 10 + (*cp++ - '0');
373 			fino = i;
374 			continue;
375 
376 		case 'J':
377 			if (line[1] != '\0')
378 				strncpy(jobname, line+1, sizeof(jobname)-1);
379 			else
380 				strcpy(jobname, " ");
381 			continue;
382 
383 		case 'C':
384 			if (line[1] != '\0')
385 				strncpy(class, line+1, sizeof(class)-1);
386 			else if (class[0] == '\0')
387 				gethostname(class, sizeof(class));
388 			continue;
389 
390 		case 'T':	/* header title for pr */
391 			strncpy(title, line+1, sizeof(title)-1);
392 			continue;
393 
394 		case 'L':	/* identification line */
395 			if (!SH && !HL)
396 				banner(line+1, jobname);
397 			continue;
398 
399 		case '1':	/* troff fonts */
400 		case '2':
401 		case '3':
402 		case '4':
403 			if (line[1] != '\0')
404 				strcpy(fonts[line[0]-'1'], line+1);
405 			continue;
406 
407 		case 'W':	/* page width */
408 			strncpy(width+2, line+1, sizeof(width)-3);
409 			continue;
410 
411 		case 'I':	/* indent amount */
412 			strncpy(indent+2, line+1, sizeof(indent)-3);
413 			continue;
414 
415 		default:	/* some file to print */
416 			switch (i = print(line[0], line+1)) {
417 			case ERROR:
418 				if (bombed == OK)
419 					bombed = FATALERR;
420 				break;
421 			case REPRINT:
422 				(void) fclose(cfp);
423 				return(REPRINT);
424 			case FILTERERR:
425 			case ACCESS:
426 				bombed = i;
427 				sendmail(logname, bombed);
428 			}
429 			title[0] = '\0';
430 			continue;
431 
432 		case 'N':
433 		case 'U':
434 		case 'M':
435 		case 'R':
436 			continue;
437 		}
438 
439 	/* pass 2 */
440 
441 pass2:
442 	fseek(cfp, 0L, 0);
443 	while (getline(cfp))
444 		switch (line[0]) {
445 		case 'L':	/* identification line */
446 			if (!SH && HL)
447 				banner(line+1, jobname);
448 			continue;
449 
450 		case 'M':
451 			if (bombed < NOACCT)	/* already sent if >= NOACCT */
452 				sendmail(line+1, bombed);
453 			continue;
454 
455 		case 'U':
456 			(void) unlink(line+1);
457 		}
458 	/*
459 	 * clean-up in case another control file exists
460 	 */
461 	(void) fclose(cfp);
462 	(void) unlink(file);
463 	return(bombed == OK ? OK : ERROR);
464 }
465 
466 /*
467  * Print a file.
468  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
469  * Return -1 if a non-recoverable error occured,
470  * 2 if the filter detected some errors (but printed the job anyway),
471  * 1 if we should try to reprint this job and
472  * 0 if all is well.
473  * Note: all filters take stdin as the file, stdout as the printer,
474  * stderr as the log file, and must not ignore SIGINT.
475  */
476 static int
477 print(format, file)
478 	int format;
479 	char *file;
480 {
481 	register int n;
482 	register char *prog;
483 	int fi, fo;
484 	FILE *fp;
485 	char *av[15], buf[BUFSIZ];
486 	int pid, p[2], stopped = 0;
487 	union wait status;
488 	struct stat stb;
489 
490 	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
491 		return(ERROR);
492 	/*
493 	 * Check to see if data file is a symbolic link. If so, it should
494 	 * still point to the same file or someone is trying to print
495 	 * something he shouldn't.
496 	 */
497 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
498 	    (stb.st_dev != fdev || stb.st_ino != fino))
499 		return(ACCESS);
500 	if (!SF && !tof) {		/* start on a fresh page */
501 		(void) write(ofd, FF, strlen(FF));
502 		tof = 1;
503 	}
504 	if (IF == NULL && (format == 'f' || format == 'l')) {
505 		tof = 0;
506 		while ((n = read(fi, buf, BUFSIZ)) > 0)
507 			if (write(ofd, buf, n) != n) {
508 				(void) close(fi);
509 				return(REPRINT);
510 			}
511 		(void) close(fi);
512 		return(OK);
513 	}
514 	switch (format) {
515 	case 'p':	/* print file using 'pr' */
516 		if (IF == NULL) {	/* use output filter */
517 			prog = _PATH_PR;
518 			av[0] = "pr";
519 			av[1] = width;
520 			av[2] = length;
521 			av[3] = "-h";
522 			av[4] = *title ? title : " ";
523 			av[5] = 0;
524 			fo = ofd;
525 			goto start;
526 		}
527 		pipe(p);
528 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
529 			dup2(fi, 0);		/* file is stdin */
530 			dup2(p[1], 1);		/* pipe is stdout */
531 			closelog();
532 			for (n = 3; n < NOFILE; n++)
533 				(void) close(n);
534 			execl(_PATH_PR, "pr", width, length,
535 			    "-h", *title ? title : " ", 0);
536 			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
537 			exit(2);
538 		}
539 		(void) close(p[1]);		/* close output side */
540 		(void) close(fi);
541 		if (prchild < 0) {
542 			prchild = 0;
543 			(void) close(p[0]);
544 			return(ERROR);
545 		}
546 		fi = p[0];			/* use pipe for input */
547 	case 'f':	/* print plain text file */
548 		prog = IF;
549 		av[1] = width;
550 		av[2] = length;
551 		av[3] = indent;
552 		n = 4;
553 		break;
554 	case 'l':	/* like 'f' but pass control characters */
555 		prog = IF;
556 		av[1] = "-c";
557 		av[2] = width;
558 		av[3] = length;
559 		av[4] = indent;
560 		n = 5;
561 		break;
562 	case 'r':	/* print a fortran text file */
563 		prog = RF;
564 		av[1] = width;
565 		av[2] = length;
566 		n = 3;
567 		break;
568 	case 't':	/* print troff output */
569 	case 'n':	/* print ditroff output */
570 	case 'd':	/* print tex output */
571 		(void) unlink(".railmag");
572 		if ((fo = creat(".railmag", FILMOD)) < 0) {
573 			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
574 			(void) unlink(".railmag");
575 		} else {
576 			for (n = 0; n < 4; n++) {
577 				if (fonts[n][0] != '/')
578 					(void) write(fo, _PATH_VFONT,
579 					    sizeof(_PATH_VFONT) - 1);
580 				(void) write(fo, fonts[n], strlen(fonts[n]));
581 				(void) write(fo, "\n", 1);
582 			}
583 			(void) close(fo);
584 		}
585 		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
586 		av[1] = pxwidth;
587 		av[2] = pxlength;
588 		n = 3;
589 		break;
590 	case 'c':	/* print cifplot output */
591 		prog = CF;
592 		av[1] = pxwidth;
593 		av[2] = pxlength;
594 		n = 3;
595 		break;
596 	case 'g':	/* print plot(1G) output */
597 		prog = GF;
598 		av[1] = pxwidth;
599 		av[2] = pxlength;
600 		n = 3;
601 		break;
602 	case 'v':	/* print raster output */
603 		prog = VF;
604 		av[1] = pxwidth;
605 		av[2] = pxlength;
606 		n = 3;
607 		break;
608 	default:
609 		(void) close(fi);
610 		syslog(LOG_ERR, "%s: illegal format character '%c'",
611 			printer, format);
612 		return(ERROR);
613 	}
614 	if (prog == NULL) {
615 		(void) close(fi);
616 		syslog(LOG_ERR,
617 		   "%s: no filter found in printcap for format character '%c'",
618 		   printer, format);
619 		return(ERROR);
620 	}
621 	if ((av[0] = rindex(prog, '/')) != NULL)
622 		av[0]++;
623 	else
624 		av[0] = prog;
625 	av[n++] = "-n";
626 	av[n++] = logname;
627 	av[n++] = "-h";
628 	av[n++] = fromhost;
629 	av[n++] = AF;
630 	av[n] = 0;
631 	fo = pfd;
632 	if (ofilter > 0) {		/* stop output filter */
633 		write(ofd, "\031\1", 2);
634 		while ((pid =
635 		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
636 			;
637 		if (status.w_stopval != WSTOPPED) {
638 			(void) close(fi);
639 			syslog(LOG_WARNING,
640 				"%s: output filter died (retcode=%d termsig=%d)",
641 				printer, status.w_retcode, status.w_termsig);
642 			return(REPRINT);
643 		}
644 		stopped++;
645 	}
646 start:
647 	if ((child = dofork(DORETURN)) == 0) {	/* child */
648 		dup2(fi, 0);
649 		dup2(fo, 1);
650 		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
651 		if (n >= 0)
652 			dup2(n, 2);
653 		closelog();
654 		for (n = 3; n < NOFILE; n++)
655 			(void) close(n);
656 		execv(prog, av);
657 		syslog(LOG_ERR, "cannot execv %s", prog);
658 		exit(2);
659 	}
660 	(void) close(fi);
661 	if (child < 0)
662 		status.w_retcode = 100;
663 	else
664 		while ((pid = wait((int *)&status)) > 0 && pid != child)
665 			;
666 	child = 0;
667 	prchild = 0;
668 	if (stopped) {		/* restart output filter */
669 		if (kill(ofilter, SIGCONT) < 0) {
670 			syslog(LOG_ERR, "cannot restart output filter");
671 			exit(1);
672 		}
673 	}
674 	tof = 0;
675 
676 	/* Copy filter output to "lf" logfile */
677 	if (fp = fopen(tempfile, "r")) {
678 		while (fgets(buf, sizeof(buf), fp))
679 			fputs(buf, stderr);
680 		fclose(fp);
681 	}
682 
683 	if (!WIFEXITED(status)) {
684 		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
685 			printer, format, status.w_termsig);
686 		return(ERROR);
687 	}
688 	switch (status.w_retcode) {
689 	case 0:
690 		tof = 1;
691 		return(OK);
692 	case 1:
693 		return(REPRINT);
694 	case 2:
695 		return(ERROR);
696 	default:
697 		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
698 			printer, format, status.w_retcode);
699 		return(FILTERERR);
700 	}
701 }
702 
703 /*
704  * Send the daemon control file (cf) and any data files.
705  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
706  * 0 if all is well.
707  */
708 static int
709 sendit(file)
710 	char *file;
711 {
712 	register int i, err = OK;
713 	char *cp, last[BUFSIZ];
714 
715 	/*
716 	 * open control file
717 	 */
718 	if ((cfp = fopen(file, "r")) == NULL)
719 		return(OK);
720 	/*
721 	 *      read the control file for work to do
722 	 *
723 	 *      file format -- first character in the line is a command
724 	 *      rest of the line is the argument.
725 	 *      commands of interest are:
726 	 *
727 	 *            a-z -- "file name" name of file to print
728 	 *              U -- "unlink" name of file to remove
729 	 *                    (after we print it. (Pass 2 only)).
730 	 */
731 
732 	/*
733 	 * pass 1
734 	 */
735 	while (getline(cfp)) {
736 	again:
737 		if (line[0] == 'S') {
738 			cp = line+1;
739 			i = 0;
740 			while (*cp >= '0' && *cp <= '9')
741 				i = i * 10 + (*cp++ - '0');
742 			fdev = i;
743 			cp++;
744 			i = 0;
745 			while (*cp >= '0' && *cp <= '9')
746 				i = i * 10 + (*cp++ - '0');
747 			fino = i;
748 			continue;
749 		}
750 		if (line[0] >= 'a' && line[0] <= 'z') {
751 			strcpy(last, line);
752 			while (i = getline(cfp))
753 				if (strcmp(last, line))
754 					break;
755 			switch (sendfile('\3', last+1)) {
756 			case OK:
757 				if (i)
758 					goto again;
759 				break;
760 			case REPRINT:
761 				(void) fclose(cfp);
762 				return(REPRINT);
763 			case ACCESS:
764 				sendmail(logname, ACCESS);
765 			case ERROR:
766 				err = ERROR;
767 			}
768 			break;
769 		}
770 	}
771 	if (err == OK && sendfile('\2', file) > 0) {
772 		(void) fclose(cfp);
773 		return(REPRINT);
774 	}
775 	/*
776 	 * pass 2
777 	 */
778 	fseek(cfp, 0L, 0);
779 	while (getline(cfp))
780 		if (line[0] == 'U')
781 			(void) unlink(line+1);
782 	/*
783 	 * clean-up in case another control file exists
784 	 */
785 	(void) fclose(cfp);
786 	(void) unlink(file);
787 	return(err);
788 }
789 
790 /*
791  * Send a data file to the remote machine and spool it.
792  * Return positive if we should try resending.
793  */
794 static int
795 sendfile(type, file)
796 	int type;
797 	char *file;
798 {
799 	register int f, i, amt;
800 	struct stat stb;
801 	char buf[BUFSIZ];
802 	int sizerr, resp;
803 
804 	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
805 		return(ERROR);
806 	/*
807 	 * Check to see if data file is a symbolic link. If so, it should
808 	 * still point to the same file or someone is trying to print something
809 	 * he shouldn't.
810 	 */
811 	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
812 	    (stb.st_dev != fdev || stb.st_ino != fino))
813 		return(ACCESS);
814 	(void) sprintf(buf, "%c%ld %s\n", type, (long)stb.st_size, file);
815 	amt = strlen(buf);
816 	for (i = 0;  ; i++) {
817 		if (write(pfd, buf, amt) != amt ||
818 		    (resp = response()) < 0 || resp == '\1') {
819 			(void) close(f);
820 			return(REPRINT);
821 		} else if (resp == '\0')
822 			break;
823 		if (i == 0)
824 			pstatus("no space on remote; waiting for queue to drain");
825 		if (i == 10)
826 			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
827 				printer, RM);
828 		sleep(5 * 60);
829 	}
830 	if (i)
831 		pstatus("sending to %s", RM);
832 	sizerr = 0;
833 	for (i = 0; i < stb.st_size; i += BUFSIZ) {
834 		amt = BUFSIZ;
835 		if (i + amt > stb.st_size)
836 			amt = stb.st_size - i;
837 		if (sizerr == 0 && read(f, buf, amt) != amt)
838 			sizerr = 1;
839 		if (write(pfd, buf, amt) != amt) {
840 			(void) close(f);
841 			return(REPRINT);
842 		}
843 	}
844 
845 
846 
847 
848 	(void) close(f);
849 	if (sizerr) {
850 		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
851 		/* tell recvjob to ignore this file */
852 		(void) write(pfd, "\1", 1);
853 		return(ERROR);
854 	}
855 	if (write(pfd, "", 1) != 1 || response())
856 		return(REPRINT);
857 	return(OK);
858 }
859 
860 /*
861  * Check to make sure there have been no errors and that both programs
862  * are in sync with eachother.
863  * Return non-zero if the connection was lost.
864  */
865 static char
866 response()
867 {
868 	char resp;
869 
870 	if (read(pfd, &resp, 1) != 1) {
871 		syslog(LOG_INFO, "%s: lost connection", printer);
872 		return(-1);
873 	}
874 	return(resp);
875 }
876 
877 /*
878  * Banner printing stuff
879  */
880 static void
881 banner(name1, name2)
882 	char *name1, *name2;
883 {
884 	time_t tvec;
885 	extern char *ctime();
886 
887 	time(&tvec);
888 	if (!SF && !tof)
889 		(void) write(ofd, FF, strlen(FF));
890 	if (SB) {	/* short banner only */
891 		if (class[0]) {
892 			(void) write(ofd, class, strlen(class));
893 			(void) write(ofd, ":", 1);
894 		}
895 		(void) write(ofd, name1, strlen(name1));
896 		(void) write(ofd, "  Job: ", 7);
897 		(void) write(ofd, name2, strlen(name2));
898 		(void) write(ofd, "  Date: ", 8);
899 		(void) write(ofd, ctime(&tvec), 24);
900 		(void) write(ofd, "\n", 1);
901 	} else {	/* normal banner */
902 		(void) write(ofd, "\n\n\n", 3);
903 		scan_out(ofd, name1, '\0');
904 		(void) write(ofd, "\n\n", 2);
905 		scan_out(ofd, name2, '\0');
906 		if (class[0]) {
907 			(void) write(ofd,"\n\n\n",3);
908 			scan_out(ofd, class, '\0');
909 		}
910 		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
911 		(void) write(ofd, name2, strlen(name2));
912 		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
913 		(void) write(ofd, ctime(&tvec), 24);
914 		(void) write(ofd, "\n", 1);
915 	}
916 	if (!SF)
917 		(void) write(ofd, FF, strlen(FF));
918 	tof = 1;
919 }
920 
921 static char *
922 scnline(key, p, c)
923 	register int key;
924 	register char *p;
925 	int c;
926 {
927 	register scnwidth;
928 
929 	for (scnwidth = WIDTH; --scnwidth;) {
930 		key <<= 1;
931 		*p++ = key & 0200 ? c : BACKGND;
932 	}
933 	return (p);
934 }
935 
936 #define TRC(q)	(((q)-' ')&0177)
937 
938 static void
939 scan_out(scfd, scsp, dlm)
940 	int scfd, dlm;
941 	char *scsp;
942 {
943 	register char *strp;
944 	register nchrs, j;
945 	char outbuf[LINELEN+1], *sp, c, cc;
946 	int d, scnhgt;
947 	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
948 
949 	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
950 		strp = &outbuf[0];
951 		sp = scsp;
952 		for (nchrs = 0; ; ) {
953 			d = dropit(c = TRC(cc = *sp++));
954 			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
955 				for (j = WIDTH; --j;)
956 					*strp++ = BACKGND;
957 			else
958 				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
959 			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
960 				break;
961 			*strp++ = BACKGND;
962 			*strp++ = BACKGND;
963 		}
964 		while (*--strp == BACKGND && strp >= outbuf)
965 			;
966 		strp++;
967 		*strp++ = '\n';
968 		(void) write(scfd, outbuf, strp-outbuf);
969 	}
970 }
971 
972 static int
973 dropit(c)
974 	int c;
975 {
976 	switch(c) {
977 
978 	case TRC('_'):
979 	case TRC(';'):
980 	case TRC(','):
981 	case TRC('g'):
982 	case TRC('j'):
983 	case TRC('p'):
984 	case TRC('q'):
985 	case TRC('y'):
986 		return (DROP);
987 
988 	default:
989 		return (0);
990 	}
991 }
992 
993 /*
994  * sendmail ---
995  *   tell people about job completion
996  */
997 static void
998 sendmail(user, bombed)
999 	char *user;
1000 	int bombed;
1001 {
1002 	register int i;
1003 	int p[2], s;
1004 	register char *cp;
1005 	char buf[100];
1006 	struct stat stb;
1007 	FILE *fp;
1008 
1009 	pipe(p);
1010 	if ((s = dofork(DORETURN)) == 0) {		/* child */
1011 		dup2(p[0], 0);
1012 		closelog();
1013 		for (i = 3; i < NOFILE; i++)
1014 			(void) close(i);
1015 		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
1016 			cp++;
1017 	else
1018 			cp = _PATH_SENDMAIL;
1019 		sprintf(buf, "%s@%s", user, fromhost);
1020 		execl(_PATH_SENDMAIL, cp, buf, 0);
1021 		exit(0);
1022 	} else if (s > 0) {				/* parent */
1023 		dup2(p[1], 1);
1024 		printf("To: %s@%s\n", user, fromhost);
1025 		printf("Subject: %s printer job \"%s\"\n", printer,
1026 			*jobname ? jobname : "<unknown>");
1027 		printf("Reply-To: root@%s\n\n", host);
1028 		printf("Your printer job ");
1029 		if (*jobname)
1030 			printf("(%s) ", jobname);
1031 		switch (bombed) {
1032 		case OK:
1033 			printf("\ncompleted successfully\n");
1034 			cp = "OK";
1035 			break;
1036 		default:
1037 		case FATALERR:
1038 			printf("\ncould not be printed\n");
1039 			cp = "FATALERR";
1040 			break;
1041 		case NOACCT:
1042 			printf("\ncould not be printed without an account on %s\n", host);
1043 			cp = "NOACCT";
1044 			break;
1045 		case FILTERERR:
1046 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
1047 			    (fp = fopen(tempfile, "r")) == NULL) {
1048 				printf("\nhad some errors and may not have printed\n");
1049 				break;
1050 			}
1051 			printf("\nhad the following errors and may not have printed:\n");
1052 			while ((i = getc(fp)) != EOF)
1053 				putchar(i);
1054 			(void) fclose(fp);
1055 			cp = "FILTERERR";
1056 			break;
1057 		case ACCESS:
1058 			printf("\nwas not printed because it was not linked to the original file\n");
1059 			cp = "ACCESS";
1060 		}
1061 		fflush(stdout);
1062 		(void) close(1);
1063 	}
1064 	(void) close(p[0]);
1065 	(void) close(p[1]);
1066 	wait(&s);
1067 	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
1068 		user, *jobname ? jobname : "<unknown>", printer, cp);
1069 }
1070 
1071 /*
1072  * dofork - fork with retries on failure
1073  */
1074 static int
1075 dofork(action)
1076 	int action;
1077 {
1078 	register int i, pid;
1079 
1080 	for (i = 0; i < 20; i++) {
1081 		if ((pid = fork()) < 0) {
1082 			sleep((unsigned)(i*i));
1083 			continue;
1084 		}
1085 		/*
1086 		 * Child should run as daemon instead of root
1087 		 */
1088 		if (pid == 0)
1089 			setuid(DU);
1090 		return(pid);
1091 	}
1092 	syslog(LOG_ERR, "can't fork");
1093 
1094 	switch (action) {
1095 	case DORETURN:
1096 		return (-1);
1097 	default:
1098 		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1099 		/*FALL THRU*/
1100 	case DOABORT:
1101 		exit(1);
1102 	}
1103 	/*NOTREACHED*/
1104 }
1105 
1106 /*
1107  * Kill child processes to abort current job.
1108  */
1109 static void
1110 abortpr(signo)
1111 	int signo;
1112 {
1113 	(void) unlink(tempfile);
1114 	kill(0, SIGINT);
1115 	if (ofilter > 0)
1116 		kill(ofilter, SIGCONT);
1117 	while (wait(NULL) > 0)
1118 		;
1119 	exit(0);
1120 }
1121 
1122 static void
1123 init()
1124 {
1125 	int status;
1126 	char *s;
1127 
1128 	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
1129 		syslog(LOG_ERR, "can't open printer description file");
1130 		exit(1);
1131 	} else if (status == -1) {
1132 		syslog(LOG_ERR, "unknown printer: %s", printer);
1133 		exit(1);
1134 	} else if (status == -3)
1135 		fatal("potential reference loop detected in printcap file");
1136 
1137 	if (cgetstr(bp, "lp", &LP) == -1)
1138 		LP = _PATH_DEFDEVLP;
1139 	if (cgetstr(bp, "rp", &RP) == -1)
1140 		RP = DEFLP;
1141 	if (cgetstr(bp, "lo", &LO) == -1)
1142 		LO = DEFLOCK;
1143 	if (cgetstr(bp, "st", &ST) == -1)
1144 		ST = DEFSTAT;
1145 	if (cgetstr(bp, "lf", &LF) == -1)
1146 		LF = _PATH_CONSOLE;
1147 	if (cgetstr(bp, "sd", &SD) == -1)
1148 		SD = _PATH_DEFSPOOL;
1149 	if (cgetnum(bp, "du", &DU) < 0)
1150 		DU = DEFUID;
1151 	if (cgetstr(bp,"ff", &FF) == -1)
1152 		FF = DEFFF;
1153 	if (cgetnum(bp, "pw", &PW) < 0)
1154 		PW = DEFWIDTH;
1155 	sprintf(&width[2], "%d", PW);
1156 	if (cgetnum(bp, "pl", &PL) < 0)
1157 		PL = DEFLENGTH;
1158 	sprintf(&length[2], "%d", PL);
1159 	if (cgetnum(bp,"px", &PX) < 0)
1160 		PX = 0;
1161 	sprintf(&pxwidth[2], "%d", PX);
1162 	if (cgetnum(bp, "py", &PY) < 0)
1163 		PY = 0;
1164 	sprintf(&pxlength[2], "%d", PY);
1165 	cgetstr(bp, "rm", &RM);
1166 	if (s = checkremote())
1167 		syslog(LOG_WARNING, s);
1168 
1169 	cgetstr(bp, "af", &AF);
1170 	cgetstr(bp, "of", &OF);
1171 	cgetstr(bp, "if", &IF);
1172 	cgetstr(bp, "rf", &RF);
1173 	cgetstr(bp, "tf", &TF);
1174 	cgetstr(bp, "nf", &NF);
1175 	cgetstr(bp, "df", &DF);
1176 	cgetstr(bp, "gf", &GF);
1177 	cgetstr(bp, "vf", &VF);
1178 	cgetstr(bp, "cf", &CF);
1179 	cgetstr(bp, "tr", &TR);
1180 
1181 	RS = (cgetcap(bp, "rs", ':') != NULL);
1182 	SF = (cgetcap(bp, "sf", ':') != NULL);
1183 	SH = (cgetcap(bp, "sh", ':') != NULL);
1184 	SB = (cgetcap(bp, "sb", ':') != NULL);
1185 	HL = (cgetcap(bp, "hl", ':') != NULL);
1186 	RW = (cgetcap(bp, "rw", ':') != NULL);
1187 
1188 	cgetnum(bp, "br", &BR);
1189 	if (cgetnum(bp, "fc", &FC) < 0)
1190 		FC = 0;
1191 	if (cgetnum(bp, "fs", &FS) < 0)
1192 		FS = 0;
1193 	if (cgetnum(bp, "xc", &XC) < 0)
1194 		XC = 0;
1195 	if (cgetnum(bp, "xs", &XS) < 0)
1196 		XS = 0;
1197 
1198 	tof = (cgetcap(bp, "fo", ':') == NULL);
1199 }
1200 
1201 /*
1202  * Acquire line printer or remote connection.
1203  */
1204 static void
1205 openpr()
1206 {
1207 	register int i;
1208 	char *cp;
1209 
1210 	if (!remote && *LP) {
1211 		if (cp = index(LP, '@'))
1212 			opennet(cp);
1213 		else
1214 			opentty();
1215 	} else if (remote) {
1216 		openrem();
1217 	} else {
1218 		syslog(LOG_ERR, "%s: no line printer device or host name",
1219 			printer);
1220 		exit(1);
1221 	}
1222 
1223 	/*
1224 	 * Start up an output filter, if needed.
1225 	 */
1226 	if (!remote && OF) {
1227 		int p[2];
1228 
1229 		pipe(p);
1230 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
1231 			dup2(p[0], 0);		/* pipe is std in */
1232 			dup2(pfd, 1);		/* printer is std out */
1233 			closelog();
1234 			for (i = 3; i < NOFILE; i++)
1235 				(void) close(i);
1236 			if ((cp = rindex(OF, '/')) == NULL)
1237 				cp = OF;
1238 			else
1239 				cp++;
1240 			execl(OF, cp, width, length, 0);
1241 			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1242 			exit(1);
1243 		}
1244 		(void) close(p[0]);		/* close input side */
1245 		ofd = p[1];			/* use pipe for output */
1246 	} else {
1247 		ofd = pfd;
1248 		ofilter = 0;
1249 	}
1250 }
1251 
1252 /*
1253  * Printer connected directly to the network
1254  * or to a terminal server on the net
1255  */
1256 static void
1257 opennet(cp)
1258 	char *cp;
1259 {
1260 	register int i;
1261 	int resp, port;
1262 
1263 	*cp++ = '\0';
1264 	port = atoi(cp);
1265 	if (port <= 0) {
1266 		syslog(LOG_ERR, "%s: bad port number: %s", printer, cp);
1267 		exit(1);
1268 	}
1269 
1270 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1271 		resp = -1;
1272 		pfd = getport(LP, port);
1273 		if (pfd < 0 && errno == ECONNREFUSED)
1274 			resp = 1;
1275 		else if (pfd >= 0) {
1276 			/*
1277 			 * need to delay a bit for rs232 lines
1278 			 * to stabilize in case printer is
1279 			 * connected via a terminal server
1280 			 */
1281 			delay(500);
1282 			break;
1283 		}
1284 		if (i == 1) {
1285 		   if (resp < 0)
1286 			pstatus("waiting for %s to come up", LP);
1287 		   else
1288 			pstatus("waiting for access to printer on %s", LP);
1289 		}
1290 		sleep(i);
1291 	}
1292 	pstatus("sending to %s port %d", LP, port);
1293 	*(--cp) = '@';	/* restore LP parameter in case we are called again */
1294 }
1295 
1296 /*
1297  * Printer is connected to an RS232 port on this host
1298  */
1299 static void
1300 opentty()
1301 {
1302 	register int i;
1303 	int resp, port;
1304 
1305 	for (i = 1; ; i = i < 32 ? i << 1 : i) {
1306 		pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1307 		if (pfd >= 0) {
1308 			delay(500);
1309 			break;
1310 		}
1311 		if (errno == ENOENT) {
1312 			syslog(LOG_ERR, "%s: %m", LP);
1313 			exit(1);
1314 		}
1315 		if (i == 1)
1316 			pstatus("waiting for %s to become ready (offline ?)",
1317 				printer);
1318 		sleep(i);
1319 	}
1320 	if (isatty(pfd))
1321 		setty();
1322 	pstatus("%s is ready and printing", printer);
1323 }
1324 
1325 /*
1326  * Printer is on a remote host
1327  */
1328 static void
1329 openrem()
1330 {
1331 	register int i, n;
1332 	int resp, port;
1333 
1334 	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1335 		resp = -1;
1336 		pfd = getport(RM, 0);
1337 		if (pfd >= 0) {
1338 			(void) sprintf(line, "\2%s\n", RP);
1339 			n = strlen(line);
1340 			if (write(pfd, line, n) == n &&
1341 			    (resp = response()) == '\0')
1342 				break;
1343 			(void) close(pfd);
1344 		}
1345 		if (i == 1) {
1346 			if (resp < 0)
1347 				pstatus("waiting for %s to come up", RM);
1348 			else {
1349 				pstatus("waiting for queue to be enabled on %s",
1350 					RM);
1351 				i = 256;
1352 			}
1353 		}
1354 		sleep(i);
1355 	}
1356 	pstatus("sending to %s", RM);
1357 }
1358 
1359 struct bauds {
1360 	int	baud;
1361 	int	speed;
1362 } bauds[] = {
1363 	50,	B50,
1364 	75,	B75,
1365 	110,	B110,
1366 	134,	B134,
1367 	150,	B150,
1368 	200,	B200,
1369 	300,	B300,
1370 	600,	B600,
1371 	1200,	B1200,
1372 	1800,	B1800,
1373 	2400,	B2400,
1374 	4800,	B4800,
1375 	9600,	B9600,
1376 	19200,	EXTA,
1377 	38400,	EXTB,
1378 	0,	0
1379 };
1380 
1381 /*
1382  * setup tty lines.
1383  */
1384 static void
1385 setty()
1386 {
1387 	struct sgttyb ttybuf;
1388 	register struct bauds *bp;
1389 
1390 	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1391 		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1392 		exit(1);
1393 	}
1394 	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1395 		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
1396 		exit(1);
1397 	}
1398 	if (BR > 0) {
1399 		for (bp = bauds; bp->baud; bp++)
1400 			if (BR == bp->baud)
1401 				break;
1402 		if (!bp->baud) {
1403 			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1404 			exit(1);
1405 		}
1406 		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1407 	}
1408 	ttybuf.sg_flags &= ~FC;
1409 	ttybuf.sg_flags |= FS;
1410 	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1411 		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
1412 		exit(1);
1413 	}
1414 	if (XC) {
1415 		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1416 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
1417 			exit(1);
1418 		}
1419 	}
1420 	if (XS) {
1421 		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1422 			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
1423 			exit(1);
1424 		}
1425 	}
1426 }
1427 
1428 #if __STDC__
1429 #include <stdarg.h>
1430 #else
1431 #include <varargs.h>
1432 #endif
1433 
1434 void
1435 #if __STDC__
1436 pstatus(const char *msg, ...)
1437 #else
1438 pstatus(msg, va_alist)
1439 	char *msg;
1440         va_dcl
1441 #endif
1442 {
1443 	register int fd;
1444 	char buf[BUFSIZ];
1445 	va_list ap;
1446 #if __STDC__
1447 	va_start(ap, msg);
1448 #else
1449 	va_start(ap);
1450 #endif
1451 
1452 	umask(0);
1453 	fd = open(ST, O_WRONLY|O_CREAT, 0664);
1454 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1455 		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1456 		exit(1);
1457 	}
1458 	ftruncate(fd, 0);
1459 	(void)vsnprintf(buf, sizeof(buf), msg, ap);
1460 	va_end(ap);
1461 	strcat(buf, "\n");
1462 	(void) write(fd, buf, strlen(buf));
1463 	(void) close(fd);
1464 }
1465