1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/wait.h> 5 #include <fcntl.h> 6 #include <stdlib.h> 7 #include <stdio.h> 8 #include <signal.h> 9 #include <errno.h> 10 #include <time.h> 11 #include <string.h> 12 #include <stdarg.h> 13 14 /* for Plan 9 */ 15 #ifdef PLAN9 16 #define LP "/bin/lp" 17 #define TMPDIR "/sys/lib/lp/tmp" 18 #define LPDAEMONLOG "/sys/lib/lp/log/lpdaemonl" 19 #endif 20 /* for Tenth Edition systems */ 21 #ifdef V10 22 #define LP "/usr/bin/lp" 23 #define TMPDIR "/tmp" 24 #define LPDAEMONLOG "/tmp/lpdaemonl" 25 #endif 26 /* for System V or BSD systems */ 27 #if defined(SYSV) || defined(BSD) 28 #define LP "/v/bin/lp" 29 #define TMPDIR "/tmp" 30 #define LPDAEMONLOG "/tmp/lpdaemonl" 31 #endif 32 33 #define ARGSIZ 4096 34 #define NAMELEN 30 35 36 unsigned char argvstr[ARGSIZ]; /* arguments after parsing */ 37 unsigned char *argvals[ARGSIZ/2+1]; /* pointers to arguments after parsing */ 38 int ascnt = 0, argcnt = 0; /* number of arguments parsed */ 39 /* for 'stuff' gleened from lpr cntrl file */ 40 struct jobinfo { 41 char user[NAMELEN+1]; 42 char host[NAMELEN+1]; 43 } *getjobinfo(); 44 45 #define MIN(a,b) ((a<b)?a:b) 46 47 #define CPYFIELD(src, dst) { while (*(src)!=' ' && *(src)!='\t' && *(src)!='\r' && *(src)!='\n' && *(src)!='\0') *(dst)++ = *(src)++; } 48 49 #define ACK() write(1, "", 1) 50 #define NAK() write(1, "\001", 1) 51 52 #define LNBFSZ 4096 53 unsigned char lnbuf[LNBFSZ]; 54 55 #define RDSIZE 512 56 unsigned char jobbuf[RDSIZE]; 57 58 int datafd[400], cntrlfd = -1; 59 60 int dbgstate = 0; 61 char *dbgstrings[] = { 62 "", 63 "sendack1", 64 "send", 65 "rcvack", 66 "sendack2", 67 "done" 68 }; 69 70 void 71 error(char *s1, ...) 72 { 73 FILE *fp; 74 long thetime; 75 char *chartime; 76 va_list ap; 77 char *args[8]; 78 int argno = 0; 79 80 if((fp=fopen(LPDAEMONLOG, "a"))==NULL) { 81 fprintf(stderr, "cannot open %s in append mode\n", LPDAEMONLOG); 82 return; 83 } 84 time(&thetime); 85 chartime = ctime(&thetime); 86 fprintf(fp, "%.15s [%5.5d] ", &(chartime[4]), getpid()); 87 va_start(ap, s1); 88 while((args[argno++] = va_arg(ap, char*)) && argno<8); 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 fflush(fp); 92 fclose(fp); 93 return; 94 } 95 96 void 97 forklp(int inputfd) 98 { 99 int i, cpid; 100 unsigned char *bp, *cp; 101 unsigned char logent[LNBFSZ]; 102 103 /* log this call to lp */ 104 cp = logent; 105 for (i=1; i<argcnt; i++) { 106 bp = argvals[i]; 107 if (cp+strlen((const char *)bp)+1 < logent+LNBFSZ-1) { 108 CPYFIELD(bp, cp); 109 *cp++ = ' '; 110 } 111 } 112 *--cp = '\n'; 113 *++cp = '\0'; 114 error((const char *)logent); 115 switch((cpid=fork())){ 116 case -1: 117 error("fork error\n"); 118 exit(2); 119 case 0: 120 if (inputfd != 0) 121 dup2(inputfd, 0); 122 dup2(1, 2); 123 lseek(0, 0L, 0); 124 execvp(LP, (const char **)argvals); 125 error("exec failed\n"); 126 exit(3); 127 default: 128 while(wait((int *)0) != cpid); 129 } 130 } 131 132 int 133 tempfile(void) 134 { 135 static tindx = 0; 136 char tmpf[sizeof(TMPDIR)+64]; 137 int crtfd, tmpfd; 138 139 sprintf(tmpf, "%s/lp%d.%d", TMPDIR, getpid(), tindx++); 140 if((crtfd=creat(tmpf, 0666)) < 0) { 141 error("cannot create temp file %s\n", tmpf); 142 NAK(); 143 exit(3); 144 } 145 if((tmpfd=open(tmpf, 2)) < 0) { 146 error("cannot open temp file %s\n", tmpf); 147 NAK(); 148 exit(3); 149 } 150 close(crtfd); 151 unlink(tmpf); /* comment out for debugging */ 152 return(tmpfd); 153 } 154 155 int 156 readfile(int outfd, int bsize) 157 { 158 int rv; 159 160 dbgstate = 1; 161 alarm(60); 162 ACK(); 163 dbgstate = 2; 164 for(; bsize > 0; bsize -= rv) { 165 alarm(60); 166 if((rv=read(0, jobbuf, MIN(bsize,RDSIZE))) < 0) { 167 error("error reading input, %d unread\n", bsize); 168 exit(4); 169 } else if (rv == 0) { 170 error("connection closed prematurely\n"); 171 exit(4); 172 } else if((write(outfd, jobbuf, rv)) != rv) { 173 error("error writing temp file, %d unread\n", bsize); 174 exit(5); 175 } 176 } 177 dbgstate = 3; 178 alarm(60); 179 if (((rv=read(0, jobbuf, 1))==1) && (*jobbuf=='\0')) { 180 alarm(60); 181 ACK(); 182 dbgstate = 4; 183 alarm(0); 184 return(outfd); 185 } 186 alarm(0); 187 error("received bad status <%d> from sender\n", *jobbuf); 188 error("rv=%d\n", rv); 189 NAK(); 190 return(-1); 191 } 192 193 /* reads a line from the input into lnbuf 194 * if there is no error, it returns 195 * the number of characters in the buffer 196 * if there is an error and there where characters 197 * read, it returns the negative value of the 198 * number of characters read 199 * if there is an error and no characters were read, 200 * it returns the negative value of 1 greater than 201 * the size of the line buffer 202 */ 203 int 204 readline(int inpfd) 205 { 206 unsigned char *ap; 207 int i, rv; 208 209 ap = lnbuf; 210 lnbuf[0] = '\0'; 211 i = 0; 212 alarm(60); 213 do { 214 rv = read(inpfd, ap, 1); 215 } while (rv==1 && ++i && *ap != '\n' && ap++ && (i < LNBFSZ - 2)); 216 alarm(0); 217 if (i != 0 && *ap != '\n') { 218 *++ap = '\n'; 219 i++; 220 } 221 *++ap = '\0'; 222 if (rv < 0) { 223 error("read error; lost connection\n"); 224 if (i==0) i = -(LNBFSZ+1); 225 else i = -i; 226 } 227 return(i); 228 } 229 230 int 231 getfiles(void) 232 { 233 unsigned char *ap; 234 int filecnt, bsize, rv; 235 236 filecnt = 0; 237 /* get a line, hopefully containing a ctrl char, size, and name */ 238 for(;;) { 239 ap = lnbuf; 240 if ((rv=readline(0)) < 0) NAK(); 241 if (rv <= 0) { 242 return(filecnt); 243 } 244 switch(*ap++) { 245 case '\1': /* cleanup - data sent was bad (whatever that means) */ 246 break; 247 case '\2': /* read control file */ 248 bsize = atoi((const char *)ap); 249 cntrlfd = tempfile(); 250 if (readfile(cntrlfd, bsize) < 0) { 251 close(cntrlfd); 252 NAK(); 253 return(0); 254 } 255 break; 256 case '\3': /* read data file */ 257 bsize = atoi((const char *)ap); 258 datafd[filecnt] = tempfile(); 259 if (readfile(datafd[filecnt], bsize) < 0) { 260 close(datafd[filecnt]); 261 NAK(); 262 return(0); 263 } 264 filecnt++; 265 break; 266 default: 267 error("protocol error <%d>\n", *(ap-1)); 268 NAK(); 269 } 270 } 271 return(filecnt); 272 } 273 274 struct jobinfo * 275 getjobinfo(int fd) 276 { 277 unsigned char *ap; 278 int rv; 279 static struct jobinfo info; 280 281 if (fd < 0) error("getjobinfo: bad file descriptor\n"); 282 if (lseek(fd, 0L, 0) < 0) { 283 error("error seeking in temp file\n"); 284 exit(7); 285 } 286 /* the following strings should be < NAMELEN or else they will not 287 * be null terminated. 288 */ 289 strncpy(info.user, "daemon", NAMELEN); 290 strncpy(info.host, "nowhere", NAMELEN); 291 /* there may be a space after the name and host. It will be filtered out 292 * by CPYFIELD. 293 */ 294 while ((rv=readline(fd)) > 0) { 295 ap = lnbuf; 296 ap[rv-1] = '\0'; /* remove newline from string */ 297 switch (*ap) { 298 case 'H': 299 if (ap[1] == '\0') 300 strncpy(info.host, "unknown", NAMELEN); 301 else 302 strncpy(info.host, (const char *)&ap[1], NAMELEN); 303 info.host[strlen(info.host)] = '\0'; 304 break; 305 case 'P': 306 if (ap[1] == '\0') 307 strncpy(info.user, "unknown", NAMELEN); 308 else 309 strncpy(info.user, (const char *)&ap[1], NAMELEN); 310 info.user[strlen(info.user)] = '\0'; 311 break; 312 } 313 } 314 return(&info); 315 } 316 317 void 318 alarmhandler(int sig) { 319 signal(sig, alarmhandler); 320 error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]); 321 } 322 323 main() 324 { 325 unsigned char *ap, *bp, *cp, *savbufpnt; 326 int i, blen, rv, saveflg, savargcnt; 327 struct jobinfo *jinfop; 328 329 signal(1, SIG_IGN); /* SIGHUP not in lcc */ 330 signal(14, alarmhandler); /* SIGALRM not in lcc */ 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