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[NAMELEN] = '\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[NAMELEN] = '\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 void 323 main() 324 { 325 unsigned char *ap, *bp, *cp, *savbufpnt; 326 int i, blen, rv, saveflg, savargcnt; 327 struct jobinfo *jinfop; 328 329 signal(SIGHUP, SIG_IGN); 330 signal(SIGALRM, alarmhandler); 331 cp = argvstr; 332 /* setup argv[0] for exec */ 333 argvals[argcnt++] = cp; 334 for (bp = (unsigned char *)LP, i = 0; (*bp != '\0') && (i < ARGSIZ-1); *cp++ = *bp++, i++); 335 *cp++ = '\0'; 336 /* get the first line sent and parse it as arguments for lp */ 337 if ((rv=readline(0)) < 0) 338 exit(1); 339 bp = lnbuf; 340 /* setup the remaining arguments */ 341 /* check for BSD style request */ 342 /* ^A, ^B, ^C, ^D, ^E (for BSD lpr) */ 343 switch (*bp) { 344 case '\001': 345 case '\003': 346 case '\004': 347 bp++; /* drop the ctrl character from the input */ 348 argvals[argcnt++] = cp; 349 *cp++ = '-'; *cp++ = 'q'; *cp++ = '\0'; /* -q */ 350 argvals[argcnt++] = cp; 351 *cp++ = '-'; *cp++ = 'd'; /* -d */ 352 CPYFIELD(bp, cp); /* printer */ 353 *cp++ = '\0'; 354 break; 355 case '\002': 356 bp++; /* drop the ctrl character from the input */ 357 argvals[argcnt++] = cp; 358 *cp++ = '-'; *cp++ = 'd'; /* -d */ 359 CPYFIELD(bp, cp); /* printer */ 360 *cp++ = '\0'; 361 ACK(); 362 savargcnt = argcnt; 363 savbufpnt = cp; 364 while ((rv=getfiles())) { 365 jinfop = getjobinfo(cntrlfd); 366 close(cntrlfd); 367 argcnt = savargcnt; 368 cp = savbufpnt; 369 argvals[argcnt++] = cp; 370 *cp++ = '-'; *cp++ = 'M'; /* -M */ 371 bp = (unsigned char *)jinfop->host; 372 CPYFIELD(bp, cp); /* host name */ 373 *cp++ = '\0'; 374 argvals[argcnt++] = cp; 375 *cp++ = '-'; *cp++ = 'u'; /* -u */ 376 bp = (unsigned char *)jinfop->user; 377 CPYFIELD(bp, cp); /* user name */ 378 *cp++ = '\0'; 379 for(i=0;i<rv;i++) 380 forklp(datafd[i]); 381 } 382 exit(0); 383 case '\005': 384 bp++; /* drop the ctrl character from the input */ 385 argvals[argcnt++] = cp; 386 *cp++ = '-'; *cp++ = 'k'; *cp++ = '\0'; /* -k */ 387 argvals[argcnt++] = cp; 388 *cp++ = '-'; *cp++ = 'd'; /* -d */ 389 CPYFIELD(bp, cp); /* printer */ 390 *cp++ = '\0'; 391 argvals[argcnt++] = cp; 392 *cp++ = '-'; ap = cp; *cp++ = 'u'; /* -u */ 393 CPYFIELD(bp, cp); /* username */ 394 395 /* deal with bug in lprng where the username is not supplied 396 */ 397 if (ap == (cp-1)) { 398 ap = (unsigned char *)"none"; 399 CPYFIELD(ap, cp); 400 } 401 402 *cp++ = '\0'; 403 datafd[0] = tempfile(); 404 blen = strlen((const char *)bp); 405 if (write(datafd[0], bp, blen) != blen) { 406 error("write error\n"); 407 exit(6); 408 } 409 if (write(datafd[0], "\n", 1) != 1) { 410 error("write error\n"); 411 exit(6); 412 } 413 break; 414 default: 415 /* otherwise get my lp arguments */ 416 do { 417 /* move to next non-white space */ 418 while (*bp==' '||*bp=='\t') 419 ++bp; 420 if (*bp=='\n') continue; 421 /* only accept arguments beginning with - 422 * this is done to prevent the printing of 423 * local files from the destination host 424 */ 425 if (*bp=='-') { 426 argvals[argcnt++] = cp; 427 saveflg = 1; 428 } else 429 saveflg = 0; 430 /* move to next white space copying text to argument buffer */ 431 while (*bp!=' ' && *bp!='\t' && *bp!='\n' 432 && *bp!='\0') { 433 *cp = *bp++; 434 cp += saveflg; 435 } 436 *cp = '\0'; 437 cp += saveflg; 438 } while (*bp!='\n' && *bp!='\0'); 439 if (readline(0) < 0) exit(7); 440 datafd[0] = tempfile(); 441 if(readfile(datafd[0], atoi((const char *)lnbuf)) < 0) { 442 error("readfile failed\n"); 443 exit(8); 444 } 445 } 446 forklp(datafd[0]); 447 exit(0); 448 } 449