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