1: /* lpd.c 4.9 81/09/04 */ 2: 3: /* 4: * lpd -- line-printer daemon 5: * 6: * Recoded into c from assembly. Part of the code is stolen 7: * from the data-phone daemon. 8: */ 9: 10: #include <signal.h> 11: 12: #include <stdio.h> 13: #include <sys/types.h> 14: #include <sys/dir.h> 15: #include <sys/stat.h> 16: #include <whoami.h> 17: #include "lp.local.h" 18: 19: #define SLEEPER 20: 21: #define DORETURN 0 /* absorb fork error */ 22: #define DOABORT 1 /* abort lpd if dofork fails */ 23: #define DOUNLOCK 2 /* remove lock file before aborting */ 24: 25: char line[132]; /* line from daemon file */ 26: char title[80]; /* ``pr'' title */ 27: FILE *dfd; /* daemon file */ 28: int fo; /* output file */ 29: int lp; /* line printer file descriptor */ 30: int df; /* lpd directory */ 31: int lfd; /* lock file */ 32: int pid; /* id of process */ 33: int child; /* id of any children */ 34: static int filter = 0; /* id of output filter, if any */ 35: int tof = TOF; /* flag - at top of form */ 36: 37: char *LP; /* line printer name */ 38: char *LO; /* lock file name */ 39: char *SD; /* spooling directory */ 40: char *AF; /* accounting file */ 41: char *LF; /* log file for error messages */ 42: char *OF; /* name of ouput filter */ 43: char *FF; /* form feed string */ 44: int PL; /* page length */ 45: short SF; /* suppress FF on each print job */ 46: short SH; /* suppress header page */ 47: int DU; /* daemon uid */ 48: #ifdef SLEEPER 49: static int haveslept = 0; /* sleep just in case... */ 50: #endif SLEEPER 51: 52: extern banner(); /* big character printer */ 53: char *logname; /* points to user login name */ 54: char class[20]; /* classification field */ 55: char jobname[20]; /* job or file name */ 56: 57: char *name; /* name of program */ 58: char *printer; /* name of printer */ 59: 60: char *rindex(); 61: char *pgetstr(); 62: 63: /*ARGSUSED*/ 64: main(argc, argv) 65: char *argv[]; 66: { 67: struct stat dfb; 68: register struct direct *pdir; 69: register int nitems; 70: struct direct *p; 71: int i; 72: int dcomp(); 73: int dqcleanup(); 74: extern char *malloc(), *realloc(); 75: 76: /* 77: * set-up controlled environment; trap bad signals 78: */ 79: signal(SIGHUP, SIG_IGN); 80: signal(SIGINT, SIG_IGN); 81: signal(SIGQUIT, SIG_IGN); 82: signal(SIGTERM, dqcleanup); /* for use with lprm */ 83: 84: name = argv[0]; 85: if(argc>1) 86: printer = argv[1]; 87: else 88: printer = "lp"; 89: init(); /* set up capabilities */ 90: for (i = 0; i < NOFILE; i++) 91: close(i); 92: open("/dev/null", 0); /* standard input */ 93: open(LF, 1); /* standard output */ 94: lseek(1, 0L, 2); /* append if into a file */ 95: dup2(1, 2); /* standard error */ 96: 97: /* 98: * opr uses short form file names 99: */ 100: if(chdir(SD) < 0) { 101: log("can't change directory"); 102: exit(1); 103: } 104: if(stat(LO, &dfb) >= 0 || (lfd=creat(LO, 0444)) < 0) 105: exit(0); 106: /* 107: * kill the parent so the user's shell can function 108: */ 109: if (dofork(DOUNLOCK)) 110: exit(0); 111: /* 112: * write process id for others to know 113: */ 114: pid = getpid(); 115: if (write(lfd, (char *)&pid, sizeof(pid)) != sizeof(pid)) 116: log("can't write daemon pid"); 117: /* 118: * acquire lineprinter 119: */ 120: for (i = 0; (lp = open(LP, 1)) < 0; i++) { 121: /* this means we try for 10 minutes */ 122: if (i > 20) { 123: log("%s open failure", LP); 124: unlink(LO); 125: exit(1); 126: } 127: sleep(30); 128: } 129: 130: /* 131: * search the directory for work (file with name df which is 132: * short for daemon file) 133: */ 134: again: 135: if ((df = open(".", 0)) < 0) { 136: extern int errno; 137: 138: log("can't open \".\" (%d)", errno); 139: unlink(LO); 140: exit(2); 141: } 142: 143: /* 144: * Find all the spool files in the spooling directory 145: */ 146: lseek(df, (long)(2*sizeof(struct direct)), 0); /* skip . & .. */ 147: pdir = (struct direct *)malloc(sizeof(struct direct)); 148: nitems = 0; 149: while (1) { 150: register struct direct *proto; 151: 152: proto = &pdir[nitems]; 153: if (read(df, (char *)proto, sizeof(*proto)) != sizeof(*proto)) 154: break; 155: if (proto->d_ino == 0 || proto->d_name[0] != 'd' || 156: proto->d_name[1] != 'f') 157: continue; /* just daemon files */ 158: nitems++; 159: proto = (struct direct *)realloc((char *)pdir, 160: (unsigned)(nitems+1)*sizeof(struct direct)); 161: if (proto == NULL) 162: break; 163: pdir = proto; 164: } 165: if (nitems == 0) { /* EOF => no work to do */ 166: #ifndef SLEEPER 167: unlink(LO); 168: exit(0); 169: } 170: #else SLEEPER 171: if ( haveslept ) { 172: unlink(LO); 173: close(fo); 174: while (wait(0) > 0) 175: ; 176: exit(0); 177: } else { 178: sleep(NAPTIME); 179: haveslept = 1; 180: } 181: } else 182: haveslept = 0; 183: #endif SLEEPER 184: close(df); 185: 186: /* 187: * Start up an output filter, if needed. 188: */ 189: qsort(pdir, nitems, sizeof(struct direct), dcomp); 190: /* 191: * we found something to do now do it -- 192: * write the pid of the current daemon file into the lock file 193: * so the spool queue program can tell what we're working on 194: */ 195: for (p = pdir; nitems > 0; p++, nitems--) { 196: extern int errno; 197: 198: if (stat(p->d_name, &dfb) == -1) 199: continue; 200: lseek(lfd, (long)sizeof(int), 0); 201: pid = atoi(p->d_name+3); /* pid of current daemon file */ 202: if (write(lfd, (char *)&pid, sizeof(pid)) != sizeof(pid)) 203: log("can't write (%d) daemon file pid", errno); 204: doit(p->d_name); 205: } 206: free((char *)pdir); 207: close(fo); 208: if (filter) 209: while ((pid = wait(0)) > 0 && pid != filter) 210: ; 211: goto again; 212: } 213: 214: /* 215: * Compare routine for qsort'n the directory 216: */ 217: dcomp(d1, d2) 218: register struct direct *d1, *d2; 219: { 220: return(strncmp(d1->d_name, d2->d_name, DIRSIZ)); 221: } 222: 223: /* 224: * The remaining part is the reading of the daemon control file (df) 225: */ 226: doit(file) 227: char *file; 228: { 229: time_t tvec; 230: extern char *ctime(); 231: 232: /* 233: * open daemon file 234: */ 235: if ((dfd = fopen(file, "r")) == NULL) { 236: extern int errno; 237: 238: log("daemon file (%s) open failure <errno = %d>", file, errno); 239: return; 240: } 241: 242: /* 243: * read the daemon file for work to do 244: * 245: * file format -- first character in the line is a command 246: * rest of the line is the argument. 247: * valid commands are: 248: * 249: * L -- "literal" contains identification info from 250: * password file. 251: * I -- "indent"changes default indents driver 252: * must have stty/gtty avaialble 253: * F -- "formatted file" name of file to print 254: * U -- "unlink" name of file to remove (after 255: * we print it. (Pass 2 only). 256: * R -- "pr'ed file" print file with pr 257: * H -- "header(title)" for pr 258: * M -- "mail" to user when done printing 259: * 260: * getline read line and expands tabs to blanks 261: */ 262: 263: /* pass 1 */ 264: while (getline()) switch (line[0]) { 265: 266: case 'J': 267: if(line[1] != '\0' ) 268: strcpy(jobname, line+1); 269: else 270: strcpy(jobname, " "); 271: continue; 272: case 'C': 273: if(line[1] != '\0' ) 274: strcpy(class, line+1); 275: else 276: gethostname(class, sizeof (class)); 277: continue; 278: 279: case 'H': /* header title for pr */ 280: strcpy(title, line+1); 281: continue; 282: 283: case 'L': /* identification line */ 284: logname = line+1; 285: if (OF) { 286: int p[2], i; 287: char *cp; 288: 289: if (filter) { /* wait for last one to complete */ 290: close(fo); 291: while (wait(0) > 0) 292: ; 293: } 294: 295: pipe(p); 296: if ((filter = dofork()) == 0) { /* child */ 297: dup2(p[0], 0); /* pipe is std in */ 298: dup2(lp, 1); /* printer is std out */ 299: for (i = 3; i < NOFILE; i++) 300: close(i); 301: if ((cp = rindex(OF, '/')) == NULL) 302: cp = OF; 303: else 304: cp++; 305: execl(OF, cp, logname, 0); 306: log("can't execl output filter %s", OF); 307: exit(1); 308: } 309: fo = p[1]; /* use pipe for output */ 310: close(p[0]); /* close input side */ 311: } else { 312: fo = dup(lp); /* use printer for output */ 313: filter = 0; 314: } 315: if (SH) 316: continue; 317: time(&tvec); 318: if (!tof) 319: write(fo, FF, strlen(FF)); 320: write(fo, "\n\n\n", 3); 321: banner(logname, jobname); 322: if (strlen(class) > 0) { 323: write(fo,"\n\n\n",3); 324: scan_out(fo, class, '\0'); 325: } 326: write(fo, "\n\n\n\n\t\t\t\t\t Job: ", 20); 327: write(fo, jobname, strlen(jobname)); 328: write(fo, "\n\t\t\t\t\t Date: ", 17); 329: write(fo, ctime(&tvec), 24); 330: write(fo, "\n", 1); 331: write(fo, FF, strlen(FF)); 332: tof = 1; 333: continue; 334: 335: case 'F': /* print formatted file */ 336: dump(0); 337: title[0] = '\0'; 338: continue; 339: 340: case 'R': /* print file using 'pr' */ 341: dump(1); 342: title[0] = '\0'; /* get rid of title */ 343: continue; 344: 345: case 'N': /* file name for lpq */ 346: case 'U': /* unlink deferred to pass2 */ 347: continue; 348: 349: } 350: /* 351: * Second pass. 352: * Unlink files 353: */ 354: fseek(dfd, 0L, 0); 355: while (getline()) switch (line[0]) { 356: 357: default: 358: continue; 359: 360: case 'M': 361: sendmail(); 362: continue; 363: 364: case 'U': 365: unlink(&line[1]); 366: continue; 367: 368: } 369: /* 370: * clean-up incase another daemon file exists 371: */ 372: fclose(dfd); 373: unlink(file); 374: } 375: 376: /* 377: * print a file. 378: * name of file is in line starting in col 2 379: */ 380: dump(prflag) 381: { 382: register n, f; 383: char buf[BUFSIZ]; 384: 385: f = open(&line[1], 0); 386: if (!SF && !tof) 387: write(fo, FF, strlen(FF)); /* start on a fresh page */ 388: if (prflag && pr(f, fo) >= 0) 389: tof = 1; 390: else { 391: while ((n=read(f, buf, BUFSIZ))>0) 392: write(fo, buf, n); 393: tof = 0; 394: } 395: close(f); 396: } 397: 398: /* 399: * pr - print a file using 'pr' 400: */ 401: pr(fi, fo) 402: { 403: int pid, stat; 404: char tmp[20]; 405: 406: if ((child = dofork(DORETURN)) == 0) { /* child - pr */ 407: dup2(fi, 0); 408: dup2(fo, 1); 409: for (fo = 3; fo < NOFILE; fo++) 410: close(fo); 411: sprintf(tmp, "-l%d", PL); 412: execl(PRLOC, "pr", tmp, "-h", *title ? title : " ", 0); 413: log("can't execl %s", PRLOC); 414: exit(1); 415: } else if (child < 0) /* forget about pr'ing */ 416: return(-1); 417: /* parent, wait */ 418: while ((pid = wait(&stat)) > 0 && pid != child) 419: ; 420: child = 0; 421: return(0); 422: } 423: 424: dqcleanup() 425: { 426: signal(SIGTERM, SIG_IGN); 427: if (child > 0) 428: kill(child, SIGKILL); /* get rid of pr's */ 429: if (filter > 0) 430: kill(filter, SIGTERM); /* get rid of output filter */ 431: while (wait(0) > 0) 432: ; 433: exit(0); /* lprm removes the lock file */ 434: } 435: 436: getline() 437: { 438: register int linel = 0; 439: register char *lp = line; 440: register c; 441: 442: /* 443: * reads a line from the daemon file, removes tabs, converts 444: * new-line to null and leaves it in line. returns 0 at EOF 445: */ 446: while ((c = getc(dfd)) != '\n') { 447: if (c == EOF) 448: return(0); 449: if (c=='\t') { 450: do { 451: *lp++ = ' '; 452: linel++; 453: } while ((linel & 07) != 0); 454: continue; 455: } 456: *lp++ = c; 457: linel++; 458: } 459: *lp++ = 0; 460: return(1); 461: } 462: 463: /* 464: * dofork - fork with retries on failure 465: */ 466: dofork(action) 467: { 468: register int i, pid; 469: 470: for (i = 0; i < 20; i++) { 471: if ((pid = fork()) < 0) 472: sleep((unsigned)(i*i)); 473: else 474: return(pid); 475: } 476: log("can't fork"); 477: 478: switch(action) { 479: case DORETURN: 480: return(-1); 481: default: 482: log("bad action (%d) to dofork", action); 483: /*FALL THRU*/ 484: case DOUNLOCK: 485: unlink(LO); 486: /*FALL THRU*/ 487: case DOABORT: 488: exit(1); 489: } 490: /*NOTREACHED*/ 491: } 492: 493: /* 494: * Banner printing stuff 495: */ 496: 497: banner (name1, name2) 498: char *name1, *name2; 499: { 500: scan_out(fo, name1, '\0'); 501: write(fo, "\n\n", 2); 502: scan_out(fo, name2, '\0'); 503: } 504: 505: char * 506: scnline(key, p, c) 507: register char key, *p; 508: char c; 509: { 510: register scnwidth; 511: 512: for(scnwidth = WIDTH; --scnwidth;) { 513: key <<= 1; 514: *p++ = key & 0200 ? c : BACKGND; 515: } 516: return(p); 517: } 518: 519: #define TR(q) (((q)-' ')&0177) 520: 521: scan_out(scfd, scsp, dlm) 522: char *scsp, dlm; 523: int scfd; 524: { 525: register char *strp; 526: register nchrs, j; 527: char outbuf[LINELEN+1], *sp, c, cc; 528: int d, scnhgt; 529: extern char scnkey[][HEIGHT]; /* in lpdchar.c */ 530: 531: for(scnhgt = 0; scnhgt++ < HEIGHT+DROP;) { 532: strp = &outbuf[0]; 533: sp = scsp; 534: for(nchrs = 0;;) { 535: d = dropit(c = TR(cc = *sp++)); 536: if ((!d && scnhgt>HEIGHT ) || (scnhgt<=DROP && d)) 537: for(j=WIDTH; --j;) 538: *strp++ = BACKGND; 539: else 540: strp = scnline(scnkey[c][scnhgt-1-d], strp, cc); 541: if(*sp==dlm || *sp=='\0' || nchrs++>=LINELEN/(WIDTH+1)-1) 542: break; 543: *strp++ = BACKGND; 544: *strp++ = BACKGND; 545: } 546: while(*--strp== BACKGND && strp >= outbuf) 547: ; 548: strp++; 549: *strp++ = '\n'; 550: write(scfd, outbuf, strp-outbuf); 551: } 552: } 553: 554: dropit(c) 555: char c; 556: { 557: switch(c) { 558: 559: case TR('_'): 560: case TR(';'): 561: case TR(','): 562: case TR('g'): 563: case TR('j'): 564: case TR('p'): 565: case TR('q'): 566: case TR('y'): 567: return(DROP); 568: 569: default: 570: return(0); 571: } 572: } 573: 574: /* 575: * sendmail --- 576: * tell people about job completion 577: */ 578: sendmail() 579: { 580: static int p[2]; 581: register int i; 582: int stat; 583: 584: pipe(p); 585: if ((stat = dofork(DORETURN)) == 0) { 586: close(0); 587: dup(p[0]); 588: for (i=3; i <= NOFILE; i++) 589: close(i); 590: execl(MAIL, "mail", &line[1], 0); 591: exit(0); 592: } else if (stat > 0) { 593: close(1); 594: dup(p[1]); 595: printf("To: %s\n", &line[1]); 596: printf("Subject: printer job\n\n"); 597: if (*jobname) 598: printf("Your printer job (%s) is done\n", jobname); 599: else 600: printf("Your printer job is done\n"); 601: fflush(stdout); 602: close(1); 603: } 604: close(p[0]); 605: close(p[1]); 606: open(LF, 1); 607: wait(&stat); 608: } 609: 610: /*VARARGS1*/ 611: log(message, a1, a2, a3) 612: char *message; 613: { 614: short console = isatty(fileno(stderr)); 615: 616: fprintf(stderr, console ? "\r\n%s: " : "%s: ", name); 617: fprintf(stderr, message, a1, a2, a3); 618: if (console) 619: putc('\r', stderr); 620: putc('\n', stderr); 621: fflush(stderr); 622: } 623: 624: init() 625: { 626: char b[BUFSIZ]; 627: static char buf[BUFSIZ/2]; 628: static char *bp = buf; 629: int status; 630: 631: if ((status = pgetent(b, printer)) < 0) { 632: printf("%s: can't open printer description file\n", name); 633: exit(3); 634: } else if (status == 0) { 635: printf("%s: unknown printer\n", printer); 636: exit(4); 637: } 638: if ((LP = pgetstr("lp", &bp)) == NULL) 639: LP = DEFDEVLP; 640: if ((LO = pgetstr("lo", &bp)) == NULL) 641: LO = DEFLOCK; 642: if ((LF = pgetstr("lf", &bp)) == NULL) 643: LF = DEFLOGF; 644: AF = pgetstr("af", &bp); 645: if ((PL = pgetnum("pl", &bp)) == NULL) 646: PL = DEFPAGESIZE; 647: if ((SD = pgetstr("sd", &bp)) == NULL) 648: SD = DEFSPOOL; 649: if ((FF = pgetstr("ff", &bp)) == NULL) 650: FF = DEFFF; 651: OF = pgetstr("of", &bp); 652: SF = pgetflag("sf"); 653: SH = pgetflag("sh"); 654: if ((DU = pgetnum("du")) < 0) 655: DU = DEFUID; 656: setuid(DU); 657: }