1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <signal.h> 7 #include <stdarg.h> 8 #include <time.h> 9 #include <unistd.h> 10 #include <sys/types.h> 11 #include <sys/wait.h> 12 13 /* for Plan 9 */ 14 #ifdef PLAN9 15 #define LP "/bin/lp" 16 #define TMPDIR "/sys/lib/lp/tmp" 17 #define LPDAEMONLOG "/sys/lib/lp/log/lpdaemonl" 18 #endif 19 /* for Tenth Edition systems */ 20 #ifdef V10 21 #define LP "/usr/bin/lp" 22 #define TMPDIR "/tmp" 23 #define LPDAEMONLOG "/tmp/lpdaemonl" 24 #endif 25 /* for System V or BSD systems */ 26 #if defined(SYSV) || defined(BSD) 27 #define LP "/v/bin/lp" 28 #define TMPDIR "/tmp" 29 #define LPDAEMONLOG "/tmp/lpdaemonl" 30 #endif 31 32 #define ARGSIZ 4096 33 #define NAMELEN 30 34 35 unsigned char argvstr[ARGSIZ]; /* arguments after parsing */ 36 unsigned char *argvals[ARGSIZ/2+1]; /* pointers to arguments after parsing */ 37 int ascnt = 0, argcnt = 0; /* number of arguments parsed */ 38 /* for 'stuff' gleened from lpr cntrl file */ 39 struct jobinfo { 40 char user[NAMELEN+1]; 41 char host[NAMELEN+1]; 42 } *getjobinfo(); 43 44 #define MIN(a,b) ((a<b)?a:b) 45 46 #define CPYFIELD(src, dst) { while (*(src)!=' ' && *(src)!='\t' && *(src)!='\r' && *(src)!='\n' && *(src)!='\0') *(dst)++ = *(src)++; } 47 48 #define ACK() write(1, "", 1) 49 #define NAK() write(1, "\001", 1) 50 51 #define LNBFSZ 4096 52 unsigned char lnbuf[LNBFSZ]; 53 54 #define RDSIZE 512 55 unsigned char jobbuf[RDSIZE]; 56 57 int datafd[400], cntrlfd = -1; 58 59 int dbgstate = 0; 60 char *dbgstrings[] = { 61 "", 62 "sendack1", 63 "send", 64 "rcvack", 65 "sendack2", 66 "done" 67 }; 68 69 void 70 error(char *s1, ...) 71 { 72 FILE *fp; 73 long thetime; 74 char *chartime; 75 va_list ap; 76 char *args[8]; 77 int argno = 0; 78 79 if((fp=fopen(LPDAEMONLOG, "a"))==NULL) { 80 fprintf(stderr, "cannot open %s in append mode\n", LPDAEMONLOG); 81 return; 82 } 83 time(&thetime); 84 chartime = ctime(&thetime); 85 fprintf(fp, "%.15s [%5.5d] ", &(chartime[4]), getpid()); 86 va_start(ap, s1); 87 while((args[argno++] = va_arg(ap, char*)) && argno<8) 88 ; 89 va_end(ap); 90 fprintf(fp, s1, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); 91 fclose(fp); 92 } 93 94 void 95 forklp(int inputfd) 96 { 97 int i, cpid; 98 unsigned char *bp, *cp; 99 unsigned char logent[LNBFSZ]; 100 101 /* log this call to lp */ 102 cp = logent; 103 for (i=1; i<argcnt; i++) { 104 bp = argvals[i]; 105 if (cp+strlen((const char *)bp)+1 < logent+LNBFSZ-1) { 106 CPYFIELD(bp, cp); 107 *cp++ = ' '; 108 } 109 } 110 *--cp = '\n'; 111 *++cp = '\0'; 112 error((const char *)logent); 113 switch((cpid=fork())){ 114 case -1: 115 error("fork error\n"); 116 exit(2); 117 case 0: 118 if (inputfd != 0) 119 dup2(inputfd, 0); 120 dup2(1, 2); 121 lseek(0, 0L, 0); 122 execvp(LP, (const char **)argvals); 123 error("exec failed\n"); 124 exit(3); 125 default: 126 while(wait((int *)0) != cpid) 127 ; 128 } 129 } 130 131 int 132 tempfile(void) 133 { 134 static tindx = 0; 135 char tmpf[sizeof(TMPDIR)+64]; 136 int crtfd, tmpfd; 137 138 sprintf(tmpf, "%s/lp%d.%d", TMPDIR, getpid(), tindx++); 139 if((crtfd=creat(tmpf, 0666)) < 0) { 140 error("cannot create temp file %s\n", tmpf); 141 NAK(); 142 exit(3); 143 } 144 if((tmpfd=open(tmpf, 2)) < 0) { 145 error("cannot open temp file %s\n", tmpf); 146 NAK(); 147 exit(3); 148 } 149 close(crtfd); 150 unlink(tmpf); /* comment out for debugging */ 151 return(tmpfd); 152 } 153 154 int 155 readfile(int outfd, int bsize) 156 { 157 int rv; 158 159 dbgstate = 1; 160 alarm(60); 161 ACK(); 162 dbgstate = 2; 163 for(; bsize > 0; bsize -= rv) { 164 alarm(60); 165 if((rv=read(0, jobbuf, MIN(bsize,RDSIZE))) < 0) { 166 error("error reading input, %d unread\n", bsize); 167 exit(4); 168 } else if (rv == 0) { 169 error("connection closed prematurely\n"); 170 exit(4); 171 } else if((write(outfd, jobbuf, rv)) != rv) { 172 error("error writing temp file, %d unread\n", bsize); 173 exit(5); 174 } 175 } 176 dbgstate = 3; 177 alarm(60); 178 if (((rv=read(0, jobbuf, 1))==1) && (*jobbuf=='\0')) { 179 alarm(60); 180 ACK(); 181 dbgstate = 4; 182 alarm(0); 183 return(outfd); 184 } 185 alarm(0); 186 error("received bad status <%d> from sender\n", *jobbuf); 187 error("rv=%d\n", rv); 188 NAK(); 189 return(-1); 190 } 191 192 /* reads a line from the input into lnbuf 193 * if there is no error, it returns 194 * the number of characters in the buffer 195 * if there is an error and there where characters 196 * read, it returns the negative value of the 197 * number of characters read 198 * if there is an error and no characters were read, 199 * it returns the negative value of 1 greater than 200 * the size of the line buffer 201 */ 202 int 203 readline(int inpfd) 204 { 205 unsigned char *ap; 206 int i, rv; 207 208 ap = lnbuf; 209 lnbuf[0] = '\0'; 210 i = 0; 211 alarm(60); 212 do { 213 rv = read(inpfd, ap, 1); 214 } while (rv==1 && ++i && *ap != '\n' && ap++ && (i < LNBFSZ - 2)); 215 alarm(0); 216 if (i != 0 && *ap != '\n') { 217 *++ap = '\n'; 218 i++; 219 } 220 *++ap = '\0'; 221 if (rv < 0) { 222 error("read error; lost connection\n"); 223 if (i==0) i = -(LNBFSZ+1); 224 else i = -i; 225 } 226 return(i); 227 } 228 229 int 230 getfiles(void) 231 { 232 unsigned char *ap; 233 int filecnt, bsize, rv; 234 235 filecnt = 0; 236 /* get a line, hopefully containing a ctrl char, size, and name */ 237 for(;;) { 238 ap = lnbuf; 239 if ((rv=readline(0)) < 0) NAK(); 240 if (rv <= 0) { 241 return(filecnt); 242 } 243 switch(*ap++) { 244 case '\1': /* cleanup - data sent was bad (whatever that means) */ 245 break; 246 case '\2': /* read control file */ 247 bsize = atoi((const char *)ap); 248 cntrlfd = tempfile(); 249 if (readfile(cntrlfd, bsize) < 0) { 250 close(cntrlfd); 251 NAK(); 252 return(0); 253 } 254 break; 255 case '\3': /* read data file */ 256 bsize = atoi((const char *)ap); 257 datafd[filecnt] = tempfile(); 258 if (readfile(datafd[filecnt], bsize) < 0) { 259 close(datafd[filecnt]); 260 NAK(); 261 return(0); 262 } 263 filecnt++; 264 break; 265 default: 266 error("protocol error <%d>\n", *(ap-1)); 267 NAK(); 268 } 269 } 270 return(filecnt); 271 } 272 273 struct jobinfo * 274 getjobinfo(int fd) 275 { 276 unsigned char *ap; 277 int rv; 278 static struct jobinfo info; 279 280 if (fd < 0) error("getjobinfo: bad file descriptor\n"); 281 if (lseek(fd, 0L, 0) < 0) { 282 error("error seeking in temp file\n"); 283 exit(7); 284 } 285 /* the following strings should be < NAMELEN or else they will not 286 * be null terminated. 287 */ 288 strncpy(info.user, "daemon", NAMELEN); 289 strncpy(info.host, "nowhere", NAMELEN); 290 /* there may be a space after the name and host. It will be filtered out 291 * by CPYFIELD. 292 */ 293 while ((rv=readline(fd)) > 0) { 294 ap = lnbuf; 295 ap[rv-1] = '\0'; /* remove newline from string */ 296 switch (*ap) { 297 case 'H': 298 if (ap[1] == '\0') 299 strncpy(info.host, "unknown", NAMELEN); 300 else 301 strncpy(info.host, (const char *)&ap[1], NAMELEN); 302 info.host[strlen(info.host)] = '\0'; 303 break; 304 case 'P': 305 if (ap[1] == '\0') 306 strncpy(info.user, "unknown", NAMELEN); 307 else 308 strncpy(info.user, (const char *)&ap[1], NAMELEN); 309 info.user[strlen(info.user)] = '\0'; 310 break; 311 } 312 } 313 return(&info); 314 } 315 316 void 317 alarmhandler(int sig) { 318 signal(sig, alarmhandler); 319 error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]); 320 } 321 322 main() 323 { 324 unsigned char *ap, *bp, *cp, *savbufpnt; 325 int i, blen, rv, saveflg, savargcnt; 326 struct jobinfo *jinfop; 327 328 signal(SIGHUP, SIG_IGN); 329 signal(SIGALRM, alarmhandler); 330 cp = argvstr; 331 /* setup argv[0] for exec */ 332 argvals[argcnt++] = cp; 333 for (bp = (unsigned char *)LP, i = 0; (*bp != '\0') && (i < ARGSIZ-1); *cp++ = *bp++, i++); 334 *cp++ = '\0'; 335 /* get the first line sent and parse it as arguments for lp */ 336 if ((rv=readline(0)) < 0) 337 exit(1); 338 bp = lnbuf; 339 /* setup the remaining arguments */ 340 /* check for BSD style request */ 341 /* ^A, ^B, ^C, ^D, ^E (for BSD lpr) */ 342 switch (*bp) { 343 case '\001': 344 case '\003': 345 case '\004': 346 bp++; /* drop the ctrl character from the input */ 347 argvals[argcnt++] = cp; 348 *cp++ = '-'; *cp++ = 'q'; *cp++ = '\0'; /* -q */ 349 argvals[argcnt++] = cp; 350 *cp++ = '-'; *cp++ = 'd'; /* -d */ 351 CPYFIELD(bp, cp); /* printer */ 352 *cp++ = '\0'; 353 break; 354 case '\002': 355 bp++; /* drop the ctrl character from the input */ 356 argvals[argcnt++] = cp; 357 *cp++ = '-'; *cp++ = 'd'; /* -d */ 358 CPYFIELD(bp, cp); /* printer */ 359 *cp++ = '\0'; 360 ACK(); 361 savargcnt = argcnt; 362 savbufpnt = cp; 363 while ((rv=getfiles())) { 364 jinfop = getjobinfo(cntrlfd); 365 close(cntrlfd); 366 argcnt = savargcnt; 367 cp = savbufpnt; 368 argvals[argcnt++] = cp; 369 *cp++ = '-'; *cp++ = 'M'; /* -M */ 370 bp = (unsigned char *)jinfop->host; 371 CPYFIELD(bp, cp); /* host name */ 372 *cp++ = '\0'; 373 argvals[argcnt++] = cp; 374 *cp++ = '-'; *cp++ = 'u'; /* -u */ 375 bp = (unsigned char *)jinfop->user; 376 CPYFIELD(bp, cp); /* user name */ 377 *cp++ = '\0'; 378 for(i=0;i<rv;i++) 379 forklp(datafd[i]); 380 } 381 exit(0); 382 case '\005': 383 bp++; /* drop the ctrl character from the input */ 384 argvals[argcnt++] = cp; 385 *cp++ = '-'; *cp++ = 'k'; *cp++ = '\0'; /* -k */ 386 argvals[argcnt++] = cp; 387 *cp++ = '-'; *cp++ = 'd'; /* -d */ 388 CPYFIELD(bp, cp); /* printer */ 389 *cp++ = '\0'; 390 argvals[argcnt++] = cp; 391 *cp++ = '-'; ap = cp; *cp++ = 'u'; /* -u */ 392 CPYFIELD(bp, cp); /* username */ 393 394 /* deal with bug in lprng where the username is not supplied 395 */ 396 if (ap == (cp-1)) { 397 ap = (unsigned char *)"none"; 398 CPYFIELD(ap, cp); 399 } 400 401 *cp++ = '\0'; 402 datafd[0] = tempfile(); 403 blen = strlen((const char *)bp); 404 if (write(datafd[0], bp, blen) != blen) { 405 error("write error\n"); 406 exit(6); 407 } 408 if (write(datafd[0], "\n", 1) != 1) { 409 error("write error\n"); 410 exit(6); 411 } 412 break; 413 default: 414 /* otherwise get my lp arguments */ 415 do { 416 /* move to next non-white space */ 417 while (*bp==' '||*bp=='\t') 418 ++bp; 419 if (*bp=='\n') continue; 420 /* only accept arguments beginning with - 421 * this is done to prevent the printing of 422 * local files from the destination host 423 */ 424 if (*bp=='-') { 425 argvals[argcnt++] = cp; 426 saveflg = 1; 427 } else 428 saveflg = 0; 429 /* move to next white space copying text to argument buffer */ 430 while (*bp!=' ' && *bp!='\t' && *bp!='\n' 431 && *bp!='\0') { 432 *cp = *bp++; 433 cp += saveflg; 434 } 435 *cp = '\0'; 436 cp += saveflg; 437 } while (*bp!='\n' && *bp!='\0'); 438 if (readline(0) < 0) exit(7); 439 datafd[0] = tempfile(); 440 if(readfile(datafd[0], atoi((const char *)lnbuf)) < 0) { 441 error("readfile failed\n"); 442 exit(8); 443 } 444 } 445 forklp(datafd[0]); 446 exit(0); 447 } 448