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