1/*	$NetBSD: recvjob.c,v 1.22 2009/01/18 09:57:26 lukem Exp $	*/
2
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#include <sys/cdefs.h>
34
35#ifndef lint
36__COPYRIGHT("@(#) Copyright (c) 1983, 1993\
37 The Regents of the University of California.  All rights reserved.");
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)recvjob.c	8.2 (Berkeley) 4/27/95";
43#else
44__RCSID("$NetBSD: recvjob.c,v 1.22 2009/01/18 09:57:26 lukem Exp $");
45#endif
46#endif /* not lint */
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 <syslog.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include "lp.h"
65#include "lp.local.h"
66#include "extern.h"
67#include "pathnames.h"
68
69#define ack()	(void)write(STDOUT_FILENO, sp, 1);
70
71static char	 dfname[NAME_MAX];	/* data files */
72static int	 minfree;       /* keep at least minfree blocks available */
73static const char *sp = "";
74static char	 tfname[NAME_MAX];	/* tmp copy of cf before linking */
75
76static int        chksize(int);
77static void       frecverr(const char *, ...) __dead __printflike(1, 2);
78static int        noresponse(void);
79static void       rcleanup(int);
80static int        read_number(const char *);
81static int        readfile(char *, int);
82static int        readjob(void);
83
84
85void
86recvjob(void)
87{
88	struct stat stb;
89	int fd;
90
91	getprintcap(printer);
92	/* Set up the log file. */
93	if ((fd = open(LF, O_WRONLY|O_APPEND, 0664)) < 0) {
94		syslog(LOG_ERR, "%s: %m", LF);
95		fd = open(_PATH_DEVNULL, O_WRONLY);
96	}
97	if (fd > 0) {
98		(void) dup2(fd, STDERR_FILENO);
99		(void) close(fd);
100	} else
101		(void) close(STDERR_FILENO);
102
103	if (chdir(SD) < 0)
104		frecverr("%s: %s: %m", printer, SD);
105	if (stat(LO, &stb) == 0) {
106		if (stb.st_mode & 010) {
107			/* queue is disabled */
108			putchar('\1');		/* return error code */
109			exit(1);
110		}
111	} else if (stat(SD, &stb) < 0)
112		frecverr("%s: %s: %m", printer, SD);
113	minfree = 2 * read_number("minfree");	/* scale KB to 512 blocks */
114	signal(SIGTERM, rcleanup);
115	signal(SIGPIPE, rcleanup);
116
117	if (readjob())
118		printjob();
119}
120
121/*
122 * Read printer jobs sent by lpd and copy them to the spooling directory.
123 * Return the number of jobs successfully transfered.
124 */
125static int
126readjob(void)
127{
128	int size, nfiles;
129	char *cp;
130
131	ack();
132	nfiles = 0;
133	for (;;) {
134		/*
135		 * Read a command to tell us what to do
136		 */
137		cp = line;
138		do {
139			if ((size = read(STDOUT_FILENO, cp, 1)) != 1) {
140				if (size < 0)
141					frecverr("%s: Lost connection",
142					    printer);
143				return(nfiles);
144			}
145		} while (*cp++ != '\n' && (size_t)(cp - line + 1) < sizeof(line));
146		if ((size_t)(cp - line + 1) >= sizeof(line))
147			frecverr("readjob overflow");
148		*--cp = '\0';
149		cp = line;
150		switch (*cp++) {
151		case '\1':	/* cleanup because data sent was bad */
152			rcleanup(0);
153			continue;
154
155		case '\2':	/* read cf file */
156			size = 0;
157			while (*cp >= '0' && *cp <= '9')
158				size = size * 10 + (*cp++ - '0');
159			if (*cp++ != ' ')
160				break;
161			/*
162			 * host name has been authenticated, we use our
163			 * view of the host name since we may be passed
164			 * something different than what gethostbyaddr()
165			 * returns
166			 */
167 			(void)strlcpy(cp + 6, from,
168			    sizeof(line) + line - cp - 6);
169			if (strchr(cp, '/'))
170				frecverr("readjob: %s: illegal path name", cp);
171			(void)strlcpy(tfname, cp, sizeof(tfname));
172			tfname[0] = 't';
173			if (!chksize(size)) {
174				(void)write(STDOUT_FILENO, "\2", 1);
175				continue;
176			}
177			if (!readfile(tfname, size)) {
178				rcleanup(0);
179				continue;
180			}
181			if (link(tfname, cp) < 0)
182				frecverr("%s: %m", tfname);
183			(void)unlink(tfname);
184			tfname[0] = '\0';
185			nfiles++;
186			continue;
187
188		case '\3':	/* read df file */
189			size = 0;
190			while (*cp >= '0' && *cp <= '9')
191				size = size * 10 + (*cp++ - '0');
192			if (*cp++ != ' ')
193				break;
194			if (!chksize(size)) {
195				(void)write(STDOUT_FILENO, "\2", 1);
196				continue;
197			}
198			if (strchr(cp, '/'))
199				frecverr("readjob: %s: illegal path name", cp);
200			(void)strlcpy(dfname, cp, sizeof(dfname));
201			(void)readfile(dfname, size);
202			continue;
203		}
204		frecverr("protocol screwup: %s", line);
205	}
206}
207
208/*
209 * Read files send by lpd and copy them to the spooling directory.
210 */
211static int
212readfile(char *file, int size)
213{
214	char *cp;
215	char buf[BUFSIZ];
216	int i, j, amt;
217	int fd, err;
218
219	fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
220	if (fd < 0)
221		frecverr("readfile: %s: illegal path name: %m", file);
222	ack();
223	err = 0;
224	for (i = 0; i < size; i += BUFSIZ) {
225		amt = BUFSIZ;
226		cp = buf;
227		if (i + amt > size)
228			amt = size - i;
229		do {
230			j = read(STDOUT_FILENO, cp, amt);
231			if (j <= 0)
232				frecverr("Lost connection");
233			amt -= j;
234			cp += j;
235		} while (amt > 0);
236		amt = BUFSIZ;
237		if (i + amt > size)
238			amt = size - i;
239		if (write(fd, buf, amt) != amt) {
240			err++;
241			break;
242		}
243	}
244	(void)close(fd);
245	if (err)
246		frecverr("%s: write error", file);
247	if (noresponse()) {		/* file sent had bad data in it */
248		(void)unlink(file);
249		return(0);
250	}
251	ack();
252	return(1);
253}
254
255static int
256noresponse(void)
257{
258	char resp;
259
260	if (read(STDOUT_FILENO, &resp, 1) != 1)
261		frecverr("Lost connection");
262	if (resp == '\0')
263		return(0);
264	return(1);
265}
266
267/*
268 * Check to see if there is enough space on the disk for size bytes.
269 * 1 == OK, 0 == Not OK.
270 */
271static int
272chksize(int size)
273{
274	int spacefree;
275	struct statvfs sfb;
276
277	if (statvfs(".", &sfb) < 0) {
278		syslog(LOG_ERR, "%s: %m", "statfs(\".\")");
279		return (1);
280	}
281	spacefree = sfb.f_bavail * (sfb.f_frsize / 512);
282	size = (size + 511) / 512;
283	if (minfree + size > spacefree)
284		return(0);
285	return(1);
286}
287
288static int
289read_number(const char *fn)
290{
291	char lin[80];
292	FILE *fp;
293
294	if ((fp = fopen(fn, "r")) == NULL)
295		return (0);
296	if (fgets(lin, 80, fp) == NULL) {
297		fclose(fp);
298		return (0);
299	}
300	fclose(fp);
301	return (atoi(lin));
302}
303
304/*
305 * Remove all the files associated with the current job being transfered.
306 */
307static void
308rcleanup(int signo)
309{
310	if (tfname[0])
311		(void)unlink(tfname);
312	if (dfname[0])
313		do {
314			do {
315				if (strchr(dfname, '/') == 0)
316					(void)unlink(dfname);
317			} while (dfname[2]-- != 'A');
318			dfname[2] = 'z';
319		} while (dfname[0]-- != 'd');
320	dfname[0] = '\0';
321}
322
323#include <stdarg.h>
324
325static void
326frecverr(const char *msg, ...)
327{
328	extern char fromb[];
329	va_list ap;
330
331	va_start(ap, msg);
332	rcleanup(0);
333	syslog(LOG_ERR, "%s", fromb);
334	vsyslog(LOG_ERR, msg, ap);
335	va_end(ap);
336	putchar('\1');		/* return error code */
337	exit(1);
338}
339