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