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: }

Defined functions

banner defined in line 497; used 2 times
dcomp defined in line 217; used 2 times
dofork defined in line 466; used 4 times
doit defined in line 226; used 1 times
dqcleanup defined in line 424; used 2 times
dropit defined in line 554; used 1 times
dump defined in line 380; used 2 times
getline defined in line 436; used 2 times
init defined in line 624; used 1 times
  • in line 89
log defined in line 611; used 10 times
main defined in line 64; never used
pr defined in line 401; used 1 times
scan_out defined in line 521; used 3 times
scnline defined in line 505; used 1 times
sendmail defined in line 578; used 1 times

Defined variables

AF defined in line 40; used 1 times
DU defined in line 47; used 3 times
FF defined in line 43; used 8 times
LF defined in line 41; used 4 times
LO defined in line 38; used 9 times
LP defined in line 37; used 4 times
OF defined in line 42; used 6 times
PL defined in line 44; used 3 times
SD defined in line 39; used 3 times
SF defined in line 45; used 2 times
SH defined in line 46; used 2 times
child defined in line 33; used 6 times
class defined in line 54; used 5 times
df defined in line 30; used 4 times
filter defined in line 34; used 7 times
fo defined in line 28; used 27 times
haveslept defined in line 49; used 3 times
jobname defined in line 55; used 7 times
lfd defined in line 31; used 4 times
line defined in line 25; used 13 times
logname defined in line 53; used 3 times
lp defined in line 29; used 7 times
name defined in line 57; used 3 times
pid defined in line 32; used 16 times
printer defined in line 58; used 4 times
title defined in line 26; used 5 times
tof defined in line 35; used 5 times

Defined macros

DOABORT defined in line 22; never used
DORETURN defined in line 21; used 2 times
DOUNLOCK defined in line 23; used 1 times
SLEEPER defined in line 19; used 3 times
TR defined in line 519; used 9 times
Last modified: 1983-12-10
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1427
Valid CSS Valid XHTML 1.0 Strict