printjob.c revision 78146
1/*
2 * Copyright (c) 1983, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by the University of
17 *	California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36static const char copyright[] =
37"@(#) Copyright (c) 1983, 1993\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42/*
43static char sccsid[] = "@(#)printjob.c	8.7 (Berkeley) 5/10/95";
44*/
45static const char rcsid[] =
46  "$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 78146 2001-06-12 16:38:20Z gad $";
47#endif /* not lint */
48
49
50/*
51 * printjob -- print jobs in the queue.
52 *
53 *	NOTE: the lock file is used to pass information to lpq and lprm.
54 *	it does not need to be removed because file locks are dynamic.
55 */
56
57#include <sys/param.h>
58#include <sys/wait.h>
59#include <sys/stat.h>
60#include <sys/types.h>
61
62#include <pwd.h>
63#include <unistd.h>
64#include <signal.h>
65#include <syslog.h>
66#include <fcntl.h>
67#include <dirent.h>
68#include <errno.h>
69#include <stdio.h>
70#include <string.h>
71#include <stdlib.h>
72#include <sys/ioctl.h>
73#include <termios.h>
74#include <time.h>
75#include "lp.h"
76#include "lp.local.h"
77#include "pathnames.h"
78#include "extern.h"
79
80#define DORETURN	0	/* absorb fork error */
81#define DOABORT		1	/* abort if dofork fails */
82
83/*
84 * Error tokens
85 */
86#define REPRINT		-2
87#define ERROR		-1
88#define	OK		0
89#define	FATALERR	1
90#define	NOACCT		2
91#define	FILTERERR	3
92#define	ACCESS		4
93
94static dev_t	 fdev;		/* device of file pointed to by symlink */
95static ino_t	 fino;		/* inode of file pointed to by symlink */
96static FILE	*cfp;		/* control file */
97static int	 child;		/* id of any filters */
98static int	 job_dfcnt;	/* count of datafiles in current user job */
99static int	 lfd;		/* lock file descriptor */
100static int	 ofd;		/* output filter file descriptor */
101static int	 ofilter;	/* id of output filter, if any */
102static int	 tfd = -1;	/* output filter temp file output */
103static int	 pfd;		/* prstatic inter file descriptor */
104static int	 pid;		/* pid of lpd process */
105static int	 prchild;	/* id of pr process */
106static char	 title[80];	/* ``pr'' title */
107static char      locale[80];    /* ``pr'' locale */
108
109static char	class[32];		/* classification field */
110static char	fromhost[MAXHOSTNAMELEN];	/* user's host machine */
111				/* indentation size in static characters */
112static char	indent[10] = "-i0";
113static char	jobname[100];		/* job or file name */
114static char	length[10] = "-l";	/* page length in lines */
115static char	logname[32];		/* user's login name */
116static char	pxlength[10] = "-y";	/* page length in pixels */
117static char	pxwidth[10] = "-x";	/* page width in pixels */
118/* tempstderr is the filename used to catch stderr from exec-ing filters */
119static char	tempstderr[] = "errs.XXXXXXX";
120static char	width[10] = "-w";	/* page width in static characters */
121#define TFILENAME "fltXXXXXX"
122static char	tfile[] = TFILENAME;	/* file name for filter output */
123
124static void	 abortpr(int _signo);
125static void	 alarmhandler(int _signo);
126static void	 banner(struct printer *_pp, char *_name1, char *_name2);
127static int	 dofork(const struct printer *_pp, int _action);
128static int	 dropit(int _c);
129static void	 init(struct printer *_pp);
130static void	 openpr(const struct printer *_pp);
131static void	 opennet(const struct printer *_pp);
132static void	 opentty(const struct printer *_pp);
133static void	 openrem(const struct printer *pp);
134static int	 print(struct printer *_pp, int _format, char *_file);
135static int	 printit(struct printer *_pp, char *_file);
136static void	 pstatus(const struct printer *_pp, const char *_msg, ...);
137static char	 response(const struct printer *_pp);
138static void	 scan_out(struct printer *_pp, int _scfd, char *_scsp,
139		    int _dlm);
140static char	*scnline(int _key, char *_p, int _c);
141static int	 sendfile(struct printer *_pp, int _type, char *_file,
142		    char _format);
143static int	 sendit(struct printer *_pp, char *_file);
144static void	 sendmail(struct printer *_pp, char *_user, int _bombed);
145static void	 setty(const struct printer *_pp);
146
147void
148printjob(struct printer *pp)
149{
150	struct stat stb;
151	register struct jobqueue *q, **qp;
152	struct jobqueue **queue;
153	register int i, nitems;
154	off_t pidoff;
155	int errcnt, jobcount, tempfd;
156
157	jobcount = 0;
158	init(pp); /* set up capabilities */
159	(void) write(1, "", 1);	/* ack that daemon is started */
160	(void) close(2);			/* set up log file */
161	if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) {
162		syslog(LOG_ERR, "%s: %m", pp->log_file);
163		(void) open(_PATH_DEVNULL, O_WRONLY);
164	}
165	setgid(getegid());
166	pid = getpid();				/* for use with lprm */
167	setpgrp(0, pid);
168	signal(SIGHUP, abortpr);
169	signal(SIGINT, abortpr);
170	signal(SIGQUIT, abortpr);
171	signal(SIGTERM, abortpr);
172
173	/*
174	 * uses short form file names
175	 */
176	if (chdir(pp->spool_dir) < 0) {
177		syslog(LOG_ERR, "%s: %m", pp->spool_dir);
178		exit(1);
179	}
180	if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS))
181		exit(0);		/* printing disabled */
182	lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
183		   LOCK_FILE_MODE);
184	if (lfd < 0) {
185		if (errno == EWOULDBLOCK)	/* active daemon present */
186			exit(0);
187		syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file);
188		exit(1);
189	}
190	/* turn off non-blocking mode (was turned on for lock effects only) */
191	if (fcntl(lfd, F_SETFL, 0) < 0) {
192		syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file);
193		exit(1);
194	}
195	ftruncate(lfd, 0);
196	/*
197	 * write process id for others to know
198	 */
199	sprintf(line, "%u\n", pid);
200	pidoff = i = strlen(line);
201	if (write(lfd, line, i) != i) {
202		syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->lock_file);
203		exit(1);
204	}
205	/*
206	 * search the spool directory for work and sort by queue order.
207	 */
208	if ((nitems = getq(pp, &queue)) < 0) {
209		syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
210		       pp->spool_dir);
211		exit(1);
212	}
213	if (nitems == 0)		/* no work to do */
214		exit(0);
215	if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */
216		if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0)
217			syslog(LOG_ERR, "%s: %s: %m", pp->printer,
218			       pp->lock_file);
219	}
220
221	/* create a file which will be used to hold stderr from filters */
222	if ((tempfd = mkstemp(tempstderr)) == -1) {
223		syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
224		       tempstderr);
225		exit(1);
226	}
227	if ((i = fchmod(tempfd, 0664)) == -1) {
228		syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
229		       tempstderr);
230		exit(1);
231	}
232	/* lpd doesn't need it to be open, it just needs it to exist */
233	close(tempfd);
234
235	openpr(pp);			/* open printer or remote */
236again:
237	/*
238	 * we found something to do now do it --
239	 *    write the name of the current control file into the lock file
240	 *    so the spool queue program can tell what we're working on
241	 */
242	for (qp = queue; nitems--; free((char *) q)) {
243		q = *qp++;
244		if (stat(q->job_cfname, &stb) < 0)
245			continue;
246		errcnt = 0;
247	restart:
248		(void) lseek(lfd, pidoff, 0);
249		(void) snprintf(line, sizeof(line), "%s\n", q->job_cfname);
250		i = strlen(line);
251		if (write(lfd, line, i) != i)
252			syslog(LOG_ERR, "%s: %s: %m", pp->printer,
253			       pp->lock_file);
254		if (!pp->remote)
255			i = printit(pp, q->job_cfname);
256		else
257			i = sendit(pp, q->job_cfname);
258		/*
259		 * Check to see if we are supposed to stop printing or
260		 * if we are to rebuild the queue.
261		 */
262		if (fstat(lfd, &stb) == 0) {
263			/* stop printing before starting next job? */
264			if (stb.st_mode & LFM_PRINT_DIS)
265				goto done;
266			/* rebuild queue (after lpc topq) */
267			if (stb.st_mode & LFM_RESET_QUE) {
268				for (free(q); nitems--; free(q))
269					q = *qp++;
270				if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE)
271				    < 0)
272					syslog(LOG_WARNING, "%s: %s: %m",
273					       pp->printer, pp->lock_file);
274				break;
275			}
276		}
277		if (i == OK)		/* all files of this job printed */
278			jobcount++;
279		else if (i == REPRINT && ++errcnt < 5) {
280			/* try reprinting the job */
281			syslog(LOG_INFO, "restarting %s", pp->printer);
282			if (ofilter > 0) {
283				kill(ofilter, SIGCONT);	/* to be sure */
284				(void) close(ofd);
285				while ((i = wait(NULL)) > 0 && i != ofilter)
286					;
287				ofilter = 0;
288			}
289			(void) close(pfd);	/* close printer */
290			if (ftruncate(lfd, pidoff) < 0)
291				syslog(LOG_WARNING, "%s: %s: %m",
292				       pp->printer, pp->lock_file);
293			openpr(pp);		/* try to reopen printer */
294			goto restart;
295		} else {
296			syslog(LOG_WARNING, "%s: job could not be %s (%s)",
297			       pp->printer,
298			       pp->remote ? "sent to remote host" : "printed",
299			       q->job_cfname);
300			if (i == REPRINT) {
301				/* ensure we don't attempt this job again */
302				(void) unlink(q->job_cfname);
303				q->job_cfname[0] = 'd';
304				(void) unlink(q->job_cfname);
305				if (logname[0])
306					sendmail(pp, logname, FATALERR);
307			}
308		}
309	}
310	free(queue);
311	/*
312	 * search the spool directory for more work.
313	 */
314	if ((nitems = getq(pp, &queue)) < 0) {
315		syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
316		       pp->spool_dir);
317		exit(1);
318	}
319	if (nitems == 0) {		/* no more work to do */
320	done:
321		if (jobcount > 0) {	/* jobs actually printed */
322			if (!pp->no_formfeed && !pp->tof)
323				(void) write(ofd, pp->form_feed,
324					     strlen(pp->form_feed));
325			if (pp->trailer != NULL) /* output trailer */
326				(void) write(ofd, pp->trailer,
327					     strlen(pp->trailer));
328		}
329		(void) close(ofd);
330		(void) wait(NULL);
331		(void) unlink(tempstderr);
332		exit(0);
333	}
334	goto again;
335}
336
337char	fonts[4][50];	/* fonts for troff */
338
339char ifonts[4][40] = {
340	_PATH_VFONTR,
341	_PATH_VFONTI,
342	_PATH_VFONTB,
343	_PATH_VFONTS,
344};
345
346/*
347 * The remaining part is the reading of the control file (cf)
348 * and performing the various actions.
349 */
350static int
351printit(struct printer *pp, char *file)
352{
353	register int i;
354	char *cp;
355	int bombed, didignorehdr;
356
357	bombed = OK;
358	didignorehdr = 0;
359	/*
360	 * open control file; ignore if no longer there.
361	 */
362	if ((cfp = fopen(file, "r")) == NULL) {
363		syslog(LOG_INFO, "%s: %s: %m", pp->printer, file);
364		return(OK);
365	}
366	/*
367	 * Reset troff fonts.
368	 */
369	for (i = 0; i < 4; i++)
370		strcpy(fonts[i], ifonts[i]);
371	sprintf(&width[2], "%ld", pp->page_width);
372	strcpy(indent+2, "0");
373
374	/* initialize job-specific count of datafiles processed */
375	job_dfcnt = 0;
376
377	/*
378	 *      read the control file for work to do
379	 *
380	 *      file format -- first character in the line is a command
381	 *      rest of the line is the argument.
382	 *      valid commands are:
383	 *
384	 *		S -- "stat info" for symbolic link protection
385	 *		J -- "job name" on banner page
386	 *		C -- "class name" on banner page
387	 *              L -- "literal" user's name to print on banner
388	 *		T -- "title" for pr
389	 *		H -- "host name" of machine where lpr was done
390	 *              P -- "person" user's login name
391	 *              I -- "indent" amount to indent output
392	 *		R -- laser dpi "resolution"
393	 *              f -- "file name" name of text file to print
394	 *		l -- "file name" text file with control chars
395	 *		p -- "file name" text file to print with pr(1)
396	 *		t -- "file name" troff(1) file to print
397	 *		n -- "file name" ditroff(1) file to print
398	 *		d -- "file name" dvi file to print
399	 *		g -- "file name" plot(1G) file to print
400	 *		v -- "file name" plain raster file to print
401	 *		c -- "file name" cifplot file to print
402	 *		1 -- "R font file" for troff
403	 *		2 -- "I font file" for troff
404	 *		3 -- "B font file" for troff
405	 *		4 -- "S font file" for troff
406	 *		N -- "name" of file (used by lpq)
407	 *              U -- "unlink" name of file to remove
408	 *                    (after we print it. (Pass 2 only)).
409	 *		M -- "mail" to user when done printing
410	 *              Z -- "locale" for pr
411	 *
412	 *      getline reads a line and expands tabs to blanks
413	 */
414
415	/* pass 1 */
416
417	while (getline(cfp))
418		switch (line[0]) {
419		case 'H':
420			strncpy(fromhost, line+1, sizeof(fromhost) - 1);
421			fromhost[sizeof(fromhost) - 1] = '\0';
422			if (class[0] == '\0') {
423				strncpy(class, line+1, sizeof(class) - 1);
424				class[sizeof(class) - 1] = '\0';
425			}
426			continue;
427
428		case 'P':
429			strncpy(logname, line+1, sizeof(logname) - 1);
430			logname[sizeof(logname) - 1] = '\0';
431			if (pp->restricted) { /* restricted */
432				if (getpwnam(logname) == NULL) {
433					bombed = NOACCT;
434					sendmail(pp, line+1, bombed);
435					goto pass2;
436				}
437			}
438			continue;
439
440		case 'S':
441			cp = line+1;
442			i = 0;
443			while (*cp >= '0' && *cp <= '9')
444				i = i * 10 + (*cp++ - '0');
445			fdev = i;
446			cp++;
447			i = 0;
448			while (*cp >= '0' && *cp <= '9')
449				i = i * 10 + (*cp++ - '0');
450			fino = i;
451			continue;
452
453		case 'J':
454			if (line[1] != '\0') {
455				strncpy(jobname, line+1, sizeof(jobname) - 1);
456				jobname[sizeof(jobname) - 1] = '\0';
457			} else
458				strcpy(jobname, " ");
459			continue;
460
461		case 'C':
462			if (line[1] != '\0')
463				strncpy(class, line+1, sizeof(class) - 1);
464			else if (class[0] == '\0')
465				gethostname(class, sizeof(class));
466			class[sizeof(class) - 1] = '\0';
467			continue;
468
469		case 'T':	/* header title for pr */
470			strncpy(title, line+1, sizeof(title) - 1);
471			title[sizeof(title) - 1] = '\0';
472			continue;
473
474		case 'L':	/* identification line */
475			if (!pp->no_header && !pp->header_last)
476				banner(pp, line+1, jobname);
477			continue;
478
479		case '1':	/* troff fonts */
480		case '2':
481		case '3':
482		case '4':
483			if (line[1] != '\0') {
484				strncpy(fonts[line[0]-'1'], line+1,
485				    50-1);
486				fonts[line[0]-'1'][50-1] = '\0';
487			}
488			continue;
489
490		case 'W':	/* page width */
491			strncpy(width+2, line+1, sizeof(width) - 3);
492			width[2+sizeof(width) - 3] = '\0';
493			continue;
494
495		case 'I':	/* indent amount */
496			strncpy(indent+2, line+1, sizeof(indent) - 3);
497			indent[2+sizeof(indent) - 3] = '\0';
498			continue;
499
500		case 'Z':       /* locale for pr */
501			strncpy(locale, line+1, sizeof(locale) - 1);
502			locale[sizeof(locale) - 1] = '\0';
503			continue;
504
505		default:	/* some file to print */
506			/* only lowercase cmd-codes include a file-to-print */
507			if ((line[0] < 'a') || (line[0] > 'z')) {
508				/* ignore any other lines */
509				if (lflag <= 1)
510					continue;
511				if (!didignorehdr) {
512					syslog(LOG_INFO, "%s: in %s :",
513					       pp->printer, file);
514					didignorehdr = 1;
515				}
516				syslog(LOG_INFO, "%s: ignoring line: '%c' %s",
517				       pp->printer, line[0], &line[1]);
518				continue;
519			}
520			i = print(pp, line[0], line+1);
521			switch (i) {
522			case ERROR:
523				if (bombed == OK)
524					bombed = FATALERR;
525				break;
526			case REPRINT:
527				(void) fclose(cfp);
528				return(REPRINT);
529			case FILTERERR:
530			case ACCESS:
531				bombed = i;
532				sendmail(pp, logname, bombed);
533			}
534			title[0] = '\0';
535			continue;
536
537		case 'N':
538		case 'U':
539		case 'M':
540		case 'R':
541			continue;
542		}
543
544	/* pass 2 */
545
546pass2:
547	fseek(cfp, 0L, 0);
548	while (getline(cfp))
549		switch (line[0]) {
550		case 'L':	/* identification line */
551			if (!pp->no_header && pp->header_last)
552				banner(pp, line+1, jobname);
553			continue;
554
555		case 'M':
556			if (bombed < NOACCT)	/* already sent if >= NOACCT */
557				sendmail(pp, line+1, bombed);
558			continue;
559
560		case 'U':
561			if (strchr(line+1, '/'))
562				continue;
563			(void) unlink(line+1);
564		}
565	/*
566	 * clean-up in case another control file exists
567	 */
568	(void) fclose(cfp);
569	(void) unlink(file);
570	return(bombed == OK ? OK : ERROR);
571}
572
573/*
574 * Print a file.
575 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
576 * Return -1 if a non-recoverable error occured,
577 * 2 if the filter detected some errors (but printed the job anyway),
578 * 1 if we should try to reprint this job and
579 * 0 if all is well.
580 * Note: all filters take stdin as the file, stdout as the printer,
581 * stderr as the log file, and must not ignore SIGINT.
582 */
583static int
584print(struct printer *pp, int format, char *file)
585{
586	register int n, i;
587	register char *prog;
588	int fi, fo;
589	FILE *fp;
590	char *av[15], buf[BUFSIZ];
591	int pid, p[2], stopped;
592	union wait status;
593	struct stat stb;
594
595	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) {
596		syslog(LOG_INFO, "%s: unable to open %s ('%c' line)",
597		       pp->printer, file, format);
598		return(ERROR);
599	}
600	/*
601	 * Check to see if data file is a symbolic link. If so, it should
602	 * still point to the same file or someone is trying to print
603	 * something he shouldn't.
604	 */
605	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
606	    (stb.st_dev != fdev || stb.st_ino != fino))
607		return(ACCESS);
608
609	job_dfcnt++;		/* increment datafile counter for this job */
610	stopped = 0;		/* output filter is not stopped */
611
612	/* everything seems OK, start it up */
613	if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */
614		(void) write(ofd, pp->form_feed, strlen(pp->form_feed));
615		pp->tof = 1;
616	}
617	if (pp->filters[LPF_INPUT] == NULL
618	    && (format == 'f' || format == 'l')) {
619		pp->tof = 0;
620		while ((n = read(fi, buf, BUFSIZ)) > 0)
621			if (write(ofd, buf, n) != n) {
622				(void) close(fi);
623				return(REPRINT);
624			}
625		(void) close(fi);
626		return(OK);
627	}
628	switch (format) {
629	case 'p':	/* print file using 'pr' */
630		if (pp->filters[LPF_INPUT] == NULL) {	/* use output filter */
631			prog = _PATH_PR;
632			i = 0;
633			av[i++] = "pr";
634			av[i++] = width;
635			av[i++] = length;
636			av[i++] = "-h";
637			av[i++] = *title ? title : " ";
638			av[i++] = "-L";
639			av[i++] = *locale ? locale : "C";
640			av[i++] = "-F";
641			av[i] = 0;
642			fo = ofd;
643			goto start;
644		}
645		pipe(p);
646		if ((prchild = dofork(pp, DORETURN)) == 0) {	/* child */
647			dup2(fi, 0);		/* file is stdin */
648			dup2(p[1], 1);		/* pipe is stdout */
649			closelog();
650			closeallfds(3);
651			execl(_PATH_PR, "pr", width, length,
652			    "-h", *title ? title : " ",
653			    "-L", *locale ? locale : "C",
654			    "-F", 0);
655			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
656			exit(2);
657		}
658		(void) close(p[1]);		/* close output side */
659		(void) close(fi);
660		if (prchild < 0) {
661			prchild = 0;
662			(void) close(p[0]);
663			return(ERROR);
664		}
665		fi = p[0];			/* use pipe for input */
666	case 'f':	/* print plain text file */
667		prog = pp->filters[LPF_INPUT];
668		av[1] = width;
669		av[2] = length;
670		av[3] = indent;
671		n = 4;
672		break;
673	case 'l':	/* like 'f' but pass control characters */
674		prog = pp->filters[LPF_INPUT];
675		av[1] = "-c";
676		av[2] = width;
677		av[3] = length;
678		av[4] = indent;
679		n = 5;
680		break;
681	case 'r':	/* print a fortran text file */
682		prog = pp->filters[LPF_FORTRAN];
683		av[1] = width;
684		av[2] = length;
685		n = 3;
686		break;
687	case 't':	/* print troff output */
688	case 'n':	/* print ditroff output */
689	case 'd':	/* print tex output */
690		(void) unlink(".railmag");
691		if ((fo = creat(".railmag", FILMOD)) < 0) {
692			syslog(LOG_ERR, "%s: cannot create .railmag",
693			       pp->printer);
694			(void) unlink(".railmag");
695		} else {
696			for (n = 0; n < 4; n++) {
697				if (fonts[n][0] != '/')
698					(void) write(fo, _PATH_VFONT,
699					    sizeof(_PATH_VFONT) - 1);
700				(void) write(fo, fonts[n], strlen(fonts[n]));
701				(void) write(fo, "\n", 1);
702			}
703			(void) close(fo);
704		}
705		prog = (format == 't') ? pp->filters[LPF_TROFF]
706			: ((format == 'n') ? pp->filters[LPF_DITROFF]
707			   : pp->filters[LPF_DVI]);
708		av[1] = pxwidth;
709		av[2] = pxlength;
710		n = 3;
711		break;
712	case 'c':	/* print cifplot output */
713		prog = pp->filters[LPF_CIFPLOT];
714		av[1] = pxwidth;
715		av[2] = pxlength;
716		n = 3;
717		break;
718	case 'g':	/* print plot(1G) output */
719		prog = pp->filters[LPF_GRAPH];
720		av[1] = pxwidth;
721		av[2] = pxlength;
722		n = 3;
723		break;
724	case 'v':	/* print raster output */
725		prog = pp->filters[LPF_RASTER];
726		av[1] = pxwidth;
727		av[2] = pxlength;
728		n = 3;
729		break;
730	default:
731		(void) close(fi);
732		syslog(LOG_ERR, "%s: illegal format character '%c'",
733			pp->printer, format);
734		return(ERROR);
735	}
736	if (prog == NULL) {
737		(void) close(fi);
738		syslog(LOG_ERR,
739		   "%s: no filter found in printcap for format character '%c'",
740		   pp->printer, format);
741		return(ERROR);
742	}
743	if ((av[0] = strrchr(prog, '/')) != NULL)
744		av[0]++;
745	else
746		av[0] = prog;
747	av[n++] = "-n";
748	av[n++] = logname;
749	av[n++] = "-h";
750	av[n++] = fromhost;
751	av[n++] = pp->acct_file;
752	av[n] = 0;
753	fo = pfd;
754	if (ofilter > 0) {		/* stop output filter */
755		write(ofd, "\031\1", 2);
756		while ((pid =
757		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
758			;
759		if (status.w_stopval != WSTOPPED) {
760			(void) close(fi);
761			syslog(LOG_WARNING,
762			       "%s: output filter died "
763			       "(retcode=%d termsig=%d)",
764				pp->printer, status.w_retcode,
765			       status.w_termsig);
766			return(REPRINT);
767		}
768		stopped++;
769	}
770start:
771	if ((child = dofork(pp, DORETURN)) == 0) { /* child */
772		dup2(fi, 0);
773		dup2(fo, 1);
774		/* setup stderr for the filter (child process)
775		 * so it goes to our temporary errors file */
776		n = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
777		if (n >= 0)
778			dup2(n, 2);
779		closelog();
780		closeallfds(3);
781		execv(prog, av);
782		syslog(LOG_ERR, "cannot execv %s", prog);
783		exit(2);
784	}
785	(void) close(fi);
786	if (child < 0)
787		status.w_retcode = 100;
788	else
789		while ((pid = wait((int *)&status)) > 0 && pid != child)
790			;
791	child = 0;
792	prchild = 0;
793	if (stopped) {		/* restart output filter */
794		if (kill(ofilter, SIGCONT) < 0) {
795			syslog(LOG_ERR, "cannot restart output filter");
796			exit(1);
797		}
798	}
799	pp->tof = 0;
800
801	/* Copy the filter's output to "lf" logfile */
802	if ((fp = fopen(tempstderr, "r"))) {
803		while (fgets(buf, sizeof(buf), fp))
804			fputs(buf, stderr);
805		fclose(fp);
806	}
807
808	if (!WIFEXITED(status)) {
809		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
810			pp->printer, format, status.w_termsig);
811		return(ERROR);
812	}
813	switch (status.w_retcode) {
814	case 0:
815		pp->tof = 1;
816		return(OK);
817	case 1:
818		return(REPRINT);
819	case 2:
820		return(ERROR);
821	default:
822		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
823			pp->printer, format, status.w_retcode);
824		return(FILTERERR);
825	}
826}
827
828/*
829 * Send the daemon control file (cf) and any data files.
830 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
831 * 0 if all is well.
832 */
833static int
834sendit(struct printer *pp, char *file)
835{
836	register int i, err = OK;
837	char *cp, last[BUFSIZ];
838
839	/*
840	 * open control file
841	 */
842	if ((cfp = fopen(file, "r")) == NULL)
843		return(OK);
844
845	/* initialize job-specific count of datafiles processed */
846	job_dfcnt = 0;
847
848	/*
849	 *      read the control file for work to do
850	 *
851	 *      file format -- first character in the line is a command
852	 *      rest of the line is the argument.
853	 *      commands of interest are:
854	 *
855	 *            a-z -- "file name" name of file to print
856	 *              U -- "unlink" name of file to remove
857	 *                    (after we print it. (Pass 2 only)).
858	 */
859
860	/*
861	 * pass 1
862	 */
863	while (getline(cfp)) {
864	again:
865		if (line[0] == 'S') {
866			cp = line+1;
867			i = 0;
868			while (*cp >= '0' && *cp <= '9')
869				i = i * 10 + (*cp++ - '0');
870			fdev = i;
871			cp++;
872			i = 0;
873			while (*cp >= '0' && *cp <= '9')
874				i = i * 10 + (*cp++ - '0');
875			fino = i;
876		} else if (line[0] == 'H') {
877			strncpy(fromhost, line+1, sizeof(fromhost) - 1);
878			fromhost[sizeof(fromhost) - 1] = '\0';
879			if (class[0] == '\0') {
880				strncpy(class, line+1, sizeof(class) - 1);
881				class[sizeof(class) - 1] = '\0';
882			}
883		} else if (line[0] == 'P') {
884			strncpy(logname, line+1, sizeof(logname) - 1);
885			logname[sizeof(logname) - 1] = '\0';
886			if (pp->restricted) { /* restricted */
887				if (getpwnam(logname) == NULL) {
888					sendmail(pp, line+1, NOACCT);
889					err = ERROR;
890					break;
891				}
892			}
893		} else if (line[0] == 'I') {
894			strncpy(indent+2, line+1, sizeof(indent) - 3);
895			indent[2 + sizeof(indent) - 3] = '\0';
896		} else if (line[0] >= 'a' && line[0] <= 'z') {
897			strcpy(last, line);
898			while ((i = getline(cfp)) != 0)
899				if (strcmp(last, line))
900					break;
901			switch (sendfile(pp, '\3', last+1, *last)) {
902			case OK:
903				if (i)
904					goto again;
905				break;
906			case REPRINT:
907				(void) fclose(cfp);
908				return(REPRINT);
909			case ACCESS:
910				sendmail(pp, logname, ACCESS);
911			case ERROR:
912				err = ERROR;
913			}
914			break;
915		}
916	}
917	if (err == OK && sendfile(pp, '\2', file, '\0') > 0) {
918		(void) fclose(cfp);
919		return(REPRINT);
920	}
921	/*
922	 * pass 2
923	 */
924	fseek(cfp, 0L, 0);
925	while (getline(cfp))
926		if (line[0] == 'U' && !strchr(line+1, '/'))
927			(void) unlink(line+1);
928	/*
929	 * clean-up in case another control file exists
930	 */
931	(void) fclose(cfp);
932	(void) unlink(file);
933	return(err);
934}
935
936/*
937 * Send a data file to the remote machine and spool it.
938 * Return positive if we should try resending.
939 */
940static int
941sendfile(struct printer *pp, int type, char *file, char format)
942{
943	register int f, i, amt;
944	struct stat stb;
945	FILE *fp;
946	char buf[BUFSIZ];
947	int closedpr, resp, sizerr, statrc;
948
949	statrc = lstat(file, &stb);
950	if (statrc < 0) {
951		syslog(LOG_ERR, "%s: error from lstat(%s): %m",
952		    pp->printer, file);
953		return(ERROR);
954	}
955	f = open(file, O_RDONLY);
956	if (f < 0) {
957		syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m",
958		    pp->printer, file);
959		return(ERROR);
960	}
961	/*
962	 * Check to see if data file is a symbolic link. If so, it should
963	 * still point to the same file or someone is trying to print something
964	 * he shouldn't.
965	 */
966	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
967	    (stb.st_dev != fdev || stb.st_ino != fino))
968		return(ACCESS);
969
970	job_dfcnt++;		/* increment datafile counter for this job */
971
972	/* everything seems OK, start it up */
973	sizerr = 0;
974	closedpr = 0;
975	if (type == '\3') {
976		if (pp->filters[LPF_INPUT]) {
977			/*
978			 * We're sending something with an ifilter.  We have to
979			 * run the ifilter and store the output as a temporary
980			 * spool file (tfile...), because the protocol requires
981			 * us to send the file size before we start sending any
982			 * of the data.
983			 */
984			char *av[15];
985			int n;
986			int ifilter;
987			union wait status; /* XXX */
988
989			strcpy(tfile,TFILENAME);
990			if ((tfd = mkstemp(tfile)) == -1) {
991				syslog(LOG_ERR, "mkstemp: %m");
992				return(ERROR);
993			}
994			if ((av[0] = strrchr(pp->filters[LPF_INPUT], '/')) == NULL)
995				av[0] = pp->filters[LPF_INPUT];
996			else
997				av[0]++;
998			if (format == 'l')
999				av[n=1] = "-c";
1000			else
1001				n = 0;
1002			av[++n] = width;
1003			av[++n] = length;
1004			av[++n] = indent;
1005			av[++n] = "-n";
1006			av[++n] = logname;
1007			av[++n] = "-h";
1008			av[++n] = fromhost;
1009			av[++n] = pp->acct_file;
1010			av[++n] = 0;
1011			if ((ifilter = dofork(pp, DORETURN)) == 0) { /* child */
1012				dup2(f, 0);
1013				dup2(tfd, 1);
1014				/* setup stderr for the filter (child process)
1015				 * so it goes to our temporary errors file */
1016				n = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
1017				if (n >= 0)
1018					dup2(n, 2);
1019				closelog();
1020				closeallfds(3);
1021				execv(pp->filters[LPF_INPUT], av);
1022				syslog(LOG_ERR, "cannot execv %s",
1023				       pp->filters[LPF_INPUT]);
1024				exit(2);
1025			}
1026			(void) close(f);
1027			if (ifilter < 0)
1028				status.w_retcode = 100;
1029			else
1030				while ((pid = wait((int *)&status)) > 0 &&
1031					pid != ifilter)
1032					;
1033			/* Copy the filter's output to "lf" logfile */
1034			if ((fp = fopen(tempstderr, "r"))) {
1035				while (fgets(buf, sizeof(buf), fp))
1036					fputs(buf, stderr);
1037				fclose(fp);
1038			}
1039			/* process the return-code from the filter */
1040			switch (status.w_retcode) {
1041			case 0:
1042				break;
1043			case 1:
1044				unlink(tfile);
1045				return(REPRINT);
1046			case 2:
1047				unlink(tfile);
1048				return(ERROR);
1049			default:
1050				syslog(LOG_WARNING, "%s: filter '%c' exited"
1051					" (retcode=%d)",
1052					pp->printer, format, status.w_retcode);
1053				unlink(tfile);
1054				return(FILTERERR);
1055			}
1056			statrc = fstat(tfd, &stb);   /* to find size of tfile */
1057			if (statrc < 0)	{
1058				syslog(LOG_ERR, "%s: error processing 'if', fstat(%s): %m",
1059				    pp->printer, tfile);
1060				return(ERROR);
1061			}
1062			f = tfd;
1063			lseek(f,0,SEEK_SET);
1064		} else if (ofilter) {
1065			/*
1066			 * We're sending something with an ofilter, we have to
1067			 * store the output as a temporary file (tfile)... the
1068			 * protocol requires us to send the file size
1069			 */
1070			int i;
1071			for (i = 0; i < stb.st_size; i += BUFSIZ) {
1072				amt = BUFSIZ;
1073				if (i + amt > stb.st_size)
1074					amt = stb.st_size - i;
1075				if (sizerr == 0 && read(f, buf, amt) != amt) {
1076					sizerr = 1;
1077					break;
1078				}
1079				if (write(ofd, buf, amt) != amt) {
1080					(void) close(f);
1081					return(REPRINT);
1082				}
1083			}
1084			close(ofd);
1085			close(f);
1086			while ((i = wait(NULL)) > 0 && i != ofilter)
1087				;
1088			ofilter = 0;
1089			statrc = fstat(tfd, &stb);   /* to find size of tfile */
1090			if (statrc < 0)	{
1091				syslog(LOG_ERR, "%s: error processing 'of', fstat(%s): %m",
1092				    pp->printer, tfile);
1093				openpr(pp);
1094				return(ERROR);
1095			}
1096			f = tfd;
1097			lseek(f,0,SEEK_SET);
1098			closedpr = 1;
1099		}
1100	}
1101
1102	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
1103	amt = strlen(buf);
1104	for (i = 0;  ; i++) {
1105		if (write(pfd, buf, amt) != amt ||
1106		    (resp = response(pp)) < 0 || resp == '\1') {
1107			(void) close(f);
1108			if (tfd != -1 && type == '\3') {
1109				tfd = -1;
1110				unlink(tfile);
1111				if (closedpr)
1112					openpr(pp);
1113			}
1114			return(REPRINT);
1115		} else if (resp == '\0')
1116			break;
1117		if (i == 0)
1118			pstatus(pp,
1119				"no space on remote; waiting for queue to drain");
1120		if (i == 10)
1121			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
1122				pp->printer, pp->remote_host);
1123		sleep(5 * 60);
1124	}
1125	if (i)
1126		pstatus(pp, "sending to %s", pp->remote_host);
1127	if (type == '\3')
1128		trstat_init(pp, file, job_dfcnt);
1129	for (i = 0; i < stb.st_size; i += BUFSIZ) {
1130		amt = BUFSIZ;
1131		if (i + amt > stb.st_size)
1132			amt = stb.st_size - i;
1133		if (sizerr == 0 && read(f, buf, amt) != amt)
1134			sizerr = 1;
1135		if (write(pfd, buf, amt) != amt) {
1136			(void) close(f);
1137			if (tfd != -1 && type == '\3') {
1138				tfd = -1;
1139				unlink(tfile);
1140				if (closedpr)
1141					openpr(pp);
1142			}
1143			return(REPRINT);
1144		}
1145	}
1146
1147	(void) close(f);
1148	if (tfd != -1 && type == '\3') {
1149		tfd = -1;
1150		unlink(tfile);
1151	}
1152	if (sizerr) {
1153		syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file);
1154		/* tell recvjob to ignore this file */
1155		(void) write(pfd, "\1", 1);
1156		if (closedpr)
1157			openpr(pp);
1158		return(ERROR);
1159	}
1160	if (write(pfd, "", 1) != 1 || response(pp)) {
1161		if (closedpr)
1162			openpr(pp);
1163		return(REPRINT);
1164	}
1165	if (closedpr)
1166		openpr(pp);
1167	if (type == '\3')
1168		trstat_write(pp, TR_SENDING, stb.st_size, logname,
1169				 pp->remote_host, fromhost);
1170	return(OK);
1171}
1172
1173/*
1174 * Check to make sure there have been no errors and that both programs
1175 * are in sync with eachother.
1176 * Return non-zero if the connection was lost.
1177 */
1178static char
1179response(const struct printer *pp)
1180{
1181	char resp;
1182
1183	if (read(pfd, &resp, 1) != 1) {
1184		syslog(LOG_INFO, "%s: lost connection", pp->printer);
1185		return(-1);
1186	}
1187	return(resp);
1188}
1189
1190/*
1191 * Banner printing stuff
1192 */
1193static void
1194banner(struct printer *pp, char *name1, char *name2)
1195{
1196	time_t tvec;
1197
1198	time(&tvec);
1199	if (!pp->no_formfeed && !pp->tof)
1200		(void) write(ofd, pp->form_feed, strlen(pp->form_feed));
1201	if (pp->short_banner) {	/* short banner only */
1202		if (class[0]) {
1203			(void) write(ofd, class, strlen(class));
1204			(void) write(ofd, ":", 1);
1205		}
1206		(void) write(ofd, name1, strlen(name1));
1207		(void) write(ofd, "  Job: ", 7);
1208		(void) write(ofd, name2, strlen(name2));
1209		(void) write(ofd, "  Date: ", 8);
1210		(void) write(ofd, ctime(&tvec), 24);
1211		(void) write(ofd, "\n", 1);
1212	} else {	/* normal banner */
1213		(void) write(ofd, "\n\n\n", 3);
1214		scan_out(pp, ofd, name1, '\0');
1215		(void) write(ofd, "\n\n", 2);
1216		scan_out(pp, ofd, name2, '\0');
1217		if (class[0]) {
1218			(void) write(ofd,"\n\n\n",3);
1219			scan_out(pp, ofd, class, '\0');
1220		}
1221		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
1222		(void) write(ofd, name2, strlen(name2));
1223		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
1224		(void) write(ofd, ctime(&tvec), 24);
1225		(void) write(ofd, "\n", 1);
1226	}
1227	if (!pp->no_formfeed)
1228		(void) write(ofd, pp->form_feed, strlen(pp->form_feed));
1229	pp->tof = 1;
1230}
1231
1232static char *
1233scnline(int key, char *p, int c)
1234{
1235	register int scnwidth;
1236
1237	for (scnwidth = WIDTH; --scnwidth;) {
1238		key <<= 1;
1239		*p++ = key & 0200 ? c : BACKGND;
1240	}
1241	return (p);
1242}
1243
1244#define TRC(q)	(((q)-' ')&0177)
1245
1246static void
1247scan_out(struct printer *pp, int scfd, char *scsp, int dlm)
1248{
1249	register char *strp;
1250	register int nchrs, j;
1251	char outbuf[LINELEN+1], *sp, c, cc;
1252	int d, scnhgt;
1253
1254	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
1255		strp = &outbuf[0];
1256		sp = scsp;
1257		for (nchrs = 0; ; ) {
1258			d = dropit(c = TRC(cc = *sp++));
1259			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
1260				for (j = WIDTH; --j;)
1261					*strp++ = BACKGND;
1262			else
1263				strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc);
1264			if (*sp == dlm || *sp == '\0' ||
1265			    nchrs++ >= pp->page_width/(WIDTH+1)-1)
1266				break;
1267			*strp++ = BACKGND;
1268			*strp++ = BACKGND;
1269		}
1270		while (*--strp == BACKGND && strp >= outbuf)
1271			;
1272		strp++;
1273		*strp++ = '\n';
1274		(void) write(scfd, outbuf, strp-outbuf);
1275	}
1276}
1277
1278static int
1279dropit(int c)
1280{
1281	switch(c) {
1282
1283	case TRC('_'):
1284	case TRC(';'):
1285	case TRC(','):
1286	case TRC('g'):
1287	case TRC('j'):
1288	case TRC('p'):
1289	case TRC('q'):
1290	case TRC('y'):
1291		return (DROP);
1292
1293	default:
1294		return (0);
1295	}
1296}
1297
1298/*
1299 * sendmail ---
1300 *   tell people about job completion
1301 */
1302static void
1303sendmail(struct printer *pp, char *user, int bombed)
1304{
1305	register int i;
1306	int p[2], s;
1307	register const char *cp;
1308	struct stat stb;
1309	FILE *fp;
1310
1311	pipe(p);
1312	if ((s = dofork(pp, DORETURN)) == 0) {		/* child */
1313		dup2(p[0], 0);
1314		closelog();
1315		closeallfds(3);
1316		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
1317			cp++;
1318		else
1319			cp = _PATH_SENDMAIL;
1320		execl(_PATH_SENDMAIL, cp, "-t", 0);
1321		_exit(0);
1322	} else if (s > 0) {				/* parent */
1323		dup2(p[1], 1);
1324		printf("To: %s@%s\n", user, fromhost);
1325		printf("Subject: %s printer job \"%s\"\n", pp->printer,
1326			*jobname ? jobname : "<unknown>");
1327		printf("Reply-To: root@%s\n\n", host);
1328		printf("Your printer job ");
1329		if (*jobname)
1330			printf("(%s) ", jobname);
1331
1332		cp = "XXX compiler confusion"; /* XXX shut GCC up */
1333		switch (bombed) {
1334		case OK:
1335			printf("\ncompleted successfully\n");
1336			cp = "OK";
1337			break;
1338		default:
1339		case FATALERR:
1340			printf("\ncould not be printed\n");
1341			cp = "FATALERR";
1342			break;
1343		case NOACCT:
1344			printf("\ncould not be printed without an account on %s\n", host);
1345			cp = "NOACCT";
1346			break;
1347		case FILTERERR:
1348			if (stat(tempstderr, &stb) < 0 || stb.st_size == 0
1349			    || (fp = fopen(tempstderr, "r")) == NULL) {
1350				printf("\nhad some errors and may not have printed\n");
1351				break;
1352			}
1353			printf("\nhad the following errors and may not have printed:\n");
1354			while ((i = getc(fp)) != EOF)
1355				putchar(i);
1356			(void) fclose(fp);
1357			cp = "FILTERERR";
1358			break;
1359		case ACCESS:
1360			printf("\nwas not printed because it was not linked to the original file\n");
1361			cp = "ACCESS";
1362		}
1363		fflush(stdout);
1364		(void) close(1);
1365	} else {
1366		syslog(LOG_WARNING, "unable to send mail to %s: %m", user);
1367		return;
1368	}
1369	(void) close(p[0]);
1370	(void) close(p[1]);
1371	wait(NULL);
1372	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
1373		user, *jobname ? jobname : "<unknown>", pp->printer, cp);
1374}
1375
1376/*
1377 * dofork - fork with retries on failure
1378 */
1379static int
1380dofork(const struct printer *pp, int action)
1381{
1382	register int i, forkpid;
1383	struct passwd *pwd;
1384
1385	for (i = 0; i < 20; i++) {
1386		if ((forkpid = fork()) < 0) {
1387			sleep((unsigned)(i*i));
1388			continue;
1389		}
1390		/*
1391		 * Child should run as daemon instead of root
1392		 */
1393		if (forkpid == 0) {
1394			if ((pwd = getpwuid(pp->daemon_user)) == NULL) {
1395				syslog(LOG_ERR, "Can't lookup default daemon uid (%ld) in password file",
1396				    pp->daemon_user);
1397				break;
1398			}
1399			initgroups(pwd->pw_name, pwd->pw_gid);
1400			setgid(pwd->pw_gid);
1401			setuid(pp->daemon_user);
1402		}
1403		return(forkpid);
1404	}
1405	syslog(LOG_ERR, "can't fork");
1406
1407	switch (action) {
1408	case DORETURN:
1409		return (-1);
1410	default:
1411		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1412		/*FALL THRU*/
1413	case DOABORT:
1414		exit(1);
1415	}
1416	/*NOTREACHED*/
1417}
1418
1419/*
1420 * Kill child processes to abort current job.
1421 */
1422static void
1423abortpr(int signo __unused)
1424{
1425
1426	(void) unlink(tempstderr);
1427	kill(0, SIGINT);
1428	if (ofilter > 0)
1429		kill(ofilter, SIGCONT);
1430	while (wait(NULL) > 0)
1431		;
1432	if (ofilter > 0 && tfd != -1)
1433		unlink(tfile);
1434	exit(0);
1435}
1436
1437static void
1438init(struct printer *pp)
1439{
1440	char *s;
1441
1442	sprintf(&width[2], "%ld", pp->page_width);
1443	sprintf(&length[2], "%ld", pp->page_length);
1444	sprintf(&pxwidth[2], "%ld", pp->page_pwidth);
1445	sprintf(&pxlength[2], "%ld", pp->page_plength);
1446	if ((s = checkremote(pp)) != 0) {
1447		syslog(LOG_WARNING, "%s", s);
1448		free(s);
1449	}
1450}
1451
1452void
1453startprinting(const char *printer)
1454{
1455	struct printer myprinter, *pp = &myprinter;
1456	int status;
1457
1458	init_printer(pp);
1459	status = getprintcap(printer, pp);
1460	switch(status) {
1461	case PCAPERR_OSERR:
1462		syslog(LOG_ERR, "can't open printer description file: %m");
1463		exit(1);
1464	case PCAPERR_NOTFOUND:
1465		syslog(LOG_ERR, "unknown printer: %s", printer);
1466		exit(1);
1467	case PCAPERR_TCLOOP:
1468		fatal(pp, "potential reference loop detected in printcap file");
1469	default:
1470		break;
1471	}
1472	printjob(pp);
1473}
1474
1475/*
1476 * Acquire line printer or remote connection.
1477 */
1478static void
1479openpr(const struct printer *pp)
1480{
1481	int p[2];
1482	char *cp;
1483
1484	if (pp->remote) {
1485		openrem(pp);
1486	} else if (*pp->lp) {
1487		if ((cp = strchr(pp->lp, '@')) != NULL)
1488			opennet(pp);
1489		else
1490			opentty(pp);
1491	} else {
1492		syslog(LOG_ERR, "%s: no line printer device or host name",
1493			pp->printer);
1494		exit(1);
1495	}
1496
1497	/*
1498	 * Start up an output filter, if needed.
1499	 */
1500	if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !ofilter) {
1501		pipe(p);
1502		if (pp->remote) {
1503			strcpy(tfile, TFILENAME);
1504			tfd = mkstemp(tfile);
1505		}
1506		if ((ofilter = dofork(pp, DOABORT)) == 0) {	/* child */
1507			dup2(p[0], 0);		/* pipe is std in */
1508			/* tfile/printer is stdout */
1509			dup2(pp->remote ? tfd : pfd, 1);
1510			closelog();
1511			closeallfds(3);
1512			if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL)
1513				cp = pp->filters[LPF_OUTPUT];
1514			else
1515				cp++;
1516			execl(pp->filters[LPF_OUTPUT], cp, width, length, 0);
1517			syslog(LOG_ERR, "%s: %s: %m", pp->printer,
1518			       pp->filters[LPF_OUTPUT]);
1519			exit(1);
1520		}
1521		(void) close(p[0]);		/* close input side */
1522		ofd = p[1];			/* use pipe for output */
1523	} else {
1524		ofd = pfd;
1525		ofilter = 0;
1526	}
1527}
1528
1529/*
1530 * Printer connected directly to the network
1531 * or to a terminal server on the net
1532 */
1533static void
1534opennet(const struct printer *pp)
1535{
1536	register int i;
1537	int resp;
1538	u_long port;
1539	char *ep;
1540	void (*savealrm)(int);
1541
1542	port = strtoul(pp->lp, &ep, 0);
1543	if (*ep != '@' || port > 65535) {
1544		syslog(LOG_ERR, "%s: bad port number: %s", pp->printer,
1545		       pp->lp);
1546		exit(1);
1547	}
1548	ep++;
1549
1550	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1551		resp = -1;
1552		savealrm = signal(SIGALRM, alarmhandler);
1553		alarm(pp->conn_timeout);
1554		pfd = getport(pp, ep, port);
1555		alarm(0);
1556		(void)signal(SIGALRM, savealrm);
1557		if (pfd < 0 && errno == ECONNREFUSED)
1558			resp = 1;
1559		else if (pfd >= 0) {
1560			/*
1561			 * need to delay a bit for rs232 lines
1562			 * to stabilize in case printer is
1563			 * connected via a terminal server
1564			 */
1565			delay(500);
1566			break;
1567		}
1568		if (i == 1) {
1569			if (resp < 0)
1570				pstatus(pp, "waiting for %s to come up",
1571					pp->lp);
1572			else
1573				pstatus(pp,
1574					"waiting for access to printer on %s",
1575					pp->lp);
1576		}
1577		sleep(i);
1578	}
1579	pstatus(pp, "sending to %s port %d", ep, port);
1580}
1581
1582/*
1583 * Printer is connected to an RS232 port on this host
1584 */
1585static void
1586opentty(const struct printer *pp)
1587{
1588	register int i;
1589
1590	for (i = 1; ; i = i < 32 ? i << 1 : i) {
1591		pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY);
1592		if (pfd >= 0) {
1593			delay(500);
1594			break;
1595		}
1596		if (errno == ENOENT) {
1597			syslog(LOG_ERR, "%s: %m", pp->lp);
1598			exit(1);
1599		}
1600		if (i == 1)
1601			pstatus(pp,
1602				"waiting for %s to become ready (offline?)",
1603				pp->printer);
1604		sleep(i);
1605	}
1606	if (isatty(pfd))
1607		setty(pp);
1608	pstatus(pp, "%s is ready and printing", pp->printer);
1609}
1610
1611/*
1612 * Printer is on a remote host
1613 */
1614static void
1615openrem(const struct printer *pp)
1616{
1617	register int i;
1618	int resp;
1619	void (*savealrm)(int);
1620
1621	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1622		resp = -1;
1623		savealrm = signal(SIGALRM, alarmhandler);
1624		alarm(pp->conn_timeout);
1625		pfd = getport(pp, pp->remote_host, 0);
1626		alarm(0);
1627		(void)signal(SIGALRM, savealrm);
1628		if (pfd >= 0) {
1629			if ((writel(pfd, "\2", pp->remote_queue, "\n",
1630				    (char *)0)
1631			     == 2 + strlen(pp->remote_queue))
1632			    && (resp = response(pp)) == 0)
1633				break;
1634			(void) close(pfd);
1635		}
1636		if (i == 1) {
1637			if (resp < 0)
1638				pstatus(pp, "waiting for %s to come up",
1639					pp->remote_host);
1640			else {
1641				pstatus(pp,
1642					"waiting for queue to be enabled on %s",
1643					pp->remote_host);
1644				i = 256;
1645			}
1646		}
1647		sleep(i);
1648	}
1649	pstatus(pp, "sending to %s", pp->remote_host);
1650}
1651
1652/*
1653 * setup tty lines.
1654 */
1655static void
1656setty(const struct printer *pp)
1657{
1658	struct termios ttybuf;
1659
1660	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1661		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer);
1662		exit(1);
1663	}
1664	if (tcgetattr(pfd, &ttybuf) < 0) {
1665		syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer);
1666		exit(1);
1667	}
1668	if (pp->baud_rate > 0)
1669		cfsetspeed(&ttybuf, pp->baud_rate);
1670	if (pp->mode_set) {
1671		char *s = strdup(pp->mode_set), *tmp;
1672
1673		while ((tmp = strsep(&s, ",")) != NULL) {
1674			(void) msearch(tmp, &ttybuf);
1675		}
1676	}
1677	if (pp->mode_set != 0 || pp->baud_rate > 0) {
1678		if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
1679			syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer);
1680		}
1681	}
1682}
1683
1684#ifdef __STDC__
1685#include <stdarg.h>
1686#else
1687#include <varargs.h>
1688#endif
1689
1690static void
1691#ifdef __STDC__
1692pstatus(const struct printer *pp, const char *msg, ...)
1693#else
1694pstatus(pp, msg, va_alist)
1695	const struct printer *pp;
1696	char *msg;
1697        va_dcl
1698#endif
1699{
1700	int fd;
1701	char *buf;
1702	va_list ap;
1703#ifdef __STDC__
1704	va_start(ap, msg);
1705#else
1706	va_start(ap);
1707#endif
1708
1709	umask(0);
1710	fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
1711	if (fd < 0) {
1712		syslog(LOG_ERR, "%s: %s: %m", pp->printer, pp->status_file);
1713		exit(1);
1714	}
1715	ftruncate(fd, 0);
1716	vasprintf(&buf, msg, ap);
1717	va_end(ap);
1718	writel(fd, buf, "\n", (char *)0);
1719	close(fd);
1720	free(buf);
1721}
1722
1723void
1724alarmhandler(int signo __unused)
1725{
1726	/* the signal is ignored */
1727	/* (the '__unused' is just to avoid a compile-time warning) */
1728}
1729