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