1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1983, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34static const char copyright[] =
35"@(#) Copyright (c) 1983, 1993\n\
36	The Regents of the University of California.  All rights reserved.\n";
37#endif /* not lint */
38
39#if 0
40#ifndef lint
41static char sccsid[] = "@(#)recvjob.c	8.2 (Berkeley) 4/27/95";
42#endif /* not lint */
43#endif
44
45#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
46__FBSDID("$FreeBSD$");
47
48/*
49 * Receive printer jobs from the network, queue them and
50 * start the printer daemon.
51 */
52#include <sys/param.h>
53#include <sys/mount.h>
54#include <sys/stat.h>
55
56#include <unistd.h>
57#include <signal.h>
58#include <fcntl.h>
59#include <dirent.h>
60#include <errno.h>
61#include <syslog.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <string.h>
65#include "lp.h"
66#include "lp.local.h"
67#include "ctlinfo.h"
68#include "extern.h"
69#include "pathnames.h"
70
71#define ack()	(void) write(STDOUT_FILENO, sp, (size_t)1);
72
73/*
74 * The buffer size to use when reading/writing spool files.
75 */
76#define	SPL_BUFSIZ	BUFSIZ
77
78static char	 dfname[NAME_MAX];	/* data files */
79static int	 minfree;       /* keep at least minfree blocks available */
80static const char	*sp = "";
81static char	 tfname[NAME_MAX];	/* tmp copy of cf before linking */
82
83static int	 chksize(int _size);
84static void	 frecverr(const char *_msg, ...) __printf0like(1, 2);
85static int	 noresponse(void);
86static void	 rcleanup(int _signo);
87static int	 read_number(const char *_fn);
88static int	 readfile(struct printer *_pp, char *_file, size_t _size);
89static int	 readjob(struct printer *_pp);
90
91
92void
93recvjob(const char *printer)
94{
95	struct stat stb;
96	int status;
97	struct printer myprinter, *pp = &myprinter;
98
99	/*
100	 * Perform lookup for printer name or abbreviation
101	 */
102	init_printer(pp);
103	status = getprintcap(printer, pp);
104	switch (status) {
105	case PCAPERR_OSERR:
106		frecverr("cannot open printer description file");
107		break;
108	case PCAPERR_NOTFOUND:
109		frecverr("unknown printer %s", printer);
110		break;
111	case PCAPERR_TCLOOP:
112		fatal(pp, "potential reference loop detected in printcap file");
113	default:
114		break;
115	}
116
117	(void) close(STDERR_FILENO);			/* set up log file */
118	if (open(pp->log_file, O_WRONLY|O_APPEND, 0664) < 0) {
119		syslog(LOG_ERR, "%s: %m", pp->log_file);
120		(void) open(_PATH_DEVNULL, O_WRONLY);
121	}
122
123	if (chdir(pp->spool_dir) < 0)
124		frecverr("%s: chdir(%s): %s", pp->printer, pp->spool_dir,
125		    strerror(errno));
126	if (stat(pp->lock_file, &stb) == 0) {
127		if (stb.st_mode & 010) {
128			/* queue is disabled */
129			putchar('\1');		/* return error code */
130			exit(1);
131		}
132	} else if (stat(pp->spool_dir, &stb) < 0)
133		frecverr("%s: stat(%s): %s", pp->printer, pp->spool_dir,
134		    strerror(errno));
135	minfree = 2 * read_number("minfree");	/* scale KB to 512 blocks */
136	signal(SIGTERM, rcleanup);
137	signal(SIGPIPE, rcleanup);
138
139	if (readjob(pp))
140		printjob(pp);
141}
142
143/*
144 * Read printer jobs sent by lpd and copy them to the spooling directory.
145 * Return the number of jobs successfully transferred.
146 */
147static int
148readjob(struct printer *pp)
149{
150	register int size;
151	int cfcnt, dfcnt;
152	char *cp, *clastp, *errmsg;
153	char givenid[32], givenhost[MAXHOSTNAMELEN];
154
155	ack();
156	cfcnt = 0;
157	dfcnt = 0;
158	for (;;) {
159		/*
160		 * Read a command to tell us what to do
161		 */
162		cp = line;
163		clastp = line + sizeof(line) - 1;
164		do {
165			size = read(STDOUT_FILENO, cp, (size_t)1);
166			if (size != (ssize_t)1) {
167				if (size < (ssize_t)0) {
168					frecverr("%s: lost connection",
169					    pp->printer);
170					/*NOTREACHED*/
171				}
172				return (cfcnt);
173			}
174		} while ((*cp++ != '\n') && (cp <= clastp));
175		if (cp > clastp) {
176			frecverr("%s: readjob overflow", pp->printer);
177			/*NOTREACHED*/
178		}
179		*--cp = '\0';
180		cp = line;
181		switch (*cp++) {
182		case '\1':	/* cleanup because data sent was bad */
183			rcleanup(0);
184			continue;
185
186		case '\2':	/* read cf file */
187			size = 0;
188			dfcnt = 0;
189			while (*cp >= '0' && *cp <= '9')
190				size = size * 10 + (*cp++ - '0');
191			if (*cp++ != ' ')
192				break;
193			/*
194			 * host name has been authenticated, we use our
195			 * view of the host name since we may be passed
196			 * something different than what gethostbyaddr()
197			 * returns
198			 */
199			strlcpy(cp + 6, from_host, sizeof(line)
200			    + (size_t)(line - cp - 6));
201			if (strchr(cp, '/')) {
202				frecverr("readjob: %s: illegal path name", cp);
203				/*NOTREACHED*/
204			}
205			strlcpy(tfname, cp, sizeof(tfname));
206			tfname[sizeof (tfname) - 1] = '\0';
207			tfname[0] = 't';
208			if (!chksize(size)) {
209				(void) write(STDOUT_FILENO, "\2", (size_t)1);
210				continue;
211			}
212			if (!readfile(pp, tfname, (size_t)size)) {
213				rcleanup(0);
214				continue;
215			}
216			errmsg = ctl_renametf(pp->printer, tfname);
217			tfname[0] = '\0';
218			if (errmsg != NULL) {
219				frecverr("%s: %s", pp->printer, errmsg);
220				/*NOTREACHED*/
221			}
222			cfcnt++;
223			continue;
224
225		case '\3':	/* read df file */
226			*givenid = '\0';
227			*givenhost = '\0';
228			size = 0;
229			while (*cp >= '0' && *cp <= '9')
230				size = size * 10 + (*cp++ - '0');
231			if (*cp++ != ' ')
232				break;
233			if (strchr(cp, '/')) {
234				frecverr("readjob: %s: illegal path name", cp);
235				/*NOTREACHED*/
236			}
237			if (!chksize(size)) {
238				(void) write(STDOUT_FILENO, "\2", (size_t)1);
239				continue;
240			}
241			strlcpy(dfname, cp, sizeof(dfname));
242			dfcnt++;
243			trstat_init(pp, dfname, dfcnt);
244			(void) readfile(pp, dfname, (size_t)size);
245			trstat_write(pp, TR_RECVING, (size_t)size, givenid,
246			    from_host, givenhost);
247			continue;
248		}
249		frecverr("protocol screwup: %s", line);
250		/*NOTREACHED*/
251	}
252}
253
254/*
255 * Read files send by lpd and copy them to the spooling directory.
256 */
257static int
258readfile(struct printer *pp, char *file, size_t size)
259{
260	register char *cp;
261	char buf[SPL_BUFSIZ];
262	size_t amt, i;
263	int err, fd, j;
264
265	fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
266	if (fd < 0) {
267		frecverr("%s: readfile: error on open(%s): %s",
268			 pp->printer, file, strerror(errno));
269		/*NOTREACHED*/
270	}
271	ack();
272	err = 0;
273	for (i = 0; i < size; i += SPL_BUFSIZ) {
274		amt = SPL_BUFSIZ;
275		cp = buf;
276		if (i + amt > size)
277			amt = size - i;
278		do {
279			j = read(STDOUT_FILENO, cp, amt);
280			if (j <= 0) {
281				frecverr("%s: lost connection", pp->printer);
282				/*NOTREACHED*/
283			}
284			amt -= j;
285			cp += j;
286		} while (amt > 0);
287		amt = SPL_BUFSIZ;
288		if (i + amt > size)
289			amt = size - i;
290		if (write(fd, buf, amt) != (ssize_t)amt) {
291			err++;
292			break;
293		}
294	}
295	(void) close(fd);
296	if (err) {
297		frecverr("%s: write error on close(%s)", pp->printer, file);
298		/*NOTREACHED*/
299	}
300	if (noresponse()) {		/* file sent had bad data in it */
301		if (strchr(file, '/') == NULL)
302			(void) unlink(file);
303		return (0);
304	}
305	ack();
306	return (1);
307}
308
309static int
310noresponse(void)
311{
312	char resp;
313
314	if (read(STDOUT_FILENO, &resp, (size_t)1) != 1) {
315		frecverr("lost connection in noresponse()");
316		/*NOTREACHED*/
317	}
318	if (resp == '\0')
319		return(0);
320	return(1);
321}
322
323/*
324 * Check to see if there is enough space on the disk for size bytes.
325 * 1 == OK, 0 == Not OK.
326 */
327static int
328chksize(int size)
329{
330	int64_t spacefree;
331	struct statfs sfb;
332
333	if (statfs(".", &sfb) < 0) {
334		syslog(LOG_ERR, "%s: %m", "statfs(\".\")");
335		return (1);
336	}
337	spacefree = sfb.f_bavail * (sfb.f_bsize / 512);
338	size = (size + 511) / 512;
339	if (minfree + size > spacefree)
340		return(0);
341	return(1);
342}
343
344static int
345read_number(const char *fn)
346{
347	char lin[80];
348	register FILE *fp;
349
350	if ((fp = fopen(fn, "r")) == NULL)
351		return (0);
352	if (fgets(lin, sizeof(lin), fp) == NULL) {
353		fclose(fp);
354		return (0);
355	}
356	fclose(fp);
357	return (atoi(lin));
358}
359
360/*
361 * Remove all the files associated with the current job being transferred.
362 */
363static void
364rcleanup(int signo __unused)
365{
366	if (tfname[0] && strchr(tfname, '/') == NULL)
367		(void) unlink(tfname);
368	if (dfname[0] && strchr(dfname, '/') == NULL) {
369		do {
370			do
371				(void) unlink(dfname);
372			while (dfname[2]-- != 'A');
373			dfname[2] = 'z';
374		} while (dfname[0]-- != 'd');
375	}
376	dfname[0] = '\0';
377}
378
379#include <stdarg.h>
380
381static void
382frecverr(const char *msg, ...)
383{
384	va_list ap;
385	va_start(ap, msg);
386	syslog(LOG_ERR, "Error receiving job from %s:", from_host);
387	vsyslog(LOG_ERR, msg, ap);
388	va_end(ap);
389	/*
390	 * rcleanup is not called until AFTER logging the error message,
391	 * because rcleanup will zap some variables which may have been
392	 * supplied as parameters for that msg...
393	 */
394	rcleanup(0);
395	/*
396	 * Add a minimal delay before returning the final error code to
397	 * the sending host.  This just in case that machine responds
398	 * this error by INSTANTLY retrying (and instantly re-failing...).
399	 * It would be stupid of the sending host to do that, but if there
400	 * was a broken implementation which did it, the result might be
401	 * obscure performance problems and a flood of syslog messages on
402	 * the receiving host.
403	 */
404	sleep(2);		/* a paranoid throttling measure */
405	putchar('\1');		/* return error code */
406	exit(1);
407}
408