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