printjob.c revision 211190
1189251Ssam/*
2189251Ssam * Copyright (c) 1983, 1993
3281806Srpaulo *	The Regents of the University of California.  All rights reserved.
4189251Ssam *
5252726Srpaulo *
6252726Srpaulo * Redistribution and use in source and binary forms, with or without
7189251Ssam * modification, are permitted provided that the following conditions
8189251Ssam * are met:
9189251Ssam * 1. Redistributions of source code must retain the above copyright
10189251Ssam *    notice, this list of conditions and the following disclaimer.
11189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
12189251Ssam *    notice, this list of conditions and the following disclaimer in the
13189251Ssam *    documentation and/or other materials provided with the distribution.
14189251Ssam * 3. All advertising materials mentioning features or use of this software
15189251Ssam *    must display the following acknowledgement:
16189251Ssam *	This product includes software developed by the University of
17214734Srpaulo *	California, Berkeley and its contributors.
18252726Srpaulo * 4. Neither the name of the University nor the names of its contributors
19252726Srpaulo *    may be used to endorse or promote products derived from this software
20252726Srpaulo *    without specific prior written permission.
21252726Srpaulo *
22214734Srpaulo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23252726Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24252726Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25252726Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26252726Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31281806Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32189251Ssam * SUCH DAMAGE.
33189251Ssam */
34189251Ssam
35252726Srpaulo#ifndef lint
36252726Srpaulostatic const char copyright[] =
37189251Ssam"@(#) Copyright (c) 1983, 1993\n\
38189251Ssam	The Regents of the University of California.  All rights reserved.\n";
39252726Srpaulo#endif /* not lint */
40189251Ssam
41189251Ssam#if 0
42189251Ssam#ifndef lint
43189251Ssamstatic char sccsid[] = "@(#)printjob.c	8.7 (Berkeley) 5/10/95";
44189251Ssam#endif /* not lint */
45189251Ssam#endif
46189251Ssam
47189251Ssam#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
48189251Ssam__FBSDID("$FreeBSD: head/usr.sbin/lpr/lpd/printjob.c 211190 2010-08-11 19:32:49Z gad $");
49189251Ssam
50189251Ssam/*
51189251Ssam * printjob -- print jobs in the queue.
52189251Ssam *
53189251Ssam *	NOTE: the lock file is used to pass information to lpq and lprm.
54189251Ssam *	it does not need to be removed because file locks are dynamic.
55189251Ssam */
56189251Ssam
57189251Ssam#include <sys/param.h>
58189251Ssam#include <sys/wait.h>
59189251Ssam#include <sys/stat.h>
60189251Ssam#include <sys/types.h>
61189251Ssam
62189251Ssam#include <pwd.h>
63189251Ssam#include <unistd.h>
64189251Ssam#include <signal.h>
65189251Ssam#include <syslog.h>
66189251Ssam#include <fcntl.h>
67189251Ssam#include <dirent.h>
68189251Ssam#include <errno.h>
69189251Ssam#include <stdio.h>
70214734Srpaulo#include <string.h>
71189251Ssam#include <stdlib.h>
72189251Ssam#include <sys/ioctl.h>
73281806Srpaulo#include <termios.h>
74189251Ssam#include <time.h>
75252726Srpaulo#include "lp.h"
76252726Srpaulo#include "lp.local.h"
77252726Srpaulo#include "pathnames.h"
78252726Srpaulo#include "extern.h"
79189251Ssam
80189251Ssam#define DORETURN	0	/* dofork should return "can't fork" error */
81189251Ssam#define DOABORT		1	/* dofork should just die if fork() fails */
82189251Ssam
83214734Srpaulo/*
84281806Srpaulo * The buffer size to use when reading/writing spool files.
85189251Ssam */
86252726Srpaulo#define	SPL_BUFSIZ	BUFSIZ
87252726Srpaulo
88252726Srpaulo/*
89252726Srpaulo * Error tokens
90189251Ssam */
91252726Srpaulo#define REPRINT		-2
92252726Srpaulo#define ERROR		-1
93252726Srpaulo#define	OK		0
94281806Srpaulo#define	FATALERR	1
95189251Ssam#define	NOACCT		2
96189251Ssam#define	FILTERERR	3
97252726Srpaulo#define	ACCESS		4
98252726Srpaulo
99252726Srpaulostatic dev_t	 fdev;		/* device of file pointed to by symlink */
100252726Srpaulostatic ino_t	 fino;		/* inode of file pointed to by symlink */
101252726Srpaulostatic FILE	*cfp;		/* control file */
102252726Srpaulostatic pid_t	 of_pid;	/* process id of output filter, if any */
103252726Srpaulostatic int	 child;		/* id of any filters */
104189251Ssamstatic int	 job_dfcnt;	/* count of datafiles in current user job */
105189251Ssamstatic int	 lfd;		/* lock file descriptor */
106189251Ssamstatic int	 ofd;		/* output filter file descriptor */
107189251Ssamstatic int	 tfd = -1;	/* output filter temp file output */
108189251Ssamstatic int	 pfd;		/* prstatic inter file descriptor */
109189251Ssamstatic int	 prchild;	/* id of pr process */
110189251Ssamstatic char	 title[80];	/* ``pr'' title */
111189251Ssamstatic char      locale[80];    /* ``pr'' locale */
112189251Ssam
113189251Ssam/* these two are set from pp->daemon_user, but only if they are needed */
114189251Ssamstatic char	*daemon_uname;	/* set from pwd->pw_name */
115189251Ssamstatic int	 daemon_defgid;
116252726Srpaulo
117189251Ssamstatic char	class[32];		/* classification field */
118252726Srpaulostatic char	origin_host[MAXHOSTNAMELEN];	/* user's host machine */
119189251Ssam				/* indentation size in static characters */
120189251Ssamstatic char	indent[10] = "-i0";
121189251Ssamstatic char	jobname[100];		/* job or file name */
122252726Srpaulostatic char	length[10] = "-l";	/* page length in lines */
123252726Srpaulostatic char	logname[32];		/* user's login name */
124252726Srpaulostatic char	pxlength[10] = "-y";	/* page length in pixels */
125252726Srpaulostatic char	pxwidth[10] = "-x";	/* page width in pixels */
126252726Srpaulo/* tempstderr is the filename used to catch stderr from exec-ing filters */
127252726Srpaulostatic char	tempstderr[] = "errs.XXXXXXX";
128214734Srpaulostatic char	width[10] = "-w";	/* page width in static characters */
129252726Srpaulo#define TFILENAME "fltXXXXXX"
130252726Srpaulostatic char	tfile[] = TFILENAME;	/* file name for filter output */
131189251Ssam
132252726Srpaulostatic void	 abortpr(int _signo);
133252726Srpaulostatic void	 alarmhandler(int _signo);
134252726Srpaulostatic void	 banner(struct printer *_pp, char *_name1, char *_name2);
135214734Srpaulostatic int	 dofork(const struct printer *_pp, int _action);
136214734Srpaulostatic int	 dropit(int _c);
137252726Srpaulostatic int	 execfilter(struct printer *_pp, char *_f_cmd, char **_f_av,
138252726Srpaulo		    int _infd, int _outfd);
139252726Srpaulostatic void	 init(struct printer *_pp);
140214734Srpaulostatic void	 openpr(const struct printer *_pp);
141252726Srpaulostatic void	 opennet(const struct printer *_pp);
142252726Srpaulostatic void	 opentty(const struct printer *_pp);
143252726Srpaulostatic void	 openrem(const struct printer *pp);
144252726Srpaulostatic int	 print(struct printer *_pp, int _format, char *_file);
145252726Srpaulostatic int	 printit(struct printer *_pp, char *_file);
146252726Srpaulostatic void	 pstatus(const struct printer *_pp, const char *_msg, ...)
147252726Srpaulo		    __printflike(2, 3);
148214734Srpaulostatic char	 response(const struct printer *_pp);
149214734Srpaulostatic void	 scan_out(struct printer *_pp, int _scfd, char *_scsp,
150252726Srpaulo		    int _dlm);
151252726Srpaulostatic char	*scnline(int _key, char *_p, int _c);
152252726Srpaulostatic int	 sendfile(struct printer *_pp, int _type, char *_file,
153252726Srpaulo		    char _format, int _copyreq);
154252726Srpaulostatic int	 sendit(struct printer *_pp, char *_file);
155252726Srpaulostatic void	 sendmail(struct printer *_pp, char *_userid, int _bombed);
156252726Srpaulostatic void	 setty(const struct printer *_pp);
157252726Srpaulostatic void	 wait4data(struct printer *_pp, const char *_dfile);
158252726Srpaulo
159252726Srpaulovoid
160252726Srpauloprintjob(struct printer *pp)
161252726Srpaulo{
162252726Srpaulo	struct stat stb;
163252726Srpaulo	register struct jobqueue *q, **qp;
164252726Srpaulo	struct jobqueue **queue;
165252726Srpaulo	register int i, nitems;
166252726Srpaulo	off_t pidoff;
167252726Srpaulo	pid_t printpid;
168252726Srpaulo	int errcnt, jobcount, statok, tempfd;
169252726Srpaulo
170252726Srpaulo	jobcount = 0;
171252726Srpaulo	init(pp); /* set up capabilities */
172252726Srpaulo	(void) write(STDOUT_FILENO, "", 1);	/* ack that daemon is started */
173252726Srpaulo	(void) close(STDERR_FILENO);			/* set up log file */
174252726Srpaulo	if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) {
175252726Srpaulo		syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
176252726Srpaulo		    pp->log_file);
177252726Srpaulo		(void) open(_PATH_DEVNULL, O_WRONLY);
178281806Srpaulo	}
179252726Srpaulo	setgid(getegid());
180252726Srpaulo	printpid = getpid();			/* for use with lprm */
181252726Srpaulo	setpgrp(0, printpid);
182252726Srpaulo
183252726Srpaulo	/*
184252726Srpaulo	 * At initial lpd startup, printjob may be called with various
185252726Srpaulo	 * signal handlers in effect.  After that initial startup, any
186252726Srpaulo	 * calls to printjob will have a *different* set of signal-handlers
187252726Srpaulo	 * in effect.  Make sure all handlers are the ones we want.
188252726Srpaulo	 */
189252726Srpaulo	signal(SIGCHLD, SIG_DFL);
190252726Srpaulo	signal(SIGHUP, abortpr);
191252726Srpaulo	signal(SIGINT, abortpr);
192252726Srpaulo	signal(SIGQUIT, abortpr);
193252726Srpaulo	signal(SIGTERM, abortpr);
194252726Srpaulo
195252726Srpaulo	/*
196252726Srpaulo	 * uses short form file names
197252726Srpaulo	 */
198252726Srpaulo	if (chdir(pp->spool_dir) < 0) {
199252726Srpaulo		syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer,
200252726Srpaulo		    pp->spool_dir);
201252726Srpaulo		exit(1);
202252726Srpaulo	}
203252726Srpaulo	statok = stat(pp->lock_file, &stb);
204252726Srpaulo	if (statok == 0 && (stb.st_mode & LFM_PRINT_DIS))
205252726Srpaulo		exit(0);		/* printing disabled */
206252726Srpaulo	umask(S_IWOTH);
207252726Srpaulo	lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
208252726Srpaulo		   LOCK_FILE_MODE);
209252726Srpaulo	if (lfd < 0) {
210252726Srpaulo		if (errno == EWOULDBLOCK)	/* active daemon present */
211252726Srpaulo			exit(0);
212252726Srpaulo		syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
213252726Srpaulo		    pp->lock_file);
214252726Srpaulo		exit(1);
215252726Srpaulo	}
216252726Srpaulo	/*
217252726Srpaulo	 * If the initial call to stat() failed, then lock_file will have
218252726Srpaulo	 * been created by open().  Update &stb to match that new file.
219252726Srpaulo	 */
220252726Srpaulo	if (statok != 0)
221252726Srpaulo		statok = stat(pp->lock_file, &stb);
222252726Srpaulo	/* turn off non-blocking mode (was turned on for lock effects only) */
223252726Srpaulo	if (fcntl(lfd, F_SETFL, 0) < 0) {
224252726Srpaulo		syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer,
225252726Srpaulo		    pp->lock_file);
226281806Srpaulo		exit(1);
227252726Srpaulo	}
228252726Srpaulo	ftruncate(lfd, 0);
229252726Srpaulo	/*
230252726Srpaulo	 * write process id for others to know
231252726Srpaulo	 */
232252726Srpaulo	sprintf(line, "%u\n", printpid);
233252726Srpaulo	pidoff = i = strlen(line);
234252726Srpaulo	if (write(lfd, line, i) != i) {
235252726Srpaulo		syslog(LOG_ERR, "%s: write(%s): %m", pp->printer,
236252726Srpaulo		    pp->lock_file);
237252726Srpaulo		exit(1);
238252726Srpaulo	}
239252726Srpaulo	/*
240252726Srpaulo	 * search the spool directory for work and sort by queue order.
241252726Srpaulo	 */
242252726Srpaulo	if ((nitems = getq(pp, &queue)) < 0) {
243252726Srpaulo		syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
244252726Srpaulo		    pp->spool_dir);
245252726Srpaulo		exit(1);
246252726Srpaulo	}
247252726Srpaulo	if (nitems == 0)		/* no work to do */
248252726Srpaulo		exit(0);
249252726Srpaulo	if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */
250214734Srpaulo		if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0)
251252726Srpaulo			syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
252252726Srpaulo			    pp->lock_file);
253252726Srpaulo	}
254252726Srpaulo
255252726Srpaulo	/* create a file which will be used to hold stderr from filters */
256252726Srpaulo	if ((tempfd = mkstemp(tempstderr)) == -1) {
257252726Srpaulo		syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
258252726Srpaulo		    tempstderr);
259252726Srpaulo		exit(1);
260252726Srpaulo	}
261252726Srpaulo	if ((i = fchmod(tempfd, 0664)) == -1) {
262252726Srpaulo		syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
263252726Srpaulo		    tempstderr);
264252726Srpaulo		exit(1);
265252726Srpaulo	}
266252726Srpaulo	/* lpd doesn't need it to be open, it just needs it to exist */
267214734Srpaulo	close(tempfd);
268214734Srpaulo
269252726Srpaulo	openpr(pp);			/* open printer or remote */
270252726Srpauloagain:
271252726Srpaulo	/*
272252726Srpaulo	 * we found something to do now do it --
273214734Srpaulo	 *    write the name of the current control file into the lock file
274214734Srpaulo	 *    so the spool queue program can tell what we're working on
275214734Srpaulo	 */
276252726Srpaulo	for (qp = queue; nitems--; free((char *) q)) {
277252726Srpaulo		q = *qp++;
278252726Srpaulo		if (stat(q->job_cfname, &stb) < 0)
279252726Srpaulo			continue;
280252726Srpaulo		errcnt = 0;
281252726Srpaulo	restart:
282252726Srpaulo		(void) lseek(lfd, pidoff, 0);
283252726Srpaulo		(void) snprintf(line, sizeof(line), "%s\n", q->job_cfname);
284252726Srpaulo		i = strlen(line);
285252726Srpaulo		if (write(lfd, line, i) != i)
286252726Srpaulo			syslog(LOG_ERR, "%s: write(%s): %m", pp->printer,
287252726Srpaulo			    pp->lock_file);
288252726Srpaulo		if (!pp->remote)
289252726Srpaulo			i = printit(pp, q->job_cfname);
290252726Srpaulo		else
291252726Srpaulo			i = sendit(pp, q->job_cfname);
292252726Srpaulo		/*
293252726Srpaulo		 * Check to see if we are supposed to stop printing or
294252726Srpaulo		 * if we are to rebuild the queue.
295252726Srpaulo		 */
296252726Srpaulo		if (fstat(lfd, &stb) == 0) {
297252726Srpaulo			/* stop printing before starting next job? */
298252726Srpaulo			if (stb.st_mode & LFM_PRINT_DIS)
299252726Srpaulo				goto done;
300252726Srpaulo			/* rebuild queue (after lpc topq) */
301252726Srpaulo			if (stb.st_mode & LFM_RESET_QUE) {
302252726Srpaulo				for (free(q); nitems--; free(q))
303214734Srpaulo					q = *qp++;
304214734Srpaulo				if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE)
305189251Ssam				    < 0)
306189251Ssam					syslog(LOG_WARNING,
307214734Srpaulo					    "%s: fchmod(%s): %m",
308214734Srpaulo					    pp->printer, pp->lock_file);
309214734Srpaulo				break;
310214734Srpaulo			}
311214734Srpaulo		}
312214734Srpaulo		if (i == OK)		/* all files of this job printed */
313214734Srpaulo			jobcount++;
314189251Ssam		else if (i == REPRINT && ++errcnt < 5) {
315252726Srpaulo			/* try reprinting the job */
316189251Ssam			syslog(LOG_INFO, "restarting %s", pp->printer);
317189251Ssam			if (of_pid > 0) {
318189251Ssam				kill(of_pid, SIGCONT); /* to be sure */
319214734Srpaulo				(void) close(ofd);
320189251Ssam				while ((i = wait(NULL)) > 0 && i != of_pid)
321252726Srpaulo					;
322252726Srpaulo				if (i < 0)
323252726Srpaulo					syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m",
324252726Srpaulo					    pp->printer, of_pid);
325252726Srpaulo				of_pid = 0;
326189251Ssam			}
327252726Srpaulo			(void) close(pfd);	/* close printer */
328189251Ssam			if (ftruncate(lfd, pidoff) < 0)
329252726Srpaulo				syslog(LOG_WARNING, "%s: ftruncate(%s): %m",
330252726Srpaulo				    pp->printer, pp->lock_file);
331252726Srpaulo			openpr(pp);		/* try to reopen printer */
332252726Srpaulo			goto restart;
333252726Srpaulo		} else {
334252726Srpaulo			syslog(LOG_WARNING, "%s: job could not be %s (%s)",
335252726Srpaulo			    pp->printer,
336281806Srpaulo			    pp->remote ? "sent to remote host" : "printed",
337252726Srpaulo			    q->job_cfname);
338252726Srpaulo			if (i == REPRINT) {
339252726Srpaulo				/* ensure we don't attempt this job again */
340252726Srpaulo				(void) unlink(q->job_cfname);
341252726Srpaulo				q->job_cfname[0] = 'd';
342189251Ssam				(void) unlink(q->job_cfname);
343214734Srpaulo				if (logname[0])
344214734Srpaulo					sendmail(pp, logname, FATALERR);
345214734Srpaulo			}
346214734Srpaulo		}
347214734Srpaulo	}
348214734Srpaulo	free(queue);
349214734Srpaulo	/*
350214734Srpaulo	 * search the spool directory for more work.
351214734Srpaulo	 */
352189251Ssam	if ((nitems = getq(pp, &queue)) < 0) {
353189251Ssam		syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
354214734Srpaulo		    pp->spool_dir);
355214734Srpaulo		exit(1);
356214734Srpaulo	}
357214734Srpaulo	if (nitems == 0) {		/* no more work to do */
358252726Srpaulo	done:
359252726Srpaulo		if (jobcount > 0) {	/* jobs actually printed */
360252726Srpaulo			if (!pp->no_formfeed && !pp->tof)
361252726Srpaulo				(void) write(ofd, pp->form_feed,
362214734Srpaulo					     strlen(pp->form_feed));
363214734Srpaulo			if (pp->trailer != NULL) /* output trailer */
364214734Srpaulo				(void) write(ofd, pp->trailer,
365252726Srpaulo					     strlen(pp->trailer));
366214734Srpaulo		}
367214734Srpaulo		(void) close(ofd);
368214734Srpaulo		(void) wait(NULL);
369214734Srpaulo		(void) unlink(tempstderr);
370214734Srpaulo		exit(0);
371189251Ssam	}
372189251Ssam	goto again;
373189251Ssam}
374189251Ssam
375189251Ssamchar	fonts[4][50];	/* fonts for troff */
376189251Ssam
377189251Ssamchar ifonts[4][40] = {
378189251Ssam	_PATH_VFONTR,
379189251Ssam	_PATH_VFONTI,
380214734Srpaulo	_PATH_VFONTB,
381189251Ssam	_PATH_VFONTS,
382189251Ssam};
383189251Ssam
384189251Ssam/*
385214734Srpaulo * The remaining part is the reading of the control file (cf)
386252726Srpaulo * and performing the various actions.
387214734Srpaulo */
388214734Srpaulostatic int
389214734Srpauloprintit(struct printer *pp, char *file)
390189251Ssam{
391189251Ssam	register int i;
392189251Ssam	char *cp;
393189251Ssam	int bombed, didignorehdr;
394189251Ssam
395189251Ssam	bombed = OK;
396189251Ssam	didignorehdr = 0;
397189251Ssam	/*
398189251Ssam	 * open control file; ignore if no longer there.
399189251Ssam	 */
400189251Ssam	if ((cfp = fopen(file, "r")) == NULL) {
401281806Srpaulo		syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file);
402189251Ssam		return (OK);
403189251Ssam	}
404189251Ssam	/*
405189251Ssam	 * Reset troff fonts.
406189251Ssam	 */
407189251Ssam	for (i = 0; i < 4; i++)
408189251Ssam		strcpy(fonts[i], ifonts[i]);
409281806Srpaulo	sprintf(&width[2], "%ld", pp->page_width);
410281806Srpaulo	strcpy(indent+2, "0");
411281806Srpaulo
412281806Srpaulo	/* initialize job-specific count of datafiles processed */
413281806Srpaulo	job_dfcnt = 0;
414281806Srpaulo
415189251Ssam	/*
416189251Ssam	 *      read the control file for work to do
417189251Ssam	 *
418189251Ssam	 *      file format -- first character in the line is a command
419189251Ssam	 *      rest of the line is the argument.
420189251Ssam	 *      valid commands are:
421189251Ssam	 *
422189251Ssam	 *		S -- "stat info" for symbolic link protection
423189251Ssam	 *		J -- "job name" on banner page
424189251Ssam	 *		C -- "class name" on banner page
425189251Ssam	 *              L -- "literal" user's name to print on banner
426189251Ssam	 *		T -- "title" for pr
427189251Ssam	 *		H -- "host name" of machine where lpr was done
428252726Srpaulo	 *              P -- "person" user's login name
429252726Srpaulo	 *              I -- "indent" amount to indent output
430189251Ssam	 *		R -- laser dpi "resolution"
431189251Ssam	 *              f -- "file name" name of text file to print
432189251Ssam	 *		l -- "file name" text file with control chars
433189251Ssam	 *		o -- "file name" postscript file, according to
434189251Ssam	 *		     the RFC.  Here it is treated like an 'f'.
435189251Ssam	 *		p -- "file name" text file to print with pr(1)
436189251Ssam	 *		t -- "file name" troff(1) file to print
437189251Ssam	 *		n -- "file name" ditroff(1) file to print
438189251Ssam	 *		d -- "file name" dvi file to print
439189251Ssam	 *		g -- "file name" plot(1G) file to print
440189251Ssam	 *		v -- "file name" plain raster file to print
441252726Srpaulo	 *		c -- "file name" cifplot file to print
442252726Srpaulo	 *		1 -- "R font file" for troff
443252726Srpaulo	 *		2 -- "I font file" for troff
444252726Srpaulo	 *		3 -- "B font file" for troff
445252726Srpaulo	 *		4 -- "S font file" for troff
446252726Srpaulo	 *		N -- "name" of file (used by lpq)
447252726Srpaulo	 *              U -- "unlink" name of file to remove
448252726Srpaulo	 *                    (after we print it. (Pass 2 only)).
449252726Srpaulo	 *		M -- "mail" to user when done printing
450252726Srpaulo	 *              Z -- "locale" for pr
451281806Srpaulo	 *
452252726Srpaulo	 *      getline reads a line and expands tabs to blanks
453252726Srpaulo	 */
454252726Srpaulo
455252726Srpaulo	/* pass 1 */
456252726Srpaulo
457281806Srpaulo	while (getline(cfp))
458252726Srpaulo		switch (line[0]) {
459252726Srpaulo		case 'H':
460252726Srpaulo			strlcpy(origin_host, line + 1, sizeof(origin_host));
461252726Srpaulo			if (class[0] == '\0') {
462252726Srpaulo				strlcpy(class, line+1, sizeof(class));
463252726Srpaulo			}
464252726Srpaulo			continue;
465252726Srpaulo
466252726Srpaulo		case 'P':
467252726Srpaulo			strlcpy(logname, line + 1, sizeof(logname));
468252726Srpaulo			if (pp->restricted) { /* restricted */
469252726Srpaulo				if (getpwnam(logname) == NULL) {
470252726Srpaulo					bombed = NOACCT;
471252726Srpaulo					sendmail(pp, line+1, bombed);
472252726Srpaulo					goto pass2;
473252726Srpaulo				}
474281806Srpaulo			}
475252726Srpaulo			continue;
476252726Srpaulo
477252726Srpaulo		case 'S':
478252726Srpaulo			cp = line+1;
479252726Srpaulo			i = 0;
480252726Srpaulo			while (*cp >= '0' && *cp <= '9')
481252726Srpaulo				i = i * 10 + (*cp++ - '0');
482252726Srpaulo			fdev = i;
483252726Srpaulo			cp++;
484252726Srpaulo			i = 0;
485252726Srpaulo			while (*cp >= '0' && *cp <= '9')
486252726Srpaulo				i = i * 10 + (*cp++ - '0');
487252726Srpaulo			fino = i;
488252726Srpaulo			continue;
489252726Srpaulo
490252726Srpaulo		case 'J':
491252726Srpaulo			if (line[1] != '\0') {
492252726Srpaulo				strlcpy(jobname, line + 1, sizeof(jobname));
493189251Ssam			} else
494189251Ssam				strcpy(jobname, " ");
495252726Srpaulo			continue;
496252726Srpaulo
497252726Srpaulo		case 'C':
498252726Srpaulo			if (line[1] != '\0')
499281806Srpaulo				strlcpy(class, line + 1, sizeof(class));
500281806Srpaulo			else if (class[0] == '\0') {
501252726Srpaulo				/* XXX - why call gethostname instead of
502189251Ssam				 *       just strlcpy'ing local_host? */
503189251Ssam				gethostname(class, sizeof(class));
504189251Ssam				class[sizeof(class) - 1] = '\0';
505189251Ssam			}
506189251Ssam			continue;
507189251Ssam
508189251Ssam		case 'T':	/* header title for pr */
509189251Ssam			strlcpy(title, line + 1, sizeof(title));
510189251Ssam			continue;
511252726Srpaulo
512252726Srpaulo		case 'L':	/* identification line */
513252726Srpaulo			if (!pp->no_header && !pp->header_last)
514252726Srpaulo				banner(pp, line+1, jobname);
515252726Srpaulo			continue;
516252726Srpaulo
517252726Srpaulo		case '1':	/* troff fonts */
518252726Srpaulo		case '2':
519252726Srpaulo		case '3':
520252726Srpaulo		case '4':
521252726Srpaulo			if (line[1] != '\0') {
522252726Srpaulo				strlcpy(fonts[line[0]-'1'], line + 1,
523189251Ssam				    (size_t)50);
524189251Ssam			}
525189251Ssam			continue;
526189251Ssam
527189251Ssam		case 'W':	/* page width */
528189251Ssam			strlcpy(width+2, line + 1, sizeof(width) - 2);
529189251Ssam			continue;
530189251Ssam
531189251Ssam		case 'I':	/* indent amount */
532189251Ssam			strlcpy(indent+2, line + 1, sizeof(indent) - 2);
533189251Ssam			continue;
534189251Ssam
535281806Srpaulo		case 'Z':       /* locale for pr */
536281806Srpaulo			strlcpy(locale, line + 1, sizeof(locale));
537281806Srpaulo			continue;
538281806Srpaulo
539281806Srpaulo		default:	/* some file to print */
540281806Srpaulo			/* only lowercase cmd-codes include a file-to-print */
541281806Srpaulo			if ((line[0] < 'a') || (line[0] > 'z')) {
542189251Ssam				/* ignore any other lines */
543189251Ssam				if (lflag <= 1)
544252726Srpaulo					continue;
545189251Ssam				if (!didignorehdr) {
546189251Ssam					syslog(LOG_INFO, "%s: in %s :",
547189251Ssam					    pp->printer, file);
548189251Ssam					didignorehdr = 1;
549252726Srpaulo				}
550252726Srpaulo				syslog(LOG_INFO, "%s: ignoring line: '%c' %s",
551252726Srpaulo				    pp->printer, line[0], &line[1]);
552252726Srpaulo				continue;
553252726Srpaulo			}
554252726Srpaulo			i = print(pp, line[0], line+1);
555252726Srpaulo			switch (i) {
556252726Srpaulo			case ERROR:
557252726Srpaulo				if (bombed == OK)
558252726Srpaulo					bombed = FATALERR;
559252726Srpaulo				break;
560252726Srpaulo			case REPRINT:
561252726Srpaulo				(void) fclose(cfp);
562252726Srpaulo				return (REPRINT);
563252726Srpaulo			case FILTERERR:
564189251Ssam			case ACCESS:
565189251Ssam				bombed = i;
566189251Ssam				sendmail(pp, logname, bombed);
567189251Ssam			}
568189251Ssam			title[0] = '\0';
569189251Ssam			continue;
570189251Ssam
571189251Ssam		case 'N':
572189251Ssam		case 'U':
573189251Ssam		case 'M':
574252726Srpaulo		case 'R':
575252726Srpaulo			continue;
576189251Ssam		}
577189251Ssam
578189251Ssam	/* pass 2 */
579189251Ssam
580189251Ssampass2:
581189251Ssam	fseek(cfp, 0L, 0);
582189251Ssam	while (getline(cfp))
583189251Ssam		switch (line[0]) {
584189251Ssam		case 'L':	/* identification line */
585281806Srpaulo			if (!pp->no_header && pp->header_last)
586281806Srpaulo				banner(pp, line+1, jobname);
587281806Srpaulo			continue;
588281806Srpaulo
589281806Srpaulo		case 'M':
590281806Srpaulo			if (bombed < NOACCT)	/* already sent if >= NOACCT */
591281806Srpaulo				sendmail(pp, line+1, bombed);
592189251Ssam			continue;
593189251Ssam
594281806Srpaulo		case 'U':
595281806Srpaulo			if (strchr(line+1, '/'))
596281806Srpaulo				continue;
597281806Srpaulo			(void) unlink(line+1);
598281806Srpaulo		}
599281806Srpaulo	/*
600281806Srpaulo	 * clean-up in case another control file exists
601281806Srpaulo	 */
602281806Srpaulo	(void) fclose(cfp);
603281806Srpaulo	(void) unlink(file);
604281806Srpaulo	return (bombed == OK ? OK : ERROR);
605281806Srpaulo}
606281806Srpaulo
607281806Srpaulo/*
608281806Srpaulo * Print a file.
609281806Srpaulo * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
610281806Srpaulo * Return -1 if a non-recoverable error occured,
611281806Srpaulo * 2 if the filter detected some errors (but printed the job anyway),
612281806Srpaulo * 1 if we should try to reprint this job and
613281806Srpaulo * 0 if all is well.
614281806Srpaulo * Note: all filters take stdin as the file, stdout as the printer,
615281806Srpaulo * stderr as the log file, and must not ignore SIGINT.
616281806Srpaulo */
617281806Srpaulostatic int
618281806Srpauloprint(struct printer *pp, int format, char *file)
619281806Srpaulo{
620281806Srpaulo	register int n, i;
621281806Srpaulo	register char *prog;
622281806Srpaulo	int fi, fo;
623281806Srpaulo	FILE *fp;
624281806Srpaulo	char *av[15], buf[SPL_BUFSIZ];
625281806Srpaulo	pid_t wpid;
626281806Srpaulo	int p[2], retcode, stopped, wstatus, wstatus_set;
627281806Srpaulo	struct stat stb;
628281806Srpaulo
629281806Srpaulo	/* Make sure the entire data file has arrived. */
630281806Srpaulo	wait4data(pp, file);
631281806Srpaulo
632281806Srpaulo	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) {
633281806Srpaulo		syslog(LOG_INFO, "%s: unable to open %s ('%c' line)",
634281806Srpaulo		    pp->printer, file, format);
635281806Srpaulo		return (ERROR);
636281806Srpaulo	}
637281806Srpaulo	/*
638281806Srpaulo	 * Check to see if data file is a symbolic link. If so, it should
639281806Srpaulo	 * still point to the same file or someone is trying to print
640281806Srpaulo	 * something he shouldn't.
641281806Srpaulo	 */
642281806Srpaulo	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
643281806Srpaulo	    (stb.st_dev != fdev || stb.st_ino != fino))
644281806Srpaulo		return (ACCESS);
645281806Srpaulo
646281806Srpaulo	job_dfcnt++;		/* increment datafile counter for this job */
647281806Srpaulo	stopped = 0;		/* output filter is not stopped */
648281806Srpaulo
649281806Srpaulo	/* everything seems OK, start it up */
650281806Srpaulo	if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */
651281806Srpaulo		(void) write(ofd, pp->form_feed, strlen(pp->form_feed));
652281806Srpaulo		pp->tof = 1;
653189251Ssam	}
654189251Ssam	if (pp->filters[LPF_INPUT] == NULL
655281806Srpaulo	    && (format == 'f' || format == 'l' || format == 'o')) {
656281806Srpaulo		pp->tof = 0;
657281806Srpaulo		while ((n = read(fi, buf, SPL_BUFSIZ)) > 0)
658281806Srpaulo			if (write(ofd, buf, n) != n) {
659189251Ssam				(void) close(fi);
660189251Ssam				return (REPRINT);
661281806Srpaulo			}
662281806Srpaulo		(void) close(fi);
663281806Srpaulo		return (OK);
664281806Srpaulo	}
665189251Ssam	switch (format) {
666281806Srpaulo	case 'p':	/* print file using 'pr' */
667252726Srpaulo		if (pp->filters[LPF_INPUT] == NULL) {	/* use output filter */
668252726Srpaulo			prog = _PATH_PR;
669252726Srpaulo			i = 0;
670252726Srpaulo			av[i++] = "pr";
671252726Srpaulo			av[i++] = width;
672252726Srpaulo			av[i++] = length;
673189251Ssam			av[i++] = "-h";
674189251Ssam			av[i++] = *title ? title : " ";
675189251Ssam			av[i++] = "-L";
676189251Ssam			av[i++] = *locale ? locale : "C";
677189251Ssam			av[i++] = "-F";
678189251Ssam			av[i] = 0;
679189251Ssam			fo = ofd;
680189251Ssam			goto start;
681189251Ssam		}
682189251Ssam		pipe(p);
683189251Ssam		if ((prchild = dofork(pp, DORETURN)) == 0) {	/* child */
684189251Ssam			dup2(fi, STDIN_FILENO);		/* file is stdin */
685189251Ssam			dup2(p[1], STDOUT_FILENO);	/* pipe is stdout */
686189251Ssam			closelog();
687189251Ssam			closeallfds(3);
688189251Ssam			execl(_PATH_PR, "pr", width, length,
689189251Ssam			    "-h", *title ? title : " ",
690189251Ssam			    "-L", *locale ? locale : "C",
691189251Ssam			    "-F", (char *)0);
692281806Srpaulo			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
693281806Srpaulo			exit(2);
694281806Srpaulo		}
695281806Srpaulo		(void) close(p[1]);		/* close output side */
696281806Srpaulo		(void) close(fi);
697281806Srpaulo		if (prchild < 0) {
698189251Ssam			prchild = 0;
699189251Ssam			(void) close(p[0]);
700189251Ssam			return (ERROR);
701252726Srpaulo		}
702252726Srpaulo		fi = p[0];			/* use pipe for input */
703189251Ssam	case 'f':	/* print plain text file */
704189251Ssam		prog = pp->filters[LPF_INPUT];
705252726Srpaulo		av[1] = width;
706252726Srpaulo		av[2] = length;
707252726Srpaulo		av[3] = indent;
708189251Ssam		n = 4;
709189251Ssam		break;
710189251Ssam	case 'o':	/* print postscript file */
711252726Srpaulo		/*
712252726Srpaulo		 * Treat this as a "plain file with control characters", and
713189251Ssam		 * assume the standard LPF_INPUT filter will recognize that
714252726Srpaulo		 * the data is postscript and know what to do with it.  These
715252726Srpaulo		 * 'o'-file requests could come from MacOS 10.1 systems.
716252726Srpaulo		 * (later versions of MacOS 10 will explicitly use 'l')
717252726Srpaulo		 * A postscript file can contain binary data, which is why 'l'
718252726Srpaulo		 * is somewhat more appropriate than 'f'.
719252726Srpaulo		 */
720252726Srpaulo		/* FALLTHROUGH */
721252726Srpaulo	case 'l':	/* like 'f' but pass control characters */
722252726Srpaulo		prog = pp->filters[LPF_INPUT];
723252726Srpaulo		av[1] = "-c";
724252726Srpaulo		av[2] = width;
725252726Srpaulo		av[3] = length;
726252726Srpaulo		av[4] = indent;
727252726Srpaulo		n = 5;
728252726Srpaulo		break;
729252726Srpaulo	case 'r':	/* print a fortran text file */
730252726Srpaulo		prog = pp->filters[LPF_FORTRAN];
731252726Srpaulo		av[1] = width;
732252726Srpaulo		av[2] = length;
733252726Srpaulo		n = 3;
734189251Ssam		break;
735189251Ssam	case 't':	/* print troff output */
736189251Ssam	case 'n':	/* print ditroff output */
737252726Srpaulo	case 'd':	/* print tex output */
738252726Srpaulo		(void) unlink(".railmag");
739252726Srpaulo		if ((fo = creat(".railmag", FILMOD)) < 0) {
740252726Srpaulo			syslog(LOG_ERR, "%s: cannot create .railmag",
741281806Srpaulo			    pp->printer);
742252726Srpaulo			(void) unlink(".railmag");
743189251Ssam		} else {
744189251Ssam			for (n = 0; n < 4; n++) {
745189251Ssam				if (fonts[n][0] != '/')
746189251Ssam					(void) write(fo, _PATH_VFONT,
747189251Ssam					    sizeof(_PATH_VFONT) - 1);
748189251Ssam				(void) write(fo, fonts[n], strlen(fonts[n]));
749189251Ssam				(void) write(fo, "\n", 1);
750189251Ssam			}
751189251Ssam			(void) close(fo);
752252726Srpaulo		}
753252726Srpaulo		prog = (format == 't') ? pp->filters[LPF_TROFF]
754189251Ssam			: ((format == 'n') ? pp->filters[LPF_DITROFF]
755252726Srpaulo			   : pp->filters[LPF_DVI]);
756252726Srpaulo		av[1] = pxwidth;
757252726Srpaulo		av[2] = pxlength;
758252726Srpaulo		n = 3;
759252726Srpaulo		break;
760252726Srpaulo	case 'c':	/* print cifplot output */
761252726Srpaulo		prog = pp->filters[LPF_CIFPLOT];
762252726Srpaulo		av[1] = pxwidth;
763252726Srpaulo		av[2] = pxlength;
764252726Srpaulo		n = 3;
765252726Srpaulo		break;
766252726Srpaulo	case 'g':	/* print plot(1G) output */
767252726Srpaulo		prog = pp->filters[LPF_GRAPH];
768252726Srpaulo		av[1] = pxwidth;
769252726Srpaulo		av[2] = pxlength;
770252726Srpaulo		n = 3;
771252726Srpaulo		break;
772252726Srpaulo	case 'v':	/* print raster output */
773252726Srpaulo		prog = pp->filters[LPF_RASTER];
774252726Srpaulo		av[1] = pxwidth;
775189251Ssam		av[2] = pxlength;
776189251Ssam		n = 3;
777189251Ssam		break;
778252726Srpaulo	default:
779189251Ssam		(void) close(fi);
780189251Ssam		syslog(LOG_ERR, "%s: illegal format character '%c'",
781189251Ssam		    pp->printer, format);
782252726Srpaulo		return (ERROR);
783252726Srpaulo	}
784189251Ssam	if (prog == NULL) {
785252726Srpaulo		(void) close(fi);
786252726Srpaulo		syslog(LOG_ERR,
787189251Ssam		   "%s: no filter found in printcap for format character '%c'",
788252726Srpaulo		   pp->printer, format);
789252726Srpaulo		return (ERROR);
790252726Srpaulo	}
791252726Srpaulo	if ((av[0] = strrchr(prog, '/')) != NULL)
792252726Srpaulo		av[0]++;
793252726Srpaulo	else
794252726Srpaulo		av[0] = prog;
795252726Srpaulo	av[n++] = "-n";
796252726Srpaulo	av[n++] = logname;
797252726Srpaulo	av[n++] = "-h";
798252726Srpaulo	av[n++] = origin_host;
799252726Srpaulo	av[n++] = pp->acct_file;
800252726Srpaulo	av[n] = 0;
801252726Srpaulo	fo = pfd;
802252726Srpaulo	if (of_pid > 0) {		/* stop output filter */
803252726Srpaulo		write(ofd, "\031\1", 2);
804281806Srpaulo		while ((wpid =
805281806Srpaulo		    wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid)
806281806Srpaulo			;
807281806Srpaulo		if (wpid < 0)
808281806Srpaulo			syslog(LOG_WARNING,
809281806Srpaulo			    "%s: after stopping 'of', wait3() returned: %m",
810281806Srpaulo			    pp->printer);
811252726Srpaulo		else if (!WIFSTOPPED(wstatus)) {
812252726Srpaulo			(void) close(fi);
813252726Srpaulo			syslog(LOG_WARNING, "%s: output filter died "
814252726Srpaulo			    "(pid=%d retcode=%d termsig=%d)",
815252726Srpaulo			    pp->printer, of_pid, WEXITSTATUS(wstatus),
816252726Srpaulo			    WTERMSIG(wstatus));
817252726Srpaulo			return (REPRINT);
818252726Srpaulo		}
819252726Srpaulo		stopped++;
820252726Srpaulo	}
821252726Srpaulostart:
822252726Srpaulo	if ((child = dofork(pp, DORETURN)) == 0) { /* child */
823252726Srpaulo		dup2(fi, STDIN_FILENO);
824252726Srpaulo		dup2(fo, STDOUT_FILENO);
825189251Ssam		/* setup stderr for the filter (child process)
826252726Srpaulo		 * so it goes to our temporary errors file */
827252726Srpaulo		n = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
828189251Ssam		if (n >= 0)
829189251Ssam			dup2(n, STDERR_FILENO);
830189251Ssam		closelog();
831252726Srpaulo		closeallfds(3);
832252726Srpaulo		execv(prog, av);
833252726Srpaulo		syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer,
834189251Ssam		    prog);
835252726Srpaulo		exit(2);
836252726Srpaulo	}
837252726Srpaulo	(void) close(fi);
838252726Srpaulo	wstatus_set = 0;
839252726Srpaulo	if (child < 0)
840252726Srpaulo		retcode = 100;
841189251Ssam	else {
842189251Ssam		while ((wpid = wait(&wstatus)) > 0 && wpid != child)
843189251Ssam			;
844252726Srpaulo		if (wpid < 0) {
845252726Srpaulo			retcode = 100;
846189251Ssam			syslog(LOG_WARNING,
847252726Srpaulo			    "%s: after execv(%s), wait() returned: %m",
848252726Srpaulo			    pp->printer, prog);
849189251Ssam		} else {
850189251Ssam			wstatus_set = 1;
851252726Srpaulo			retcode = WEXITSTATUS(wstatus);
852252726Srpaulo		}
853252726Srpaulo	}
854252726Srpaulo	child = 0;
855189251Ssam	prchild = 0;
856189251Ssam	if (stopped) {		/* restart output filter */
857189251Ssam		if (kill(of_pid, SIGCONT) < 0) {
858281806Srpaulo			syslog(LOG_ERR, "cannot restart output filter");
859252726Srpaulo			exit(1);
860189251Ssam		}
861281806Srpaulo	}
862189251Ssam	pp->tof = 0;
863189251Ssam
864252726Srpaulo	/* Copy the filter's output to "lf" logfile */
865214734Srpaulo	if ((fp = fopen(tempstderr, "r"))) {
866252726Srpaulo		while (fgets(buf, sizeof(buf), fp))
867189251Ssam			fputs(buf, stderr);
868189251Ssam		fclose(fp);
869189251Ssam	}
870189251Ssam
871189251Ssam	if (wstatus_set && !WIFEXITED(wstatus)) {
872214734Srpaulo		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
873214734Srpaulo		    pp->printer, format, WTERMSIG(wstatus));
874214734Srpaulo		return (ERROR);
875252726Srpaulo	}
876214734Srpaulo	switch (retcode) {
877214734Srpaulo	case 0:
878214734Srpaulo		pp->tof = 1;
879214734Srpaulo		return (OK);
880214734Srpaulo	case 1:
881214734Srpaulo		return (REPRINT);
882214734Srpaulo	case 2:
883214734Srpaulo		return (ERROR);
884214734Srpaulo	default:
885214734Srpaulo		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
886214734Srpaulo		    pp->printer, format, retcode);
887214734Srpaulo		return (FILTERERR);
888252726Srpaulo	}
889252726Srpaulo}
890252726Srpaulo
891252726Srpaulo/*
892252726Srpaulo * Send the daemon control file (cf) and any data files.
893252726Srpaulo * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
894252726Srpaulo * 0 if all is well.
895214734Srpaulo */
896214734Srpaulostatic int
897214734Srpaulosendit(struct printer *pp, char *file)
898214734Srpaulo{
899214734Srpaulo	int dfcopies, err, i;
900214734Srpaulo	char *cp, last[sizeof(line)];
901214734Srpaulo
902189251Ssam	/*
903252726Srpaulo	 * open control file
904189251Ssam	 */
905214734Srpaulo	if ((cfp = fopen(file, "r")) == NULL)
906214734Srpaulo		return (OK);
907252726Srpaulo
908214734Srpaulo	/* initialize job-specific count of datafiles processed */
909214734Srpaulo	job_dfcnt = 0;
910214734Srpaulo
911214734Srpaulo	/*
912214734Srpaulo	 *      read the control file for work to do
913189251Ssam	 *
914189251Ssam	 *      file format -- first character in the line is a command
915189251Ssam	 *      rest of the line is the argument.
916281806Srpaulo	 *      commands of interest are:
917189251Ssam	 *
918189251Ssam	 *            a-z -- "file name" name of file to print
919189251Ssam	 *              U -- "unlink" name of file to remove
920189251Ssam	 *                    (after we print it. (Pass 2 only)).
921189251Ssam	 */
922189251Ssam
923189251Ssam	/*
924252726Srpaulo	 * pass 1
925252726Srpaulo	 */
926252726Srpaulo	err = OK;
927252726Srpaulo	while (getline(cfp)) {
928252726Srpaulo	again:
929252726Srpaulo		if (line[0] == 'S') {
930252726Srpaulo			cp = line+1;
931214734Srpaulo			i = 0;
932214734Srpaulo			while (*cp >= '0' && *cp <= '9')
933214734Srpaulo				i = i * 10 + (*cp++ - '0');
934252726Srpaulo			fdev = i;
935214734Srpaulo			cp++;
936214734Srpaulo			i = 0;
937214734Srpaulo			while (*cp >= '0' && *cp <= '9')
938214734Srpaulo				i = i * 10 + (*cp++ - '0');
939214734Srpaulo			fino = i;
940214734Srpaulo		} else if (line[0] == 'H') {
941214734Srpaulo			strlcpy(origin_host, line + 1, sizeof(origin_host));
942214734Srpaulo			if (class[0] == '\0') {
943214734Srpaulo				strlcpy(class, line + 1, sizeof(class));
944214734Srpaulo			}
945214734Srpaulo		} else if (line[0] == 'P') {
946214734Srpaulo			strlcpy(logname, line + 1, sizeof(logname));
947214734Srpaulo			if (pp->restricted) { /* restricted */
948214734Srpaulo				if (getpwnam(logname) == NULL) {
949252726Srpaulo					sendmail(pp, line+1, NOACCT);
950252726Srpaulo					err = ERROR;
951252726Srpaulo					break;
952214734Srpaulo				}
953252726Srpaulo			}
954252726Srpaulo		} else if (line[0] == 'I') {
955214734Srpaulo			strlcpy(indent+2, line + 1, sizeof(indent) - 2);
956214734Srpaulo		} else if (line[0] >= 'a' && line[0] <= 'z') {
957214734Srpaulo			dfcopies = 1;
958252726Srpaulo			strcpy(last, line);
959214734Srpaulo			while ((i = getline(cfp)) != 0) {
960214734Srpaulo				if (strcmp(last, line) != 0)
961214734Srpaulo					break;
962214734Srpaulo				dfcopies++;
963214734Srpaulo			}
964214734Srpaulo			switch (sendfile(pp, '\3', last+1, *last, dfcopies)) {
965252726Srpaulo			case OK:
966214734Srpaulo				if (i)
967214734Srpaulo					goto again;
968214734Srpaulo				break;
969214734Srpaulo			case REPRINT:
970214734Srpaulo				(void) fclose(cfp);
971214734Srpaulo				return (REPRINT);
972214734Srpaulo			case ACCESS:
973214734Srpaulo				sendmail(pp, logname, ACCESS);
974214734Srpaulo			case ERROR:
975214734Srpaulo				err = ERROR;
976214734Srpaulo			}
977214734Srpaulo			break;
978214734Srpaulo		}
979252726Srpaulo	}
980252726Srpaulo	if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) {
981252726Srpaulo		(void) fclose(cfp);
982252726Srpaulo		return (REPRINT);
983252726Srpaulo	}
984252726Srpaulo	/*
985252726Srpaulo	 * pass 2
986252726Srpaulo	 */
987252726Srpaulo	fseek(cfp, 0L, 0);
988252726Srpaulo	while (getline(cfp))
989252726Srpaulo		if (line[0] == 'U' && !strchr(line+1, '/'))
990252726Srpaulo			(void) unlink(line+1);
991214734Srpaulo	/*
992214734Srpaulo	 * clean-up in case another control file exists
993252726Srpaulo	 */
994252726Srpaulo	(void) fclose(cfp);
995214734Srpaulo	(void) unlink(file);
996214734Srpaulo	return (err);
997214734Srpaulo}
998252726Srpaulo
999252726Srpaulo/*
1000214734Srpaulo * Send a data file to the remote machine and spool it.
1001214734Srpaulo * Return positive if we should try resending.
1002214734Srpaulo */
1003214734Srpaulostatic int
1004252726Srpaulosendfile(struct printer *pp, int type, char *file, char format, int copyreq)
1005252726Srpaulo{
1006252726Srpaulo	int i, amt;
1007252726Srpaulo	struct stat stb;
1008252726Srpaulo	char *av[15], *filtcmd;
1009252726Srpaulo	char buf[SPL_BUFSIZ], opt_c[4], opt_h[4], opt_n[4];
1010252726Srpaulo	int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc;
1011252726Srpaulo
1012252726Srpaulo	/* Make sure the entire data file has arrived. */
1013252726Srpaulo	wait4data(pp, file);
1014252726Srpaulo
1015252726Srpaulo	statrc = lstat(file, &stb);
1016252726Srpaulo	if (statrc < 0) {
1017252726Srpaulo		syslog(LOG_ERR, "%s: error from lstat(%s): %m",
1018252726Srpaulo		    pp->printer, file);
1019252726Srpaulo		return (ERROR);
1020252726Srpaulo	}
1021252726Srpaulo	sfd = open(file, O_RDONLY);
1022252726Srpaulo	if (sfd < 0) {
1023252726Srpaulo		syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m",
1024252726Srpaulo		    pp->printer, file);
1025252726Srpaulo		return (ERROR);
1026252726Srpaulo	}
1027252726Srpaulo	/*
1028252726Srpaulo	 * Check to see if data file is a symbolic link. If so, it should
1029252726Srpaulo	 * still point to the same file or someone is trying to print something
1030252726Srpaulo	 * he shouldn't.
1031252726Srpaulo	 */
1032252726Srpaulo	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 &&
1033252726Srpaulo	    (stb.st_dev != fdev || stb.st_ino != fino)) {
1034252726Srpaulo		close(sfd);
1035252726Srpaulo		return (ACCESS);
1036252726Srpaulo	}
1037252726Srpaulo
1038214734Srpaulo	/* Everything seems OK for reading the file, now to send it */
1039214734Srpaulo	filtcmd = NULL;
1040214734Srpaulo	sizerr = 0;
1041281806Srpaulo	tfd = -1;
1042252726Srpaulo	if (type == '\3') {
1043214734Srpaulo		/*
1044214734Srpaulo		 * Type == 3 means this is a datafile, not a control file.
1045214734Srpaulo		 * Increment the counter of data-files in this job, and
1046214734Srpaulo		 * then check for input or output filters (which are only
1047214734Srpaulo		 * applied to datafiles, not control files).
1048214734Srpaulo		 */
1049252726Srpaulo		job_dfcnt++;
1050252726Srpaulo
1051252726Srpaulo		/*
1052189251Ssam		 * Note that here we are filtering datafiles, one at a time,
1053252726Srpaulo		 * as they are sent to the remote machine.  Here, the *only*
1054252726Srpaulo		 * difference between an input filter (`if=') and an output
1055252726Srpaulo		 * filter (`of=') is the argument list that the filter is
1056252726Srpaulo		 * started up with.  Here, the output filter is executed
1057252726Srpaulo		 * for each individual file as it is sent.  This is not the
1058189251Ssam		 * same as local print queues, where the output filter is
1059189251Ssam		 * started up once, and then all jobs are passed thru that
1060252726Srpaulo		 * single invocation of the output filter.
1061252726Srpaulo		 *
1062189251Ssam		 * Also note that a queue for a remote-machine can have an
1063252726Srpaulo		 * input filter or an output filter, but not both.
1064189251Ssam		 */
1065189251Ssam		if (pp->filters[LPF_INPUT]) {
1066252726Srpaulo			filtcmd = pp->filters[LPF_INPUT];
1067252726Srpaulo			av[0] = filtcmd;
1068252726Srpaulo			narg = 0;
1069252726Srpaulo			strcpy(opt_c, "-c");
1070252726Srpaulo			strcpy(opt_h, "-h");
1071252726Srpaulo			strcpy(opt_n, "-n");
1072252726Srpaulo			if (format == 'l')
1073252726Srpaulo				av[++narg] = opt_c;
1074252726Srpaulo			av[++narg] = width;
1075252726Srpaulo			av[++narg] = length;
1076252726Srpaulo			av[++narg] = indent;
1077252726Srpaulo			av[++narg] = opt_n;
1078189251Ssam			av[++narg] = logname;
1079189251Ssam			av[++narg] = opt_h;
1080189251Ssam			av[++narg] = origin_host;
1081189251Ssam			av[++narg] = pp->acct_file;
1082189251Ssam			av[++narg] = NULL;
1083189251Ssam		} else if (pp->filters[LPF_OUTPUT]) {
1084189251Ssam			filtcmd = pp->filters[LPF_OUTPUT];
1085189251Ssam			av[0] = filtcmd;
1086189251Ssam			narg = 0;
1087189251Ssam			av[++narg] = width;
1088189251Ssam			av[++narg] = length;
1089189251Ssam			av[++narg] = NULL;
1090189251Ssam		}
1091189251Ssam	}
1092189251Ssam	if (filtcmd) {
1093281806Srpaulo		/*
1094189251Ssam		 * If there is an input or output filter, we have to run
1095189251Ssam		 * the datafile thru that filter and store the result as
1096189251Ssam		 * a temporary spool file, because the protocol requires
1097189251Ssam		 * that we send the remote host the file-size before we
1098189251Ssam		 * start to send any of the data.
1099189251Ssam		 */
1100281806Srpaulo		strcpy(tfile, TFILENAME);
1101189251Ssam		tfd = mkstemp(tfile);
1102189251Ssam		if (tfd == -1) {
1103189251Ssam			syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
1104189251Ssam			    TFILENAME);
1105189251Ssam			sfres = ERROR;
1106189251Ssam			goto return_sfres;
1107189251Ssam		}
1108189251Ssam		filtstat = execfilter(pp, filtcmd, av, sfd, tfd);
1109189251Ssam
1110189251Ssam		/* process the return-code from the filter */
1111189251Ssam		switch (filtstat) {
1112189251Ssam		case 0:
1113189251Ssam			break;
1114189251Ssam		case 1:
1115189251Ssam			sfres = REPRINT;
1116189251Ssam			goto return_sfres;
1117189251Ssam		case 2:
1118189251Ssam			sfres = ERROR;
1119189251Ssam			goto return_sfres;
1120189251Ssam		default:
1121189251Ssam			syslog(LOG_WARNING,
1122189251Ssam			    "%s: filter '%c' exited (retcode=%d)",
1123189251Ssam			    pp->printer, format, filtstat);
1124189251Ssam			sfres = FILTERERR;
1125189251Ssam			goto return_sfres;
1126281806Srpaulo		}
1127189251Ssam		statrc = fstat(tfd, &stb);   /* to find size of tfile */
1128189251Ssam		if (statrc < 0)	{
1129189251Ssam			syslog(LOG_ERR,
1130189251Ssam			    "%s: error processing 'if', fstat(%s): %m",
1131189251Ssam			    pp->printer, tfile);
1132189251Ssam			sfres = ERROR;
1133281806Srpaulo			goto return_sfres;
1134189251Ssam		}
1135189251Ssam		close(sfd);
1136189251Ssam		sfd = tfd;
1137189251Ssam		lseek(sfd, 0, SEEK_SET);
1138189251Ssam	}
1139189251Ssam
1140189251Ssam	copycnt = 0;
1141189251Ssamsendagain:
1142189251Ssam	copycnt++;
1143189251Ssam
1144189251Ssam	if (copycnt < 2)
1145189251Ssam		(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
1146189251Ssam	else
1147189251Ssam		(void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size,
1148189251Ssam		    file, copycnt);
1149189251Ssam	amt = strlen(buf);
1150189251Ssam	for (i = 0;  ; i++) {
1151189251Ssam		if (write(pfd, buf, amt) != amt ||
1152189251Ssam		    (resp = response(pp)) < 0 || resp == '\1') {
1153189251Ssam			sfres = REPRINT;
1154189251Ssam			goto return_sfres;
1155189251Ssam		} else if (resp == '\0')
1156189251Ssam			break;
1157189251Ssam		if (i == 0)
1158189251Ssam			pstatus(pp,
1159189251Ssam				"no space on remote; waiting for queue to drain");
1160281806Srpaulo		if (i == 10)
1161189251Ssam			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
1162189251Ssam			    pp->printer, pp->remote_host);
1163189251Ssam		sleep(5 * 60);
1164189251Ssam	}
1165189251Ssam	if (i)
1166189251Ssam		pstatus(pp, "sending to %s", pp->remote_host);
1167281806Srpaulo	/*
1168189251Ssam	 * XXX - we should change trstat_init()/trstat_write() to include
1169189251Ssam	 *	 the copycnt in the statistics record it may write.
1170189251Ssam	 */
1171189251Ssam	if (type == '\3')
1172189251Ssam		trstat_init(pp, file, job_dfcnt);
1173189251Ssam	for (i = 0; i < stb.st_size; i += SPL_BUFSIZ) {
1174189251Ssam		amt = SPL_BUFSIZ;
1175189251Ssam		if (i + amt > stb.st_size)
1176189251Ssam			amt = stb.st_size - i;
1177189251Ssam		if (sizerr == 0 && read(sfd, buf, amt) != amt)
1178189251Ssam			sizerr = 1;
1179189251Ssam		if (write(pfd, buf, amt) != amt) {
1180189251Ssam			sfres = REPRINT;
1181189251Ssam			goto return_sfres;
1182189251Ssam		}
1183189251Ssam	}
1184189251Ssam
1185189251Ssam	if (sizerr) {
1186189251Ssam		syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file);
1187189251Ssam		/* tell recvjob to ignore this file */
1188189251Ssam		(void) write(pfd, "\1", 1);
1189189251Ssam		sfres = ERROR;
1190189251Ssam		goto return_sfres;
1191189251Ssam	}
1192189251Ssam	if (write(pfd, "", 1) != 1 || response(pp)) {
1193281806Srpaulo		sfres = REPRINT;
1194189251Ssam		goto return_sfres;
1195189251Ssam	}
1196189251Ssam	if (type == '\3') {
1197189251Ssam		trstat_write(pp, TR_SENDING, stb.st_size, logname,
1198189251Ssam		    pp->remote_host, origin_host);
1199189251Ssam		/*
1200281806Srpaulo		 * Usually we only need to send one copy of a datafile,
1201189251Ssam		 * because the control-file will simply print the same
1202189251Ssam		 * file multiple times.  However, some printers ignore
1203189251Ssam		 * the control file, and simply print each data file as
1204189251Ssam		 * it arrives.  For such "remote hosts", we need to
1205189251Ssam		 * transfer the same data file multiple times.  Such a
1206189251Ssam		 * a host is indicated by adding 'rc' to the printcap
1207189251Ssam		 * entry.
1208189251Ssam		 * XXX - Right now this ONLY works for remote hosts which
1209189251Ssam		 *	do ignore the name of the data file, because
1210189251Ssam		 *	this sends the file multiple times with slight
1211189251Ssam		 *	changes to the filename.  To do this right would
1212189251Ssam		 *	require that we also rewrite the control file
1213189251Ssam		 *	to match those filenames.
1214189251Ssam		 */
1215189251Ssam		if (pp->resend_copies && (copycnt < copyreq)) {
1216189251Ssam			lseek(sfd, 0, SEEK_SET);
1217189251Ssam			goto sendagain;
1218189251Ssam		}
1219189251Ssam	}
1220189251Ssam	sfres = OK;
1221189251Ssam
1222189251Ssamreturn_sfres:
1223189251Ssam	(void)close(sfd);
1224189251Ssam	if (tfd != -1) {
1225281806Srpaulo		/*
1226189251Ssam		 * If tfd is set, then it is the same value as sfd, and
1227189251Ssam		 * therefore it is already closed at this point.  All
1228189251Ssam		 * we need to do is remove the temporary file.
1229189251Ssam		 */
1230189251Ssam		tfd = -1;
1231189251Ssam		unlink(tfile);
1232281806Srpaulo	}
1233189251Ssam	return (sfres);
1234189251Ssam}
1235189251Ssam
1236189251Ssam/*
1237189251Ssam * Some print servers send the control-file first, and then start sending the
1238189251Ssam * matching data file(s).  That is not the correct order.  If some queue is
1239189251Ssam * already printing an active job, then when that job is finished the queue
1240189251Ssam * may proceed to the control file of any incoming print job.  This turns
1241189251Ssam * into a race between the process which is receiving the data file, and the
1242189251Ssam * process which is actively printing the very same file.  When the remote
1243281806Srpaulo * server sends files in the wrong order, it is even possible that a queue
1244281806Srpaulo * will start to print a data file before the file has been created!
1245281806Srpaulo *
1246281806Srpaulo * So before we start to print() or send() a data file, we call this routine
1247281806Srpaulo * to make sure the data file is not still changing in size.  Note that this
1248281806Srpaulo * problem will only happen for jobs arriving from a remote host, and that
1249281806Srpaulo * the process which has decided to print this job (and is thus making this
1250281806Srpaulo * check) is *not* the process which is receiving the job.
1251281806Srpaulo *
1252281806Srpaulo * A second benefit of this is that any incoming job is guaranteed to appear
1253281806Srpaulo * in a queue listing for at least a few seconds after it has arrived.  Some
1254281806Srpaulo * lpr implementations get confused if they send a job and it disappears
1255281806Srpaulo * from the queue before they can check on it.
1256281806Srpaulo */
1257281806Srpaulo#define	MAXWAIT_ARRIVE	16	    /* max to wait for the file to *exist* */
1258281806Srpaulo#define	MAXWAIT_4DATA	(20*60)	    /* max to wait for it to stop changing */
1259281806Srpaulo#define	MINWAIT_4DATA	4	    /* This value must be >= 1 */
1260281806Srpaulo#define	DEBUG_MINWAIT	1
1261281806Srpaulostatic void
1262281806Srpaulowait4data(struct printer *pp, const char *dfile)
1263281806Srpaulo{
1264281806Srpaulo	const char *cp;
1265281806Srpaulo	int statres;
1266281806Srpaulo	u_int sleepreq;
1267281806Srpaulo	size_t dlen, hlen;
1268281806Srpaulo	time_t amtslept, cur_time, prev_mtime;
1269281806Srpaulo	struct stat statdf;
1270281806Srpaulo
1271281806Srpaulo	/* Skip these checks if the print job is from the local host. */
1272281806Srpaulo	dlen = strlen(dfile);
1273281806Srpaulo	hlen = strlen(local_host);
1274281806Srpaulo	if (dlen > hlen) {
1275189251Ssam		cp = dfile + dlen - hlen;
1276189251Ssam		if (strcmp(cp, local_host) == 0)
1277189251Ssam			return;
1278189251Ssam	}
1279189251Ssam
1280189251Ssam	/*
1281189251Ssam	 * If this data file does not exist, then wait up to MAXWAIT_ARRIVE
1282189251Ssam	 * seconds for it to arrive.
1283189251Ssam	 */
1284189251Ssam	amtslept = 0;
1285189251Ssam	statres = stat(dfile, &statdf);
1286189251Ssam	while (statres < 0 && amtslept < MAXWAIT_ARRIVE) {
1287189251Ssam		if (amtslept == 0)
1288189251Ssam			pstatus(pp, "Waiting for data file from remote host");
1289189251Ssam		amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA);
1290189251Ssam		statres = stat(dfile, &statdf);
1291281806Srpaulo	}
1292189251Ssam	if (statres < 0) {
1293189251Ssam		/* The file still does not exist, so just give up on it. */
1294189251Ssam		syslog(LOG_WARNING, "%s: wait4data() abandoned wait for %s",
1295189251Ssam		    pp->printer, dfile);
1296189251Ssam		return;
1297189251Ssam	}
1298281806Srpaulo
1299189251Ssam	/*
1300189251Ssam	 * The file exists, so keep waiting until the data file has not
1301189251Ssam	 * changed for some reasonable amount of time.  Extra care is
1302189251Ssam	 * taken when computing wait-times, just in case there are data
1303189251Ssam	 * files with a last-modify time in the future.  While that is
1304189251Ssam	 * very unlikely to happen, it can happen when the system has
1305189251Ssam	 * a flakey time-of-day clock.
1306189251Ssam	 */
1307189251Ssam	prev_mtime = statdf.st_mtime;
1308189251Ssam	cur_time = time(NULL);
1309189251Ssam	if (statdf.st_mtime >= cur_time - MINWAIT_4DATA) {
1310189251Ssam		if (statdf.st_mtime >= cur_time)	/* some TOD oddity */
1311189251Ssam			sleepreq = MINWAIT_4DATA;
1312189251Ssam		else
1313189251Ssam			sleepreq = cur_time - statdf.st_mtime;
1314189251Ssam		if (amtslept == 0)
1315189251Ssam			pstatus(pp, "Waiting for data file from remote host");
1316189251Ssam		amtslept += sleepreq - sleep(sleepreq);
1317252726Srpaulo		statres = stat(dfile, &statdf);
1318252726Srpaulo	}
1319189251Ssam	sleepreq = MINWAIT_4DATA;
1320252726Srpaulo	while (statres == 0 && amtslept < MAXWAIT_4DATA) {
1321252726Srpaulo		if (statdf.st_mtime == prev_mtime)
1322252726Srpaulo			break;
1323252726Srpaulo		prev_mtime = statdf.st_mtime;
1324189251Ssam		amtslept += sleepreq - sleep(sleepreq);
1325189251Ssam		statres = stat(dfile, &statdf);
1326189251Ssam	}
1327252726Srpaulo
1328252726Srpaulo	if (statres != 0)
1329252726Srpaulo		syslog(LOG_WARNING, "%s: %s disappeared during wait4data()",
1330252726Srpaulo		    pp->printer, dfile);
1331252726Srpaulo	else if (amtslept > MAXWAIT_4DATA)
1332252726Srpaulo		syslog(LOG_WARNING,
1333189251Ssam		    "%s: %s still changing after %lu secs in wait4data()",
1334189251Ssam		    pp->printer, dfile, (unsigned long)amtslept);
1335189251Ssam#if DEBUG_MINWAIT
1336189251Ssam	else if (amtslept > MINWAIT_4DATA)
1337189251Ssam		syslog(LOG_INFO, "%s: slept %lu secs in wait4data(%s)",
1338189251Ssam		    pp->printer, (unsigned long)amtslept, dfile);
1339189251Ssam#endif
1340189251Ssam}
1341189251Ssam#undef	MAXWAIT_ARRIVE
1342189251Ssam#undef	MAXWAIT_4DATA
1343252726Srpaulo#undef	MINWAIT_4DATA
1344189251Ssam
1345189251Ssam/*
1346189251Ssam *  This routine is called to execute one of the filters as was
1347189251Ssam *  specified in a printcap entry.  While the child-process will read
1348189251Ssam *  all of 'infd', it is up to the caller to close that file descriptor
1349189251Ssam *  in the parent process.
1350252726Srpaulo */
1351189251Ssamstatic int
1352189251Ssamexecfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd)
1353189251Ssam{
1354189251Ssam	pid_t fpid, wpid;
1355189251Ssam	int errfd, retcode, wstatus;
1356189251Ssam	FILE *errfp;
1357252726Srpaulo	char buf[BUFSIZ], *slash;
1358189251Ssam
1359189251Ssam	fpid = dofork(pp, DORETURN);
1360189251Ssam	if (fpid != 0) {
1361189251Ssam		/*
1362189251Ssam		 * This is the parent process, which just waits for the child
1363189251Ssam		 * to complete and then returns the result.  Note that it is
1364189251Ssam		 * the child process which reads the input stream.
1365189251Ssam		 */
1366189251Ssam		if (fpid < 0)
1367189251Ssam			retcode = 100;
1368189251Ssam		else {
1369189251Ssam			while ((wpid = wait(&wstatus)) > 0 &&
1370189251Ssam			    wpid != fpid)
1371252726Srpaulo				;
1372189251Ssam			if (wpid < 0) {
1373189251Ssam				retcode = 100;
1374189251Ssam				syslog(LOG_WARNING,
1375189251Ssam				    "%s: after execv(%s), wait() returned: %m",
1376189251Ssam				    pp->printer, f_cmd);
1377189251Ssam			} else
1378189251Ssam				retcode = WEXITSTATUS(wstatus);
1379189251Ssam		}
1380189251Ssam
1381189251Ssam		/*
1382189251Ssam		 * Copy everything the filter wrote to stderr from our
1383189251Ssam		 * temporary errors file to the "lf=" logfile.
1384189251Ssam		 */
1385189251Ssam		errfp = fopen(tempstderr, "r");
1386189251Ssam		if (errfp) {
1387189251Ssam			while (fgets(buf, sizeof(buf), errfp))
1388189251Ssam				fputs(buf, stderr);
1389189251Ssam			fclose(errfp);
1390189251Ssam		}
1391189251Ssam
1392189251Ssam		return (retcode);
1393189251Ssam	}
1394189251Ssam
1395189251Ssam	/*
1396189251Ssam	 * This is the child process, which is the one that executes the
1397189251Ssam	 * given filter.
1398189251Ssam	 */
1399189251Ssam	/*
1400189251Ssam	 * If the first parameter has any slashes in it, then change it
1401189251Ssam	 * to point to the first character after the last slash.
1402189251Ssam	 */
1403189251Ssam	slash = strrchr(f_av[0], '/');
1404252726Srpaulo	if (slash != NULL)
1405189251Ssam		f_av[0] = slash + 1;
1406189251Ssam	/*
1407189251Ssam	 * XXX - in the future, this should setup an explicit list of
1408189251Ssam	 *       environment variables and use execve()!
1409189251Ssam	 */
1410252726Srpaulo
1411189251Ssam	/*
1412189251Ssam	 * Setup stdin, stdout, and stderr as we want them when the filter
1413189251Ssam	 * is running.  Stderr is setup so it points to a temporary errors
1414189251Ssam	 * file, and the parent process will copy that temporary file to
1415189251Ssam	 * the real logfile after the filter completes.
1416189251Ssam	 */
1417189251Ssam	dup2(infd, STDIN_FILENO);
1418189251Ssam	dup2(outfd, STDOUT_FILENO);
1419189251Ssam	errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
1420189251Ssam	if (errfd >= 0)
1421189251Ssam		dup2(errfd, STDERR_FILENO);
1422189251Ssam	closelog();
1423189251Ssam	closeallfds(3);
1424189251Ssam	execv(f_cmd, f_av);
1425189251Ssam	syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd);
1426189251Ssam	exit(2);
1427189251Ssam	/* NOTREACHED */
1428252726Srpaulo}
1429252726Srpaulo
1430252726Srpaulo/*
1431252726Srpaulo * Check to make sure there have been no errors and that both programs
1432281806Srpaulo * are in sync with eachother.
1433281806Srpaulo * Return non-zero if the connection was lost.
1434281806Srpaulo */
1435281806Srpaulostatic char
1436281806Srpauloresponse(const struct printer *pp)
1437281806Srpaulo{
1438281806Srpaulo	char resp;
1439281806Srpaulo
1440281806Srpaulo	if (read(pfd, &resp, 1) != 1) {
1441281806Srpaulo		syslog(LOG_INFO, "%s: lost connection", pp->printer);
1442281806Srpaulo		return (-1);
1443281806Srpaulo	}
1444281806Srpaulo	return (resp);
1445281806Srpaulo}
1446281806Srpaulo
1447281806Srpaulo/*
1448281806Srpaulo * Banner printing stuff
1449281806Srpaulo */
1450252726Srpaulostatic void
1451252726Srpaulobanner(struct printer *pp, char *name1, char *name2)
1452252726Srpaulo{
1453252726Srpaulo	time_t tvec;
1454252726Srpaulo
1455252726Srpaulo	time(&tvec);
1456252726Srpaulo	if (!pp->no_formfeed && !pp->tof)
1457252726Srpaulo		(void) write(ofd, pp->form_feed, strlen(pp->form_feed));
1458252726Srpaulo	if (pp->short_banner) {	/* short banner only */
1459252726Srpaulo		if (class[0]) {
1460252726Srpaulo			(void) write(ofd, class, strlen(class));
1461252726Srpaulo			(void) write(ofd, ":", 1);
1462252726Srpaulo		}
1463252726Srpaulo		(void) write(ofd, name1, strlen(name1));
1464252726Srpaulo		(void) write(ofd, "  Job: ", 7);
1465252726Srpaulo		(void) write(ofd, name2, strlen(name2));
1466252726Srpaulo		(void) write(ofd, "  Date: ", 8);
1467252726Srpaulo		(void) write(ofd, ctime(&tvec), 24);
1468252726Srpaulo		(void) write(ofd, "\n", 1);
1469252726Srpaulo	} else {	/* normal banner */
1470252726Srpaulo		(void) write(ofd, "\n\n\n", 3);
1471252726Srpaulo		scan_out(pp, ofd, name1, '\0');
1472252726Srpaulo		(void) write(ofd, "\n\n", 2);
1473252726Srpaulo		scan_out(pp, ofd, name2, '\0');
1474252726Srpaulo		if (class[0]) {
1475189251Ssam			(void) write(ofd,"\n\n\n",3);
1476189251Ssam			scan_out(pp, ofd, class, '\0');
1477252726Srpaulo		}
1478252726Srpaulo		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
1479189251Ssam		(void) write(ofd, name2, strlen(name2));
1480189251Ssam		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
1481189251Ssam		(void) write(ofd, ctime(&tvec), 24);
1482281806Srpaulo		(void) write(ofd, "\n", 1);
1483281806Srpaulo	}
1484281806Srpaulo	if (!pp->no_formfeed)
1485281806Srpaulo		(void) write(ofd, pp->form_feed, strlen(pp->form_feed));
1486281806Srpaulo	pp->tof = 1;
1487281806Srpaulo}
1488281806Srpaulo
1489281806Srpaulostatic char *
1490281806Srpauloscnline(int key, char *p, int c)
1491281806Srpaulo{
1492281806Srpaulo	register int scnwidth;
1493281806Srpaulo
1494189251Ssam	for (scnwidth = WIDTH; --scnwidth;) {
1495189251Ssam		key <<= 1;
1496189251Ssam		*p++ = key & 0200 ? c : BACKGND;
1497189251Ssam	}
1498189251Ssam	return (p);
1499189251Ssam}
1500189251Ssam
1501189251Ssam#define TRC(q)	(((q)-' ')&0177)
1502189251Ssam
1503189251Ssamstatic void
1504189251Ssamscan_out(struct printer *pp, int scfd, char *scsp, int dlm)
1505189251Ssam{
1506189251Ssam	register char *strp;
1507189251Ssam	register int nchrs, j;
1508189251Ssam	char outbuf[LINELEN+1], *sp, c, cc;
1509189251Ssam	int d, scnhgt;
1510189251Ssam
1511189251Ssam	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
1512189251Ssam		strp = &outbuf[0];
1513189251Ssam		sp = scsp;
1514189251Ssam		for (nchrs = 0; ; ) {
1515189251Ssam			d = dropit(c = TRC(cc = *sp++));
1516189251Ssam			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
1517281806Srpaulo				for (j = WIDTH; --j;)
1518189251Ssam					*strp++ = BACKGND;
1519189251Ssam			else
1520189251Ssam				strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc);
1521189251Ssam			if (*sp == dlm || *sp == '\0' ||
1522189251Ssam			    nchrs++ >= pp->page_width/(WIDTH+1)-1)
1523189251Ssam				break;
1524189251Ssam			*strp++ = BACKGND;
1525189251Ssam			*strp++ = BACKGND;
1526189251Ssam		}
1527189251Ssam		while (*--strp == BACKGND && strp >= outbuf)
1528189251Ssam			;
1529189251Ssam		strp++;
1530252726Srpaulo		*strp++ = '\n';
1531252726Srpaulo		(void) write(scfd, outbuf, strp-outbuf);
1532189251Ssam	}
1533252726Srpaulo}
1534252726Srpaulo
1535252726Srpaulostatic int
1536252726Srpaulodropit(int c)
1537252726Srpaulo{
1538252726Srpaulo	switch(c) {
1539252726Srpaulo
1540252726Srpaulo	case TRC('_'):
1541252726Srpaulo	case TRC(';'):
1542252726Srpaulo	case TRC(','):
1543189251Ssam	case TRC('g'):
1544189251Ssam	case TRC('j'):
1545252726Srpaulo	case TRC('p'):
1546189251Ssam	case TRC('q'):
1547189251Ssam	case TRC('y'):
1548189251Ssam		return (DROP);
1549189251Ssam
1550189251Ssam	default:
1551189251Ssam		return (0);
1552189251Ssam	}
1553189251Ssam}
1554189251Ssam
1555189251Ssam/*
1556189251Ssam * sendmail ---
1557189251Ssam *   tell people about job completion
1558189251Ssam */
1559189251Ssamstatic void
1560189251Ssamsendmail(struct printer *pp, char *userid, int bombed)
1561189251Ssam{
1562189251Ssam	register int i;
1563189251Ssam	int p[2], s;
1564252726Srpaulo	register const char *cp;
1565189251Ssam	struct stat stb;
1566189251Ssam	FILE *fp;
1567189251Ssam
1568189251Ssam	pipe(p);
1569189251Ssam	if ((s = dofork(pp, DORETURN)) == 0) {		/* child */
1570189251Ssam		dup2(p[0], STDIN_FILENO);
1571189251Ssam		closelog();
1572189251Ssam		closeallfds(3);
1573189251Ssam		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
1574189251Ssam			cp++;
1575189251Ssam		else
1576189251Ssam			cp = _PATH_SENDMAIL;
1577189251Ssam		execl(_PATH_SENDMAIL, cp, "-t", (char *)0);
1578189251Ssam		_exit(0);
1579189251Ssam	} else if (s > 0) {				/* parent */
1580189251Ssam		dup2(p[1], STDOUT_FILENO);
1581189251Ssam		printf("To: %s@%s\n", userid, origin_host);
1582189251Ssam		printf("Subject: %s printer job \"%s\"\n", pp->printer,
1583189251Ssam			*jobname ? jobname : "<unknown>");
1584189251Ssam		printf("Reply-To: root@%s\n\n", local_host);
1585281806Srpaulo		printf("Your printer job ");
1586281806Srpaulo		if (*jobname)
1587281806Srpaulo			printf("(%s) ", jobname);
1588281806Srpaulo
1589189251Ssam		switch (bombed) {
1590281806Srpaulo		case OK:
1591189251Ssam			cp = "OK";
1592189251Ssam			printf("\ncompleted successfully\n");
1593189251Ssam			break;
1594189251Ssam		default:
1595189251Ssam		case FATALERR:
1596189251Ssam			cp = "FATALERR";
1597189251Ssam			printf("\ncould not be printed\n");
1598189251Ssam			break;
1599189251Ssam		case NOACCT:
1600189251Ssam			cp = "NOACCT";
1601189251Ssam			printf("\ncould not be printed without an account on %s\n",
1602189251Ssam			    local_host);
1603189251Ssam			break;
1604189251Ssam		case FILTERERR:
1605189251Ssam			cp = "FILTERERR";
1606189251Ssam			if (stat(tempstderr, &stb) < 0 || stb.st_size == 0
1607189251Ssam			    || (fp = fopen(tempstderr, "r")) == NULL) {
1608189251Ssam				printf("\nhad some errors and may not have printed\n");
1609189251Ssam				break;
1610189251Ssam			}
1611189251Ssam			printf("\nhad the following errors and may not have printed:\n");
1612189251Ssam			while ((i = getc(fp)) != EOF)
1613189251Ssam				putchar(i);
1614189251Ssam			(void) fclose(fp);
1615189251Ssam			break;
1616189251Ssam		case ACCESS:
1617189251Ssam			cp = "ACCESS";
1618189251Ssam			printf("\nwas not printed because it was not linked to the original file\n");
1619189251Ssam		}
1620189251Ssam		fflush(stdout);
1621189251Ssam		(void) close(STDOUT_FILENO);
1622189251Ssam	} else {
1623189251Ssam		syslog(LOG_WARNING, "unable to send mail to %s: %m", userid);
1624189251Ssam		return;
1625189251Ssam	}
1626189251Ssam	(void) close(p[0]);
1627189251Ssam	(void) close(p[1]);
1628189251Ssam	wait(NULL);
1629189251Ssam	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
1630189251Ssam	    userid, *jobname ? jobname : "<unknown>", pp->printer, cp);
1631189251Ssam}
1632189251Ssam
1633189251Ssam/*
1634189251Ssam * dofork - fork with retries on failure
1635189251Ssam */
1636189251Ssamstatic int
1637189251Ssamdofork(const struct printer *pp, int action)
1638281806Srpaulo{
1639189251Ssam	pid_t forkpid;
1640189251Ssam	int i, fail;
1641189251Ssam	struct passwd *pwd;
1642189251Ssam
1643189251Ssam	forkpid = -1;
1644189251Ssam	if (daemon_uname == NULL) {
1645189251Ssam		pwd = getpwuid(pp->daemon_user);
1646189251Ssam		if (pwd == NULL) {
1647189251Ssam			syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file",
1648252726Srpaulo			    pp->printer, pp->daemon_user);
1649189251Ssam			goto error_ret;
1650189251Ssam		}
1651189251Ssam		daemon_uname = strdup(pwd->pw_name);
1652189251Ssam		daemon_defgid = pwd->pw_gid;
1653189251Ssam	}
1654189251Ssam
1655189251Ssam	for (i = 0; i < 20; i++) {
1656189251Ssam		forkpid = fork();
1657189251Ssam		if (forkpid < 0) {
1658189251Ssam			sleep((unsigned)(i*i));
1659214734Srpaulo			continue;
1660214734Srpaulo		}
1661214734Srpaulo		/*
1662252726Srpaulo		 * Child should run as daemon instead of root
1663214734Srpaulo		 */
1664214734Srpaulo		if (forkpid == 0) {
1665214734Srpaulo			errno = 0;
1666214734Srpaulo			fail = initgroups(daemon_uname, daemon_defgid);
1667214734Srpaulo			if (fail) {
1668214734Srpaulo				syslog(LOG_ERR, "%s: initgroups(%s,%u): %m",
1669214734Srpaulo				    pp->printer, daemon_uname, daemon_defgid);
1670214734Srpaulo				break;
1671214734Srpaulo			}
1672214734Srpaulo			fail = setgid(daemon_defgid);
1673214734Srpaulo			if (fail) {
1674214734Srpaulo				syslog(LOG_ERR, "%s: setgid(%u): %m",
1675214734Srpaulo				    pp->printer, daemon_defgid);
1676214734Srpaulo				break;
1677214734Srpaulo			}
1678252726Srpaulo			fail = setuid(pp->daemon_user);
1679214734Srpaulo			if (fail) {
1680214734Srpaulo				syslog(LOG_ERR, "%s: setuid(%ld): %m",
1681214734Srpaulo				    pp->printer, pp->daemon_user);
1682214734Srpaulo				break;
1683214734Srpaulo			}
1684214734Srpaulo		}
1685214734Srpaulo		return (forkpid);
1686214734Srpaulo	}
1687214734Srpaulo
1688214734Srpaulo	/*
1689252726Srpaulo	 * An error occurred.  If the error is in the child process, then
1690214734Srpaulo	 * this routine MUST always exit().  DORETURN only effects how
1691214734Srpaulo	 * errors should be handled in the parent process.
1692214734Srpaulo	 */
1693214734Srpauloerror_ret:
1694214734Srpaulo	if (forkpid == 0) {
1695214734Srpaulo		syslog(LOG_ERR, "%s: dofork(): aborting child process...",
1696214734Srpaulo		    pp->printer);
1697214734Srpaulo		exit(1);
1698214734Srpaulo	}
1699214734Srpaulo	syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer);
1700214734Srpaulo
1701214734Srpaulo	sleep(1);		/* throttle errors, as a safety measure */
1702214734Srpaulo	switch (action) {
1703214734Srpaulo	case DORETURN:
1704214734Srpaulo		return (-1);
1705214734Srpaulo	default:
1706214734Srpaulo		syslog(LOG_ERR, "bad action (%d) to dofork", action);
1707214734Srpaulo		/* FALLTHROUGH */
1708214734Srpaulo	case DOABORT:
1709214734Srpaulo		exit(1);
1710214734Srpaulo	}
1711214734Srpaulo	/*NOTREACHED*/
1712214734Srpaulo}
1713214734Srpaulo
1714252726Srpaulo/*
1715252726Srpaulo * Kill child processes to abort current job.
1716252726Srpaulo */
1717252726Srpaulostatic void
1718252726Srpauloabortpr(int signo __unused)
1719252726Srpaulo{
1720252726Srpaulo
1721252726Srpaulo	(void) unlink(tempstderr);
1722252726Srpaulo	kill(0, SIGINT);
1723252726Srpaulo	if (of_pid > 0)
1724252726Srpaulo		kill(of_pid, SIGCONT);
1725252726Srpaulo	while (wait(NULL) > 0)
1726252726Srpaulo		;
1727252726Srpaulo	if (of_pid > 0 && tfd != -1)
1728281806Srpaulo		unlink(tfile);
1729281806Srpaulo	exit(0);
1730281806Srpaulo}
1731281806Srpaulo
1732281806Srpaulostatic void
1733281806Srpauloinit(struct printer *pp)
1734281806Srpaulo{
1735214734Srpaulo	char *s;
1736214734Srpaulo
1737214734Srpaulo	sprintf(&width[2], "%ld", pp->page_width);
1738214734Srpaulo	sprintf(&length[2], "%ld", pp->page_length);
1739214734Srpaulo	sprintf(&pxwidth[2], "%ld", pp->page_pwidth);
1740214734Srpaulo	sprintf(&pxlength[2], "%ld", pp->page_plength);
1741214734Srpaulo	if ((s = checkremote(pp)) != 0) {
1742214734Srpaulo		syslog(LOG_WARNING, "%s", s);
1743214734Srpaulo		free(s);
1744214734Srpaulo	}
1745214734Srpaulo}
1746214734Srpaulo
1747214734Srpaulovoid
1748214734Srpaulostartprinting(const char *printer)
1749214734Srpaulo{
1750281806Srpaulo	struct printer myprinter, *pp = &myprinter;
1751214734Srpaulo	int status;
1752214734Srpaulo
1753214734Srpaulo	init_printer(pp);
1754214734Srpaulo	status = getprintcap(printer, pp);
1755281806Srpaulo	switch(status) {
1756214734Srpaulo	case PCAPERR_OSERR:
1757214734Srpaulo		syslog(LOG_ERR, "can't open printer description file: %m");
1758214734Srpaulo		exit(1);
1759214734Srpaulo	case PCAPERR_NOTFOUND:
1760252726Srpaulo		syslog(LOG_ERR, "unknown printer: %s", printer);
1761252726Srpaulo		exit(1);
1762252726Srpaulo	case PCAPERR_TCLOOP:
1763252726Srpaulo		fatal(pp, "potential reference loop detected in printcap file");
1764281806Srpaulo	default:
1765281806Srpaulo		break;
1766281806Srpaulo	}
1767281806Srpaulo	printjob(pp);
1768281806Srpaulo}
1769281806Srpaulo
1770281806Srpaulo/*
1771281806Srpaulo * Acquire line printer or remote connection.
1772281806Srpaulo */
1773281806Srpaulostatic void
1774281806Srpauloopenpr(const struct printer *pp)
1775281806Srpaulo{
1776281806Srpaulo	int p[2];
1777281806Srpaulo	char *cp;
1778281806Srpaulo
1779281806Srpaulo	if (pp->remote) {
1780281806Srpaulo		openrem(pp);
1781281806Srpaulo		/*
1782281806Srpaulo		 * Lpd does support the setting of 'of=' filters for
1783281806Srpaulo		 * jobs going to remote machines, but that does not
1784281806Srpaulo		 * have the same meaning as 'of=' does when handling
1785281806Srpaulo		 * local print queues.  For remote machines, all 'of='
1786281806Srpaulo		 * filter processing is handled in sendfile(), and that
1787281806Srpaulo		 * does not use these global "output filter" variables.
1788281806Srpaulo		 */
1789252726Srpaulo		ofd = -1;
1790252726Srpaulo		of_pid = 0;
1791252726Srpaulo		return;
1792252726Srpaulo	} else if (*pp->lp) {
1793252726Srpaulo		if ((cp = strchr(pp->lp, '@')) != NULL)
1794252726Srpaulo			opennet(pp);
1795252726Srpaulo		else
1796252726Srpaulo			opentty(pp);
1797252726Srpaulo	} else {
1798252726Srpaulo		syslog(LOG_ERR, "%s: no line printer device or host name",
1799252726Srpaulo		    pp->printer);
1800252726Srpaulo		exit(1);
1801252726Srpaulo	}
1802252726Srpaulo
1803252726Srpaulo	/*
1804252726Srpaulo	 * Start up an output filter, if needed.
1805252726Srpaulo	 */
1806252726Srpaulo	if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) {
1807252726Srpaulo		pipe(p);
1808252726Srpaulo		if (pp->remote) {
1809252726Srpaulo			strcpy(tfile, TFILENAME);
1810252726Srpaulo			tfd = mkstemp(tfile);
1811252726Srpaulo		}
1812252726Srpaulo		if ((of_pid = dofork(pp, DOABORT)) == 0) {	/* child */
1813252726Srpaulo			dup2(p[0], STDIN_FILENO);	/* pipe is std in */
1814252726Srpaulo			/* tfile/printer is stdout */
1815252726Srpaulo			dup2(pp->remote ? tfd : pfd, STDOUT_FILENO);
1816252726Srpaulo			closelog();
1817252726Srpaulo			closeallfds(3);
1818252726Srpaulo			if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL)
1819252726Srpaulo				cp = pp->filters[LPF_OUTPUT];
1820252726Srpaulo			else
1821252726Srpaulo				cp++;
1822252726Srpaulo			execl(pp->filters[LPF_OUTPUT], cp, width, length,
1823252726Srpaulo			      (char *)0);
1824252726Srpaulo			syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer,
1825252726Srpaulo			    pp->filters[LPF_OUTPUT]);
1826252726Srpaulo			exit(1);
1827252726Srpaulo		}
1828252726Srpaulo		(void) close(p[0]);		/* close input side */
1829252726Srpaulo		ofd = p[1];			/* use pipe for output */
1830252726Srpaulo	} else {
1831252726Srpaulo		ofd = pfd;
1832252726Srpaulo		of_pid = 0;
1833281806Srpaulo	}
1834281806Srpaulo}
1835281806Srpaulo
1836281806Srpaulo/*
1837281806Srpaulo * Printer connected directly to the network
1838281806Srpaulo * or to a terminal server on the net
1839281806Srpaulo */
1840281806Srpaulostatic void
1841281806Srpauloopennet(const struct printer *pp)
1842281806Srpaulo{
1843281806Srpaulo	register int i;
1844281806Srpaulo	int resp;
1845281806Srpaulo	u_long port;
1846281806Srpaulo	char *ep;
1847252726Srpaulo	void (*savealrm)(int);
1848252726Srpaulo
1849252726Srpaulo	port = strtoul(pp->lp, &ep, 0);
1850252726Srpaulo	if (*ep != '@' || port > 65535) {
1851252726Srpaulo		syslog(LOG_ERR, "%s: bad port number: %s", pp->printer,
1852252726Srpaulo		    pp->lp);
1853252726Srpaulo		exit(1);
1854252726Srpaulo	}
1855252726Srpaulo	ep++;
1856252726Srpaulo
1857252726Srpaulo	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1858252726Srpaulo		resp = -1;
1859252726Srpaulo		savealrm = signal(SIGALRM, alarmhandler);
1860252726Srpaulo		alarm(pp->conn_timeout);
1861252726Srpaulo		pfd = getport(pp, ep, port);
1862252726Srpaulo		alarm(0);
1863252726Srpaulo		(void)signal(SIGALRM, savealrm);
1864252726Srpaulo		if (pfd < 0 && errno == ECONNREFUSED)
1865252726Srpaulo			resp = 1;
1866252726Srpaulo		else if (pfd >= 0) {
1867252726Srpaulo			/*
1868252726Srpaulo			 * need to delay a bit for rs232 lines
1869252726Srpaulo			 * to stabilize in case printer is
1870252726Srpaulo			 * connected via a terminal server
1871252726Srpaulo			 */
1872252726Srpaulo			delay(500);
1873252726Srpaulo			break;
1874252726Srpaulo		}
1875252726Srpaulo		if (i == 1) {
1876252726Srpaulo			if (resp < 0)
1877252726Srpaulo				pstatus(pp, "waiting for %s to come up",
1878252726Srpaulo					pp->lp);
1879252726Srpaulo			else
1880252726Srpaulo				pstatus(pp,
1881252726Srpaulo					"waiting for access to printer on %s",
1882252726Srpaulo					pp->lp);
1883252726Srpaulo		}
1884252726Srpaulo		sleep(i);
1885252726Srpaulo	}
1886252726Srpaulo	pstatus(pp, "sending to %s port %lu", ep, port);
1887252726Srpaulo}
1888252726Srpaulo
1889252726Srpaulo/*
1890252726Srpaulo * Printer is connected to an RS232 port on this host
1891252726Srpaulo */
1892252726Srpaulostatic void
1893252726Srpauloopentty(const struct printer *pp)
1894252726Srpaulo{
1895252726Srpaulo	register int i;
1896252726Srpaulo
1897252726Srpaulo	for (i = 1; ; i = i < 32 ? i << 1 : i) {
1898252726Srpaulo		pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY);
1899252726Srpaulo		if (pfd >= 0) {
1900252726Srpaulo			delay(500);
1901252726Srpaulo			break;
1902252726Srpaulo		}
1903252726Srpaulo		if (errno == ENOENT) {
1904252726Srpaulo			syslog(LOG_ERR, "%s: %m", pp->lp);
1905252726Srpaulo			exit(1);
1906252726Srpaulo		}
1907252726Srpaulo		if (i == 1)
1908252726Srpaulo			pstatus(pp,
1909252726Srpaulo				"waiting for %s to become ready (offline?)",
1910252726Srpaulo				pp->printer);
1911252726Srpaulo		sleep(i);
1912252726Srpaulo	}
1913252726Srpaulo	if (isatty(pfd))
1914252726Srpaulo		setty(pp);
1915252726Srpaulo	pstatus(pp, "%s is ready and printing", pp->printer);
1916252726Srpaulo}
1917252726Srpaulo
1918252726Srpaulo/*
1919252726Srpaulo * Printer is on a remote host
1920252726Srpaulo */
1921252726Srpaulostatic void
1922252726Srpauloopenrem(const struct printer *pp)
1923252726Srpaulo{
1924252726Srpaulo	register int i;
1925252726Srpaulo	int resp;
1926252726Srpaulo	void (*savealrm)(int);
1927252726Srpaulo
1928252726Srpaulo	for (i = 1; ; i = i < 256 ? i << 1 : i) {
1929252726Srpaulo		resp = -1;
1930252726Srpaulo		savealrm = signal(SIGALRM, alarmhandler);
1931281806Srpaulo		alarm(pp->conn_timeout);
1932252726Srpaulo		pfd = getport(pp, pp->remote_host, 0);
1933281806Srpaulo		alarm(0);
1934252726Srpaulo		(void)signal(SIGALRM, savealrm);
1935252726Srpaulo		if (pfd >= 0) {
1936252726Srpaulo			if ((writel(pfd, "\2", pp->remote_queue, "\n",
1937252726Srpaulo				    (char *)0)
1938252726Srpaulo			     == 2 + strlen(pp->remote_queue))
1939252726Srpaulo			    && (resp = response(pp)) == 0)
1940252726Srpaulo				break;
1941252726Srpaulo			(void) close(pfd);
1942252726Srpaulo		}
1943252726Srpaulo		if (i == 1) {
1944252726Srpaulo			if (resp < 0)
1945252726Srpaulo				pstatus(pp, "waiting for %s to come up",
1946252726Srpaulo					pp->remote_host);
1947252726Srpaulo			else {
1948252726Srpaulo				pstatus(pp,
1949252726Srpaulo					"waiting for queue to be enabled on %s",
1950252726Srpaulo					pp->remote_host);
1951252726Srpaulo				i = 256;
1952252726Srpaulo			}
1953252726Srpaulo		}
1954214734Srpaulo		sleep(i);
1955214734Srpaulo	}
1956252726Srpaulo	pstatus(pp, "sending to %s", pp->remote_host);
1957252726Srpaulo}
1958252726Srpaulo
1959252726Srpaulo/*
1960252726Srpaulo * setup tty lines.
1961252726Srpaulo */
1962252726Srpaulostatic void
1963252726Srpaulosetty(const struct printer *pp)
1964281806Srpaulo{
1965252726Srpaulo	struct termios ttybuf;
1966252726Srpaulo
1967252726Srpaulo	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1968252726Srpaulo		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer);
1969252726Srpaulo		exit(1);
1970252726Srpaulo	}
1971252726Srpaulo	if (tcgetattr(pfd, &ttybuf) < 0) {
1972252726Srpaulo		syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer);
1973252726Srpaulo		exit(1);
1974252726Srpaulo	}
1975252726Srpaulo	if (pp->baud_rate > 0)
1976252726Srpaulo		cfsetspeed(&ttybuf, pp->baud_rate);
1977252726Srpaulo	if (pp->mode_set) {
1978252726Srpaulo		char *s = strdup(pp->mode_set), *tmp;
1979252726Srpaulo
1980252726Srpaulo		while ((tmp = strsep(&s, ",")) != NULL) {
1981252726Srpaulo			(void) msearch(tmp, &ttybuf);
1982252726Srpaulo		}
1983252726Srpaulo	}
1984252726Srpaulo	if (pp->mode_set != 0 || pp->baud_rate > 0) {
1985252726Srpaulo		if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
1986252726Srpaulo			syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer);
1987252726Srpaulo		}
1988252726Srpaulo	}
1989252726Srpaulo}
1990252726Srpaulo
1991252726Srpaulo#include <stdarg.h>
1992252726Srpaulo
1993252726Srpaulostatic void
1994252726Srpaulopstatus(const struct printer *pp, const char *msg, ...)
1995281806Srpaulo{
1996281806Srpaulo	int fd;
1997281806Srpaulo	char *buf;
1998281806Srpaulo	va_list ap;
1999252726Srpaulo	va_start(ap, msg);
2000281806Srpaulo
2001281806Srpaulo	umask(S_IWOTH);
2002281806Srpaulo	fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
2003281806Srpaulo	if (fd < 0) {
2004281806Srpaulo		syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
2005281806Srpaulo		    pp->status_file);
2006281806Srpaulo		exit(1);
2007281806Srpaulo	}
2008281806Srpaulo	ftruncate(fd, 0);
2009252726Srpaulo	vasprintf(&buf, msg, ap);
2010252726Srpaulo	va_end(ap);
2011252726Srpaulo	writel(fd, buf, "\n", (char *)0);
2012252726Srpaulo	close(fd);
2013281806Srpaulo	free(buf);
2014252726Srpaulo}
2015252726Srpaulo
2016252726Srpaulovoid
2017252726Srpauloalarmhandler(int signo __unused)
2018252726Srpaulo{
2019252726Srpaulo	/* the signal is ignored */
2020252726Srpaulo	/* (the '__unused' is just to avoid a compile-time warning) */
2021252726Srpaulo}
2022252726Srpaulo