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
60 static char dfname[NAME_MAX]; /* data files */
61 static int minfree; /* keep at least minfree blocks available */
62 static char *sp = "";
63 static char tfname[NAME_MAX]; /* tmp copy of cf before linking */
64
65 static int chksize(int);
66 static void frecverr(const char *, ...)
67 __attribute__((__format__(__printf__, 1, 2)));
68 static int noresponse(void);
69 static void rcleanup(int);
70 static int read_number(char *);
71 static int readfile(char *, int);
72 static int readjob(void);
73
74
75 void
recvjob(void)76 recvjob(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 */
129 static int
readjob(void)130 readjob(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 */
224 static int
readfile(char * file,int size)225 readfile(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
268 static int
noresponse(void)269 noresponse(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 */
284 static int
chksize(int size)285 chksize(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
303 static int
read_number(char * fn)304 read_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 */
322 static void
rcleanup(int signo)323 rcleanup(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
342 static void
frecverr(const char * msg,...)343 frecverr(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