common.c revision 99844
1169689Skan/*
2169689Skan * Copyright (c) 1983, 1993
3169689Skan *	The Regents of the University of California.  All rights reserved.
4169689Skan * (c) UNIX System Laboratories, Inc.
5169689Skan * All or some portions of this file are derived from material licensed
6169689Skan * to the University of California by American Telephone and Telegraph
7169689Skan * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8169689Skan * the permission of UNIX System Laboratories, Inc.
9169689Skan *
10169689Skan * Redistribution and use in source and binary forms, with or without
11169689Skan * modification, are permitted provided that the following conditions
12169689Skan * are met:
13169689Skan * 1. Redistributions of source code must retain the above copyright
14169689Skan *    notice, this list of conditions and the following disclaimer.
15169689Skan * 2. Redistributions in binary form must reproduce the above copyright
16169689Skan *    notice, this list of conditions and the following disclaimer in the
17169689Skan *    documentation and/or other materials provided with the distribution.
18169689Skan * 3. All advertising materials mentioning features or use of this software
19169689Skan *    must display the following acknowledgement:
20169689Skan *	This product includes software developed by the University of
21169689Skan *	California, Berkeley and its contributors.
22169689Skan * 4. Neither the name of the University nor the names of its contributors
23169689Skan *    may be used to endorse or promote products derived from this software
24169689Skan *    without specific prior written permission.
25169689Skan *
26169689Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29169689Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36169689Skan * SUCH DAMAGE.
37169689Skan */
38169689Skan
39169689Skan#ifndef lint
40169689Skan/*
41169689Skanstatic char sccsid[] = "@(#)common.c	8.5 (Berkeley) 4/28/95";
42169689Skan*/
43169689Skanstatic const char rcsid[] =
44169689Skan  "$FreeBSD: head/usr.sbin/lpr/common_source/common.c 99844 2002-07-12 01:31:46Z gad $";
45169689Skan#endif /* not lint */
46169689Skan
47169689Skan#include <sys/param.h>
48169689Skan#include <sys/stat.h>
49169689Skan#include <sys/time.h>
50169689Skan#include <sys/types.h>
51169689Skan
52169689Skan#include <dirent.h>
53169689Skan#include <errno.h>
54169689Skan#include <fcntl.h>
55169689Skan#include <stdio.h>
56169689Skan#include <stdlib.h>
57169689Skan#include <string.h>
58169689Skan#include <unistd.h>
59169689Skan
60169689Skan#include "lp.h"
61169689Skan#include "lp.local.h"
62169689Skan#include "pathnames.h"
63169689Skan
64169689Skan/*
65169689Skan * Routines and data common to all the line printer functions.
66169689Skan */
67169689Skanchar	line[BUFSIZ];
68169689Skanconst char	*progname;		/* program name */
69169689Skan
70169689Skanextern uid_t	uid, euid;
71169689Skan
72169689Skanstatic int compar(const void *_p1, const void *_p2);
73169689Skan
74169689Skan/*
75169689Skan * Getline reads a line from the control file cfp, removes tabs, converts
76169689Skan *  new-line to null and leaves it in line.
77169689Skan * Returns 0 at EOF or the number of characters read.
78169689Skan */
79169689Skanint
80169689Skangetline(FILE *cfp)
81169689Skan{
82169689Skan	register int linel = 0;
83169689Skan	register char *lp = line;
84169689Skan	register int c;
85169689Skan
86169689Skan	while ((c = getc(cfp)) != '\n' && (size_t)(linel+1) < sizeof(line)) {
87169689Skan		if (c == EOF)
88169689Skan			return(0);
89169689Skan		if (c == '\t') {
90169689Skan			do {
91169689Skan				*lp++ = ' ';
92169689Skan				linel++;
93169689Skan			} while ((linel & 07) != 0 && (size_t)(linel+1) <
94169689Skan			    sizeof(line));
95169689Skan			continue;
96169689Skan		}
97169689Skan		*lp++ = c;
98169689Skan		linel++;
99169689Skan	}
100169689Skan	*lp++ = '\0';
101169689Skan	return(linel);
102169689Skan}
103169689Skan
104169689Skan/*
105169689Skan * Scan the current directory and make a list of daemon files sorted by
106169689Skan * creation time.
107169689Skan * Return the number of entries and a pointer to the list.
108169689Skan */
109169689Skanint
110169689Skangetq(const struct printer *pp, struct jobqueue *(*namelist[]))
111169689Skan{
112169689Skan	register struct dirent *d;
113169689Skan	register struct jobqueue *q, **queue;
114169689Skan	size_t arraysz, entrysz, nitems;
115169689Skan	struct stat stbuf;
116169689Skan	DIR *dirp;
117169689Skan	int statres;
118169689Skan
119169689Skan	seteuid(euid);
120169689Skan	if ((dirp = opendir(pp->spool_dir)) == NULL) {
121222207Sbenl		seteuid(uid);
122169689Skan		return (-1);
123169689Skan	}
124169689Skan	if (fstat(dirp->dd_fd, &stbuf) < 0)
125169689Skan		goto errdone;
126169689Skan	seteuid(uid);
127169689Skan
128169689Skan	/*
129169689Skan	 * Estimate the array size by taking the size of the directory file
130169689Skan	 * and dividing it by a multiple of the minimum size entry.
131169689Skan	 */
132169689Skan	arraysz = (stbuf.st_size / 24);
133169689Skan	queue = (struct jobqueue **)malloc(arraysz * sizeof(struct jobqueue *));
134169689Skan	if (queue == NULL)
135169689Skan		goto errdone;
136169689Skan
137169689Skan	nitems = 0;
138169689Skan	while ((d = readdir(dirp)) != NULL) {
139169689Skan		if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
140169689Skan			continue;	/* daemon control files only */
141169689Skan		seteuid(euid);
142169689Skan		statres = stat(d->d_name, &stbuf);
143169689Skan		seteuid(uid);
144169689Skan		if (statres < 0)
145169689Skan			continue;	/* Doesn't exist */
146169689Skan		entrysz = sizeof(struct jobqueue) - sizeof(q->job_cfname) +
147169689Skan		    strlen(d->d_name) + 1;
148169689Skan		q = (struct jobqueue *)malloc(entrysz);
149169689Skan		if (q == NULL)
150169689Skan			goto errdone;
151169689Skan		q->job_matched = 0;
152169689Skan		q->job_processed = 0;
153169689Skan		q->job_time = stbuf.st_mtime;
154169689Skan		strcpy(q->job_cfname, d->d_name);
155169689Skan		/*
156169689Skan		 * Check to make sure the array has space left and
157169689Skan		 * realloc the maximum size.
158169689Skan		 */
159169689Skan		if (++nitems > arraysz) {
160169689Skan			arraysz *= 2;
161169689Skan			queue = (struct jobqueue **)realloc((char *)queue,
162169689Skan			    arraysz * sizeof(struct jobqueue *));
163169689Skan			if (queue == NULL)
164169689Skan				goto errdone;
165169689Skan		}
166169689Skan		queue[nitems-1] = q;
167169689Skan	}
168169689Skan	closedir(dirp);
169169689Skan	if (nitems)
170169689Skan		qsort(queue, nitems, sizeof(struct jobqueue *), compar);
171169689Skan	*namelist = queue;
172169689Skan	return(nitems);
173169689Skan
174169689Skanerrdone:
175169689Skan	closedir(dirp);
176169689Skan	seteuid(uid);
177169689Skan	return (-1);
178169689Skan}
179169689Skan
180169689Skan/*
181169689Skan * Compare modification times.
182169689Skan */
183169689Skanstatic int
184169689Skancompar(const void *p1, const void *p2)
185169689Skan{
186169689Skan	const struct jobqueue *qe1, *qe2;
187169689Skan
188169689Skan	qe1 = *(const struct jobqueue * const *)p1;
189169689Skan	qe2 = *(const struct jobqueue * const *)p2;
190169689Skan
191169689Skan	if (qe1->job_time < qe2->job_time)
192169689Skan		return (-1);
193169689Skan	if (qe1->job_time > qe2->job_time)
194169689Skan		return (1);
195169689Skan	/*
196169689Skan	 * At this point, the two files have the same last-modification time.
197169689Skan	 * return a result based on filenames, so that 'cfA001some.host' will
198169689Skan	 * come before 'cfA002some.host'.  Since the jobid ('001') will wrap
199169689Skan	 * around when it gets to '999', we also assume that '9xx' jobs are
200169689Skan	 * older than '0xx' jobs.
201169689Skan	*/
202169689Skan	if ((qe1->job_cfname[3] == '9') && (qe2->job_cfname[3] == '0'))
203169689Skan		return (-1);
204169689Skan	if ((qe1->job_cfname[3] == '0') && (qe2->job_cfname[3] == '9'))
205169689Skan		return (1);
206169689Skan	return (strcmp(qe1->job_cfname, qe2->job_cfname));
207169689Skan}
208169689Skan
209169689Skan/* sleep n milliseconds */
210169689Skanvoid
211169689Skandelay(int millisec)
212169689Skan{
213169689Skan	struct timeval tdelay;
214169689Skan
215169689Skan	if (millisec <= 0 || millisec > 10000)
216169689Skan		fatal((struct printer *)0, /* fatal() knows how to deal */
217169689Skan		    "unreasonable delay period (%d)", millisec);
218169689Skan	tdelay.tv_sec = millisec / 1000;
219169689Skan	tdelay.tv_usec = millisec * 1000 % 1000000;
220169689Skan	(void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tdelay);
221169689Skan}
222169689Skan
223169689Skanchar *
224169689Skanlock_file_name(const struct printer *pp, char *buf, size_t len)
225169689Skan{
226169689Skan	static char staticbuf[MAXPATHLEN];
227169689Skan
228169689Skan	if (buf == 0)
229169689Skan		buf = staticbuf;
230169689Skan	if (len == 0)
231169689Skan		len = MAXPATHLEN;
232169689Skan
233169689Skan	if (pp->lock_file[0] == '/')
234169689Skan		strlcpy(buf, pp->lock_file, len);
235169689Skan	else
236169689Skan		snprintf(buf, len, "%s/%s", pp->spool_dir, pp->lock_file);
237169689Skan
238169689Skan	return buf;
239169689Skan}
240169689Skan
241169689Skanchar *
242169689Skanstatus_file_name(const struct printer *pp, char *buf, size_t len)
243169689Skan{
244169689Skan	static char staticbuf[MAXPATHLEN];
245169689Skan
246169689Skan	if (buf == 0)
247169689Skan		buf = staticbuf;
248169689Skan	if (len == 0)
249169689Skan		len = MAXPATHLEN;
250169689Skan
251169689Skan	if (pp->status_file[0] == '/')
252169689Skan		strlcpy(buf, pp->status_file, len);
253169689Skan	else
254169689Skan		snprintf(buf, len, "%s/%s", pp->spool_dir, pp->status_file);
255169689Skan
256169689Skan	return buf;
257169689Skan}
258169689Skan
259169689Skan/*
260169689Skan * Routine to change operational state of a print queue.  The operational
261169689Skan * state is indicated by the access bits on the lock file for the queue.
262169689Skan * At present, this is only called from various routines in lpc/cmds.c.
263169689Skan *
264169689Skan *  XXX - Note that this works by changing access-bits on the
265169689Skan *	file, and you can only do that if you are the owner of
266169689Skan *	the file, or root.  Thus, this won't really work for
267169689Skan *	userids in the "LPR_OPER" group, unless lpc is running
268169689Skan *	setuid to root (or maybe setuid to daemon).
269169689Skan *	Generally lpc is installed setgid to daemon, but does
270169689Skan *	not run setuid.
271169689Skan */
272169689Skanint
273169689Skanset_qstate(int action, const char *lfname)
274169689Skan{
275169689Skan	struct stat stbuf;
276169689Skan	mode_t chgbits, newbits, oldmask;
277169689Skan	const char *failmsg, *okmsg;
278169689Skan	static const char *nomsg = "no state msg";
279169689Skan	int chres, errsav, fd, res, statres;
280169689Skan
281169689Skan	/*
282169689Skan	 * Find what the current access-bits are.
283169689Skan	 */
284169689Skan	memset(&stbuf, 0, sizeof(stbuf));
285169689Skan	seteuid(euid);
286169689Skan	statres = stat(lfname, &stbuf);
287169689Skan	errsav = errno;
288169689Skan	seteuid(uid);
289169689Skan	if ((statres < 0) && (errsav != ENOENT)) {
290169689Skan		printf("\tcannot stat() lock file\n");
291169689Skan		return (SQS_STATFAIL);
292169689Skan		/* NOTREACHED */
293169689Skan	}
294169689Skan
295169689Skan	/*
296169689Skan	 * Determine which bit(s) should change for the requested action.
297169689Skan	 */
298169689Skan	chgbits = stbuf.st_mode;
299169689Skan	newbits = LOCK_FILE_MODE;
300169689Skan	okmsg = NULL;
301169689Skan	failmsg = NULL;
302169689Skan	if (action & SQS_QCHANGED) {
303169689Skan		chgbits |= LFM_RESET_QUE;
304169689Skan		newbits |= LFM_RESET_QUE;
305169689Skan		/* The okmsg is not actually printed for this case. */
306169689Skan		okmsg = nomsg;
307169689Skan		failmsg = "set queue-changed";
308169689Skan	}
309169689Skan	if (action & SQS_DISABLEQ) {
310169689Skan		chgbits |= LFM_QUEUE_DIS;
311169689Skan		newbits |= LFM_QUEUE_DIS;
312169689Skan		okmsg = "queuing disabled";
313169689Skan		failmsg = "disable queuing";
314169689Skan	}
315169689Skan	if (action & SQS_STOPP) {
316169689Skan		chgbits |= LFM_PRINT_DIS;
317169689Skan		newbits |= LFM_PRINT_DIS;
318169689Skan		okmsg = "printing disabled";
319169689Skan		failmsg = "disable printing";
320169689Skan		if (action & SQS_DISABLEQ) {
321169689Skan			okmsg = "printer and queuing disabled";
322169689Skan			failmsg = "disable queuing and printing";
323169689Skan		}
324169689Skan	}
325169689Skan	if (action & SQS_ENABLEQ) {
326169689Skan		chgbits &= ~LFM_QUEUE_DIS;
327169689Skan		newbits &= ~LFM_QUEUE_DIS;
328169689Skan		okmsg = "queuing enabled";
329169689Skan		failmsg = "enable queuing";
330169689Skan	}
331169689Skan	if (action & SQS_STARTP) {
332169689Skan		chgbits &= ~LFM_PRINT_DIS;
333169689Skan		newbits &= ~LFM_PRINT_DIS;
334169689Skan		okmsg = "printing enabled";
335169689Skan		failmsg = "enable printing";
336169689Skan	}
337169689Skan	if (okmsg == NULL) {
338169689Skan		/* This routine was called with an invalid action. */
339169689Skan		printf("\t<error in set_qstate!>\n");
340169689Skan		return (SQS_PARMERR);
341169689Skan		/* NOTREACHED */
342169689Skan	}
343169689Skan
344169689Skan	res = 0;
345169689Skan	if (statres >= 0) {
346169689Skan		/* The file already exists, so change the access. */
347169689Skan		seteuid(euid);
348169689Skan		chres = chmod(lfname, chgbits);
349169689Skan		errsav = errno;
350169689Skan		seteuid(uid);
351169689Skan		res = SQS_CHGOK;
352169689Skan		if (res < 0)
353169689Skan			res = SQS_CHGFAIL;
354169689Skan	} else if (newbits == LOCK_FILE_MODE) {
355169689Skan		/*
356169689Skan		 * The file does not exist, but the state requested is
357169689Skan		 * the same as the default state when no file exists.
358169689Skan		 * Thus, there is no need to create the file.
359169689Skan		 */
360169689Skan		res = SQS_SKIPCREOK;
361169689Skan	} else {
362169689Skan		/*
363169689Skan		 * The file did not exist, so create it with the
364169689Skan		 * appropriate access bits for the requested action.
365169689Skan		 * Push a new umask around that create, to make sure
366169689Skan		 * all the read/write bits are set as desired.
367169689Skan		 */
368169689Skan		oldmask = umask(S_IWOTH);
369169689Skan		seteuid(euid);
370169689Skan		fd = open(lfname, O_WRONLY|O_CREAT, newbits);
371169689Skan		errsav = errno;
372169689Skan		seteuid(uid);
373169689Skan		umask(oldmask);
374169689Skan		res = SQS_CREFAIL;
375169689Skan		if (fd >= 0) {
376169689Skan			res = SQS_CREOK;
377169689Skan			close(fd);
378169689Skan		}
379169689Skan	}
380169689Skan
381169689Skan	switch (res) {
382169689Skan	case SQS_CHGOK:
383169689Skan	case SQS_CREOK:
384169689Skan	case SQS_SKIPCREOK:
385169689Skan		if (okmsg != nomsg)
386169689Skan			printf("\t%s\n", okmsg);
387171825Skan		break;
388171825Skan	case SQS_CREFAIL:
389171825Skan		printf("\tcannot create lock file: %s\n",
390171825Skan		    strerror(errsav));
391169689Skan		break;
392171825Skan	default:
393169689Skan		printf("\tcannot %s: %s\n", failmsg, strerror(errsav));
394169689Skan		break;
395169689Skan	}
396169689Skan
397169689Skan	return (res);
398169689Skan}
399169689Skan
400169689Skan/* routine to get a current timestamp, optionally in a standard-fmt string */
401169689Skanvoid
402169689Skanlpd_gettime(struct timespec *tsp, char *strp, size_t strsize)
403169689Skan{
404169689Skan	struct timespec local_ts;
405169689Skan	struct timeval btime;
406169689Skan	char tempstr[TIMESTR_SIZE];
407169689Skan#ifdef STRFTIME_WRONG_z
408169689Skan	char *destp;
409169689Skan#endif
410169689Skan
411169689Skan	if (tsp == NULL)
412169689Skan		tsp = &local_ts;
413169689Skan
414169689Skan	/* some platforms have a routine called clock_gettime, but the
415169689Skan	 * routine does nothing but return "not implemented". */
416169689Skan	memset(tsp, 0, sizeof(struct timespec));
417169689Skan	if (clock_gettime(CLOCK_REALTIME, tsp)) {
418169689Skan		/* nanosec-aware rtn failed, fall back to microsec-aware rtn */
419169689Skan		memset(tsp, 0, sizeof(struct timespec));
420169689Skan		gettimeofday(&btime, NULL);
421169689Skan		tsp->tv_sec = btime.tv_sec;
422169689Skan		tsp->tv_nsec = btime.tv_usec * 1000;
423169689Skan	}
424169689Skan
425169689Skan	/* caller may not need a character-ized version */
426169689Skan	if ((strp == NULL) || (strsize < 1))
427169689Skan		return;
428169689Skan
429169689Skan	strftime(tempstr, TIMESTR_SIZE, LPD_TIMESTAMP_PATTERN,
430169689Skan		 localtime(&tsp->tv_sec));
431169689Skan
432169689Skan	/*
433169689Skan	 * This check is for implementations of strftime which treat %z
434169689Skan	 * (timezone as [+-]hhmm ) like %Z (timezone as characters), or
435169689Skan	 * completely ignore %z.  This section is not needed on freebsd.
436169689Skan	 * I'm not sure this is completely right, but it should work OK
437169689Skan	 * for EST and EDT...
438169689Skan	 */
439169689Skan#ifdef STRFTIME_WRONG_z
440169689Skan	destp = strrchr(tempstr, ':');
441169689Skan	if (destp != NULL) {
442169689Skan		destp += 3;
443169689Skan		if ((*destp != '+') && (*destp != '-')) {
444169689Skan			char savday[6];
445169689Skan			int tzmin = timezone / 60;
446169689Skan			int tzhr = tzmin / 60;
447169689Skan			if (daylight)
448169689Skan				tzhr--;
449169689Skan			strcpy(savday, destp + strlen(destp) - 4);
450169689Skan			snprintf(destp, (destp - tempstr), "%+03d%02d",
451169689Skan			    (-1*tzhr), tzmin % 60);
452169689Skan			strcat(destp, savday);
453169689Skan		}
454169689Skan	}
455169689Skan#endif
456169689Skan
457169689Skan	if (strsize > TIMESTR_SIZE) {
458169689Skan		strsize = TIMESTR_SIZE;
459169689Skan		strp[TIMESTR_SIZE+1] = '\0';
460169689Skan	}
461169689Skan	strlcpy(strp, tempstr, strsize);
462169689Skan}
463169689Skan
464169689Skan/* routines for writing transfer-statistic records */
465169689Skanvoid
466169689Skantrstat_init(struct printer *pp, const char *fname, int filenum)
467169689Skan{
468169689Skan	register const char *srcp;
469169689Skan	register char *destp, *endp;
470169689Skan
471169689Skan	/*
472169689Skan	 * Figure out the job id of this file.  The filename should be
473169689Skan	 * 'cf', 'df', or maybe 'tf', followed by a letter (or sometimes
474169689Skan	 * two), followed by the jobnum, followed by a hostname.
475169689Skan	 * The jobnum is usually 3 digits, but might be as many as 5.
476169689Skan	 * Note that some care has to be taken parsing this, as the
477169689Skan	 * filename could be coming from a remote-host, and thus might
478169689Skan	 * not look anything like what is expected...
479169689Skan	 */
480169689Skan	memset(pp->jobnum, 0, sizeof(pp->jobnum));
481169689Skan	pp->jobnum[0] = '0';
482169689Skan	srcp = strchr(fname, '/');
483169689Skan	if (srcp == NULL)
484169689Skan		srcp = fname;
485169689Skan	destp = &(pp->jobnum[0]);
486169689Skan	endp = destp + 5;
487169689Skan	while (*srcp != '\0' && (*srcp < '0' || *srcp > '9'))
488169689Skan		srcp++;
489169689Skan	while (*srcp >= '0' && *srcp <= '9' && destp < endp)
490169689Skan		*(destp++) = *(srcp++);
491169689Skan
492169689Skan	/* get the starting time in both numeric and string formats, and
493169689Skan	 * save those away along with the file-number */
494169689Skan	pp->jobdfnum = filenum;
495169689Skan	lpd_gettime(&pp->tr_start, pp->tr_timestr, (size_t)TIMESTR_SIZE);
496169689Skan
497169689Skan	return;
498169689Skan}
499169689Skan
500169689Skanvoid
501169689Skantrstat_write(struct printer *pp, tr_sendrecv sendrecv, size_t bytecnt,
502169689Skan    const char *userid, const char *otherhost, const char *orighost)
503169689Skan{
504169689Skan#define STATLINE_SIZE 1024
505169689Skan	double trtime;
506169689Skan	size_t remspace;
507169689Skan	int statfile;
508169689Skan	char thishost[MAXHOSTNAMELEN], statline[STATLINE_SIZE];
509169689Skan	char *eostat;
510169689Skan	const char *lprhost, *recvdev, *recvhost, *rectype;
511169689Skan	const char *sendhost, *statfname;
512169689Skan#define UPD_EOSTAT(xStr) do {         \
513169689Skan	eostat = strchr(xStr, '\0');  \
514169689Skan	remspace = eostat - xStr;     \
515169689Skan} while(0)
516169689Skan
517169689Skan	lpd_gettime(&pp->tr_done, NULL, (size_t)0);
518169689Skan	trtime = DIFFTIME_TS(pp->tr_done, pp->tr_start);
519169689Skan
520169689Skan	gethostname(thishost, sizeof(thishost));
521169689Skan	lprhost = sendhost = recvhost = recvdev = NULL;
522169689Skan	switch (sendrecv) {
523169689Skan	    case TR_SENDING:
524169689Skan		rectype = "send";
525169689Skan		statfname = pp->stat_send;
526169689Skan		sendhost = thishost;
527169689Skan		recvhost = otherhost;
528169689Skan		break;
529169689Skan	    case TR_RECVING:
530169689Skan		rectype = "recv";
531169689Skan		statfname = pp->stat_recv;
532169689Skan		sendhost = otherhost;
533169689Skan		recvhost = thishost;
534169689Skan		break;
535169689Skan	    case TR_PRINTING:
536169689Skan		/*
537169689Skan		 * This case is for copying to a device (presumably local,
538169689Skan		 * though filters using things like 'net/CAP' can confuse
539169689Skan		 * this assumption...).
540169689Skan		 */
541169689Skan		rectype = "prnt";
542169689Skan		statfname = pp->stat_send;
543169689Skan		sendhost = thishost;
544169689Skan		recvdev = _PATH_DEFDEVLP;
545169689Skan		if (pp->lp) recvdev = pp->lp;
546169689Skan		break;
547169689Skan	    default:
548169689Skan		/* internal error...  should we syslog/printf an error? */
549169689Skan		return;
550169689Skan	}
551169689Skan	if (statfname == NULL)
552169689Skan		return;
553169689Skan
554169689Skan	/*
555169689Skan	 * the original-host and userid are found out by reading thru the
556169689Skan	 * cf (control-file) for the job.  Unfortunately, on incoming jobs
557169689Skan	 * the df's (data-files) are sent before the matching cf, so the
558169689Skan	 * orighost & userid are generally not-available for incoming jobs.
559169689Skan	 *
560169689Skan	 * (it would be nice to create a work-around for that..)
561169689Skan	 */
562169689Skan	if (orighost && (*orighost != '\0'))
563169689Skan		lprhost = orighost;
564169689Skan	else
565169689Skan		lprhost = ".na.";
566169689Skan	if (*userid == '\0')
567169689Skan		userid = NULL;
568169689Skan
569169689Skan	/*
570169689Skan	 * Format of statline.
571169689Skan	 * Some of the keywords listed here are not implemented here, but
572169689Skan	 * they are listed to reserve the meaning for a given keyword.
573169689Skan	 * Fields are separated by a blank.  The fields in statline are:
574169689Skan	 *   <tstamp>      - time the transfer started
575169689Skan	 *   <ptrqueue>    - name of the printer queue (the short-name...)
576169689Skan	 *   <hname>       - hostname the file originally came from (the
577169689Skan	 *		     'lpr host'), if known, or  "_na_" if not known.
578169689Skan	 *   <xxx>         - id of job from that host (generally three digits)
579169689Skan	 *   <n>           - file count (# of file within job)
580169689Skan	 *   <rectype>     - 4-byte field indicating the type of transfer
581169689Skan	 *		     statistics record.  "send" means it's from the
582169689Skan	 *		     host sending a datafile, "recv" means it's from
583169689Skan	 *		     a host as it receives a datafile.
584169689Skan	 *   user=<userid> - user who sent the job (if known)
585169689Skan	 *   secs=<n>      - seconds it took to transfer the file
586169689Skan	 *   bytes=<n>     - number of bytes transfered (ie, "bytecount")
587169689Skan	 *   bps=<n.n>e<n> - Bytes/sec (if the transfer was "big enough"
588169689Skan	 *		     for this to be useful)
589169689Skan	 * ! top=<str>     - type of printer (if the type is defined in
590169689Skan	 *		     printcap, and if this statline is for sending
591169689Skan	 *		     a file to that ptr)
592169689Skan	 * ! qls=<n>       - queue-length at start of send/print-ing a job
593169689Skan	 * ! qle=<n>       - queue-length at end of send/print-ing a job
594169689Skan	 *   sip=<addr>    - IP address of sending host, only included when
595169689Skan	 *		     receiving a job.
596169689Skan	 *   shost=<hname> - sending host (if that does != the original host)
597169689Skan	 *   rhost=<hname> - hostname receiving the file (ie, "destination")
598169689Skan	 *   rdev=<dev>    - device receiving the file, when the file is being
599169689Skan	 *		     send to a device instead of a remote host.
600169689Skan	 *
601169689Skan	 * Note: A single print job may be transferred multiple times.  The
602169689Skan	 * original 'lpr' occurs on one host, and that original host might
603169689Skan	 * send to some interim host (or print server).  That interim host
604169689Skan	 * might turn around and send the job to yet another host (most likely
605169689Skan	 * the real printer).  The 'shost=' parameter is only included if the
606169689Skan	 * sending host for this particular transfer is NOT the same as the
607169689Skan	 * host which did the original 'lpr'.
608169689Skan	 *
609169689Skan	 * Many values have 'something=' tags before them, because they are
610169689Skan	 * in some sense "optional", or their order may vary.  "Optional" may
611169689Skan	 * mean in the sense that different SITES might choose to have other
612169689Skan	 * fields in the record, or that some fields are only included under
613169689Skan	 * some circumstances.  Programs processing these records should not
614169689Skan	 * assume the order or existence of any of these keyword fields.
615169689Skan	 */
616169689Skan	snprintf(statline, STATLINE_SIZE, "%s %s %s %s %03ld %s",
617169689Skan	    pp->tr_timestr, pp->printer, lprhost, pp->jobnum,
618169689Skan	    pp->jobdfnum, rectype);
619169689Skan	UPD_EOSTAT(statline);
620169689Skan
621169689Skan	if (userid != NULL) {
622169689Skan		snprintf(eostat, remspace, " user=%s", userid);
623169689Skan		UPD_EOSTAT(statline);
624169689Skan	}
625169689Skan	snprintf(eostat, remspace, " secs=%#.2f bytes=%lu", trtime,
626169689Skan	    (unsigned long)bytecnt);
627169689Skan	UPD_EOSTAT(statline);
628169689Skan
629169689Skan	/*
630169689Skan	 * The bps field duplicates info from bytes and secs, so do
631169689Skan	 * not bother to include it for very small files.
632169689Skan	 */
633169689Skan	if ((bytecnt > 25000) && (trtime > 1.1)) {
634169689Skan		snprintf(eostat, remspace, " bps=%#.2e",
635169689Skan		    ((double)bytecnt/trtime));
636169689Skan		UPD_EOSTAT(statline);
637169689Skan	}
638169689Skan
639169689Skan	if (sendrecv == TR_RECVING) {
640169689Skan		if (remspace > 5+strlen(from_ip) ) {
641169689Skan			snprintf(eostat, remspace, " sip=%s", from_ip);
642169689Skan			UPD_EOSTAT(statline);
643169689Skan		}
644169689Skan	}
645169689Skan	if (0 != strcmp(lprhost, sendhost)) {
646169689Skan		if (remspace > 7+strlen(sendhost) ) {
647169689Skan			snprintf(eostat, remspace, " shost=%s", sendhost);
648169689Skan			UPD_EOSTAT(statline);
649169689Skan		}
650169689Skan	}
651169689Skan	if (recvhost) {
652169689Skan		if (remspace > 7+strlen(recvhost) ) {
653169689Skan			snprintf(eostat, remspace, " rhost=%s", recvhost);
654169689Skan			UPD_EOSTAT(statline);
655169689Skan		}
656169689Skan	}
657169689Skan	if (recvdev) {
658169689Skan		if (remspace > 6+strlen(recvdev) ) {
659169689Skan			snprintf(eostat, remspace, " rdev=%s", recvdev);
660169689Skan			UPD_EOSTAT(statline);
661169689Skan		}
662169689Skan	}
663169689Skan	if (remspace > 1) {
664169689Skan		strcpy(eostat, "\n");
665169689Skan	} else {
666169689Skan		/* probably should back up to just before the final " x=".. */
667169689Skan		strcpy(statline+STATLINE_SIZE-2, "\n");
668169689Skan	}
669169689Skan	statfile = open(statfname, O_WRONLY|O_APPEND, 0664);
670169689Skan	if (statfile < 0) {
671169689Skan		/* statfile was given, but we can't open it.  should we
672169689Skan		 * syslog/printf this as an error? */
673169689Skan		return;
674169689Skan	}
675169689Skan	write(statfile, statline, strlen(statline));
676169689Skan	close(statfile);
677169689Skan
678169689Skan	return;
679169689Skan#undef UPD_EOSTAT
680169689Skan}
681169689Skan
682169689Skan#include <stdarg.h>
683169689Skan
684169689Skanvoid
685169689Skanfatal(const struct printer *pp, const char *msg, ...)
686169689Skan{
687169689Skan	va_list ap;
688169689Skan	va_start(ap, msg);
689169689Skan	/* this error message is being sent to the 'from_host' */
690169689Skan	if (from_host != local_host)
691169689Skan		(void)printf("%s: ", local_host);
692169689Skan	(void)printf("%s: ", progname);
693169689Skan	if (pp && pp->printer)
694169689Skan		(void)printf("%s: ", pp->printer);
695169689Skan	(void)vprintf(msg, ap);
696169689Skan	va_end(ap);
697169689Skan	(void)putchar('\n');
698169689Skan	exit(1);
699169689Skan}
700169689Skan
701169689Skan/*
702169689Skan * Close all file descriptors from START on up.
703169689Skan * This is a horrific kluge, since getdtablesize() might return
704169689Skan * ``infinity'', in which case we will be spending a long time
705169689Skan * closing ``files'' which were never open.  Perhaps it would
706169689Skan * be better to close the first N fds, for some small value of N.
707169689Skan */
708169689Skanvoid
709169689Skancloseallfds(int start)
710169689Skan{
711169689Skan	int stop = getdtablesize();
712169689Skan	for (; start < stop; start++)
713169689Skan		close(start);
714169689Skan}
715169689Skan
716169689Skan