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