1/*	$OpenBSD: recvjob.c,v 1.26 2015/01/16 06:40:18 deraadt Exp $	*/
2/*	$NetBSD: recvjob.c,v 1.14 2001/12/04 22:52:44 christos Exp $	*/
3
4/*
5 * Copyright (c) 1983, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * Receive printer jobs from the network, queue them and
36 * start the printer daemon.
37 */
38#include <sys/types.h>
39#include <sys/mount.h>
40#include <sys/stat.h>
41
42#include <unistd.h>
43#include <signal.h>
44#include <fcntl.h>
45#include <dirent.h>
46#include <syslog.h>
47#include <stdio.h>
48#include <errno.h>
49#include <stdlib.h>
50#include <string.h>
51#include <stdarg.h>
52#include <limits.h>
53#include "lp.h"
54#include "lp.local.h"
55#include "extern.h"
56#include "pathnames.h"
57
58#define ack()	(void)write(STDOUT_FILENO, sp, 1);
59
60static char	 dfname[NAME_MAX];	/* data files */
61static int	 minfree;       /* keep at least minfree blocks available */
62static char	*sp = "";
63static char	 tfname[NAME_MAX];	/* tmp copy of cf before linking */
64
65static int        chksize(int);
66static void       frecverr(const char *, ...)
67	__attribute__((__format__(__printf__, 1, 2)));
68static int        noresponse(void);
69static void       rcleanup(int);
70static int        read_number(char *);
71static int        readfile(char *, int);
72static int        readjob(void);
73
74
75void
76recvjob(void)
77{
78	struct stat stb;
79	int status;
80
81	/*
82	 * Perform lookup for printer name or abbreviation
83	 */
84	if ((status = cgetent(&bp, printcapdb, printer)) == -2)
85		frecverr("cannot open printer description file");
86	else if (status == -1)
87		frecverr("unknown printer %s", printer);
88	else if (status == -3)
89		fatal("potential reference loop detected in printcap file");
90
91	if (cgetstr(bp, "lf", &LF) == -1)
92		LF = _PATH_CONSOLE;
93	if (cgetstr(bp, "sd", &SD) == -1)
94		SD = _PATH_DEFSPOOL;
95	if (cgetstr(bp, "lo", &LO) == -1)
96		LO = DEFLOCK;
97
98	(void)close(2);			/* set up log file */
99	PRIV_START;
100	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
101		syslog(LOG_ERR, "%s: %m", LF);
102		(void)open(_PATH_DEVNULL, O_WRONLY);
103	}
104	PRIV_END;
105
106	if (chdir(SD) < 0)
107		frecverr("%s: %s: %m", printer, SD);
108	if (stat(LO, &stb) == 0) {
109		if (stb.st_mode & 010) {
110			/* queue is disabled */
111			putchar('\1');		/* return error code */
112			exit(1);
113		}
114	} else if (stat(SD, &stb) < 0)
115		frecverr("%s: %s: %m", printer, SD);
116
117	minfree = 2 * read_number("minfree");	/* scale KB to 512 blocks */
118	signal(SIGTERM, rcleanup);
119	signal(SIGPIPE, rcleanup);
120
121	if (readjob())
122		printjob();
123}
124
125/*
126 * Read printer jobs sent by lpd and copy them to the spooling directory.
127 * Return the number of jobs successfully transferred.
128 */
129static int
130readjob(void)
131{
132	int size, nfiles;
133	char *cp;
134
135	ack();
136	nfiles = 0;
137	for (;;) {
138		/*
139		 * Read a command to tell us what to do
140		 */
141		cp = line;
142		do {
143			if ((size = read(STDOUT_FILENO, cp, 1)) != 1) {
144				if (size < 0)
145					frecverr("%s: Lost connection",
146					    printer);
147				return(nfiles);
148			}
149		} while (*cp++ != '\n' && (cp - line + 1) < sizeof(line));
150		if (cp - line + 1 >= sizeof(line))
151			frecverr("readjob overflow");
152		*--cp = '\0';
153		cp = line;
154		switch (*cp++) {
155		case '\1':	/* cleanup because data sent was bad */
156			rcleanup(0);
157			continue;
158
159		case '\2':	/* read cf file */
160			size = 0;
161			while (*cp >= '0' && *cp <= '9')
162				size = size * 10 + (*cp++ - '0');
163			if (*cp++ != ' ')
164				break;
165			/*
166			 * host name has been authenticated, we use our
167			 * view of the host name since we may be passed
168			 * something different than what gethostbyaddr()
169			 * returns
170			 */
171			strlcpy(cp + 6, from, sizeof(line) + line - cp - 6);
172			if (strchr(cp, '/'))
173				frecverr("readjob: %s: illegal path name", cp);
174			strlcpy(tfname, cp, sizeof(tfname));
175			tfname[0] = 't';
176			if (!chksize(size)) {
177				(void)write(STDOUT_FILENO, "\2", 1);
178				continue;
179			}
180			/*
181			 * XXX
182			 * We blindly believe what the remote host puts
183			 * for the path to the df file.  In general this
184			 * is OK since we don't allow paths with '/' in
185			 * them.  Still, it would be better to sanity
186			 * check the cf file sent to us and make the
187			 * df name match the cf name we used.  That way
188			 * we avoid any possible collisions.
189			 */
190			if (!readfile(tfname, size)) {
191				rcleanup(0);
192				continue;
193			}
194			if (link(tfname, cp) < 0)
195				frecverr("link %s %s: %m", tfname, cp);
196			(void)unlink(tfname);
197			tfname[0] = '\0';
198			nfiles++;
199			continue;
200
201		case '\3':	/* read df file */
202			size = 0;
203			while (*cp >= '0' && *cp <= '9')
204				size = size * 10 + (*cp++ - '0');
205			if (*cp++ != ' ')
206				break;
207			if (strchr(cp, '/'))
208				frecverr("readjob: %s: illegal path name", cp);
209			if (!chksize(size)) {
210				(void)write(STDOUT_FILENO, "\2", 1);
211				continue;
212			}
213			(void)strlcpy(dfname, cp, sizeof(dfname));
214			(void)readfile(dfname, size);
215			continue;
216		}
217		frecverr("protocol screwup: %s", line);
218	}
219}
220
221/*
222 * Read files send by lpd and copy them to the spooling directory.
223 */
224static int
225readfile(char *file, int size)
226{
227	char *cp;
228	char buf[BUFSIZ];
229	int i, j, amt;
230	int fd, err;
231
232	if ((fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD)) < 0)
233		frecverr("readfile: %s: illegal path name: %m", file);
234	ack();
235	err = 0;
236	for (i = 0; i < size; i += BUFSIZ) {
237		amt = BUFSIZ;
238		cp = buf;
239		if (i + amt > size)
240			amt = size - i;
241		do {
242			j = read(STDOUT_FILENO, cp, amt);
243			if (j <= 0)
244				frecverr("Lost connection");
245			amt -= j;
246			cp += j;
247		} while (amt > 0);
248		amt = BUFSIZ;
249		if (i + amt > size)
250			amt = size - i;
251		if (write(fd, buf, amt) != amt) {
252			err++;
253			break;
254		}
255	}
256	(void)close(fd);
257	if (err)
258		frecverr("%s: write error", file);
259	if (noresponse()) {		/* file sent had bad data in it */
260		if (strchr(file, '/') == NULL)
261			(void)unlink(file);
262		return(0);
263	}
264	ack();
265	return(1);
266}
267
268static int
269noresponse(void)
270{
271	char resp;
272
273	if (read(STDOUT_FILENO, &resp, 1) != 1)
274		frecverr("Lost connection");
275	if (resp == '\0')
276		return(0);
277	return(1);
278}
279
280/*
281 * Check to see if there is enough space on the disk for size bytes.
282 * 1 == OK, 0 == Not OK.
283 */
284static int
285chksize(int size)
286{
287	int64_t spacefree;
288	struct statfs sfb;
289
290	if (size <= 0)
291		return (0);
292	if (statfs(".", &sfb) < 0) {
293		syslog(LOG_ERR, "%s: %m", "statfs(\".\")");
294		return (1);
295	}
296	spacefree = sfb.f_bavail * (sfb.f_bsize / 512);
297	size = (size + 511) / 512;
298	if (minfree + size > spacefree)
299		return(0);
300	return(1);
301}
302
303static int
304read_number(char *fn)
305{
306	char lin[80];
307	FILE *fp;
308
309	if ((fp = fopen(fn, "r")) == NULL)
310		return (0);
311	if (fgets(lin, sizeof(lin), fp) == NULL) {
312		fclose(fp);
313		return (0);
314	}
315	fclose(fp);
316	return (atoi(lin));
317}
318
319/*
320 * Remove all the files associated with the current job being transferred.
321 */
322static void
323rcleanup(int signo)
324{
325	int save_errno = errno;
326
327	if (tfname[0] && strchr(tfname, '/') == NULL)
328		(void)unlink(tfname);
329	if (dfname[0] && strchr(dfname, '/') == NULL) {
330		do {
331			do
332				(void)unlink(dfname);
333			while (dfname[2]-- != 'A')
334				;
335			dfname[2] = 'z';
336		} while (dfname[0]-- != 'd');
337	}
338	dfname[0] = '\0';
339	errno = save_errno;
340}
341
342static void
343frecverr(const char *msg, ...)
344{
345	extern char fromb[];
346	va_list ap;
347
348	va_start(ap, msg);
349	rcleanup(0);
350	syslog(LOG_ERR, "%s", fromb);
351	vsyslog(LOG_ERR, msg, ap);
352	va_end(ap);
353	putchar('\1');		/* return error code */
354	exit(1);
355}
356