1: /* 2: * Copyright (c) 1983 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #if defined(DOSCCS) && !defined(lint) 8: char copyright[] = 9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: 12: static char sccsid[] = "@(#)lpr.c 5.2.1 (2.11BSD GTE) 12/31/93"; 13: #endif 14: 15: /* 16: * lpr -- off line print 17: * 18: * Allows multiple printers and printers on remote machines by 19: * using information from a printer data base. 20: */ 21: 22: #include <stdio.h> 23: #include <sys/types.h> 24: #include <sys/file.h> 25: #include <sys/stat.h> 26: #include <pwd.h> 27: #include <grp.h> 28: #include <signal.h> 29: #include <ctype.h> 30: #include <syslog.h> 31: #include "lp.local.h" 32: 33: char *tfname; /* tmp copy of cf before linking */ 34: char *cfname; /* daemon control files, linked from tf's */ 35: char *dfname; /* data files */ 36: 37: int nact; /* number of jobs to act on */ 38: int tfd; /* control file descriptor */ 39: int mailflg; /* send mail */ 40: int qflag; /* q job, but don't exec daemon */ 41: char format = 'f'; /* format char for printing files */ 42: int rflag; /* remove files upon completion */ 43: int sflag; /* symbolic link flag */ 44: int inchar; /* location to increment char in file names */ 45: int ncopies = 1; /* # of copies to make */ 46: int iflag; /* indentation wanted */ 47: int indent; /* amount to indent */ 48: int hdr = 1; /* print header or not (default is yes) */ 49: int userid; /* user id */ 50: char *person; /* user name */ 51: char *title; /* pr'ing title */ 52: char *fonts[4]; /* troff font names */ 53: char *width; /* width for versatec printing */ 54: char host[32]; /* host name */ 55: char *class = host; /* class title on header page */ 56: char *jobname; /* job name on header page */ 57: char *name; /* program name */ 58: char *printer; /* printer name */ 59: struct stat statb; 60: 61: int MX; /* maximum number of blocks to copy */ 62: int MC; /* maximum number of copies allowed */ 63: int DU; /* daemon user-id */ 64: char *SD; /* spool directory */ 65: char *LO; /* lock file name */ 66: char *RG; /* restrict group */ 67: short SC; /* suppress multiple copies */ 68: 69: char *getenv(); 70: char *rindex(); 71: char *linked(); 72: int cleanup(); 73: 74: /*ARGSUSED*/ 75: main(argc, argv) 76: int argc; 77: char *argv[]; 78: { 79: extern struct passwd *getpwuid(); 80: struct passwd *pw; 81: struct group *gptr; 82: extern char *itoa(); 83: register char *arg, *cp; 84: char buf[BUFSIZ]; 85: int i, f; 86: struct stat stb; 87: 88: if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 89: signal(SIGHUP, cleanup); 90: if (signal(SIGINT, SIG_IGN) != SIG_IGN) 91: signal(SIGINT, cleanup); 92: if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 93: signal(SIGQUIT, cleanup); 94: if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 95: signal(SIGTERM, cleanup); 96: 97: name = argv[0]; 98: gethostname(host, sizeof (host)); 99: openlog("lpd", 0, LOG_LPR); 100: 101: while (argc > 1 && argv[1][0] == '-') { 102: argc--; 103: arg = *++argv; 104: switch (arg[1]) { 105: 106: case 'P': /* specifiy printer name */ 107: if (arg[2]) 108: printer = &arg[2]; 109: else if (argc > 1) { 110: argc--; 111: printer = *++argv; 112: } 113: break; 114: 115: case 'C': /* classification spec */ 116: hdr++; 117: if (arg[2]) 118: class = &arg[2]; 119: else if (argc > 1) { 120: argc--; 121: class = *++argv; 122: } 123: break; 124: 125: case 'J': /* job name */ 126: hdr++; 127: if (arg[2]) 128: jobname = &arg[2]; 129: else if (argc > 1) { 130: argc--; 131: jobname = *++argv; 132: } 133: break; 134: 135: case 'T': /* pr's title line */ 136: if (arg[2]) 137: title = &arg[2]; 138: else if (argc > 1) { 139: argc--; 140: title = *++argv; 141: } 142: break; 143: 144: case 'l': /* literal output */ 145: case 'p': /* print using ``pr'' */ 146: case 't': /* print troff output (cat files) */ 147: case 'n': /* print ditroff output */ 148: case 'd': /* print tex output (dvi files) */ 149: case 'g': /* print graph(1G) output */ 150: case 'c': /* print cifplot output */ 151: case 'v': /* print vplot output */ 152: format = arg[1]; 153: break; 154: 155: case 'f': /* print fortran output */ 156: format = 'r'; 157: break; 158: 159: case '4': /* troff fonts */ 160: case '3': 161: case '2': 162: case '1': 163: if (argc > 1) { 164: argc--; 165: fonts[arg[1] - '1'] = *++argv; 166: } 167: break; 168: 169: case 'w': /* versatec page width */ 170: width = arg+2; 171: break; 172: 173: case 'r': /* remove file when done */ 174: rflag++; 175: break; 176: 177: case 'm': /* send mail when done */ 178: mailflg++; 179: break; 180: 181: case 'h': /* toggle want of header page */ 182: hdr = !hdr; 183: break; 184: 185: case 's': /* try to link files */ 186: sflag++; 187: break; 188: 189: case 'q': /* just q job */ 190: qflag++; 191: break; 192: 193: case 'i': /* indent output */ 194: iflag++; 195: indent = arg[2] ? atoi(&arg[2]) : 8; 196: break; 197: 198: case '#': /* n copies */ 199: if (isdigit(arg[2])) { 200: i = atoi(&arg[2]); 201: if (i > 0) 202: ncopies = i; 203: } 204: } 205: } 206: if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 207: printer = DEFLP; 208: chkprinter(printer); 209: if (SC && ncopies > 1) 210: fatal("multiple copies are not allowed"); 211: if (MC > 0 && ncopies > MC) 212: fatal("only %d copies are allowed", MC); 213: /* 214: * Get the identity of the person doing the lpr using the same 215: * algorithm as lprm. 216: */ 217: userid = getuid(); 218: if ((pw = getpwuid(userid)) == NULL) 219: fatal("Who are you?"); 220: person = pw->pw_name; 221: /* 222: * Check for restricted group access. 223: */ 224: if (RG != NULL) { 225: if ((gptr = getgrnam(RG)) == NULL) 226: fatal("Restricted group specified incorrectly"); 227: if (gptr->gr_gid != getgid()) { 228: while (*gptr->gr_mem != NULL) { 229: if ((strcmp(person, *gptr->gr_mem)) == 0) 230: break; 231: gptr->gr_mem++; 232: } 233: if (*gptr->gr_mem == NULL) 234: fatal("Not a member of the restricted group"); 235: } 236: } 237: /* 238: * Check to make sure queuing is enabled if userid is not root. 239: */ 240: (void) sprintf(buf, "%s/%s", SD, LO); 241: if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010)) 242: fatal("Printer queue is disabled"); 243: /* 244: * Initialize the control file. 245: */ 246: mktemps(); 247: tfd = nfile(tfname); 248: (void) fchown(tfd, DU, -1); /* owned by daemon for protection */ 249: card('H', host); 250: card('P', person); 251: if (hdr) { 252: if (jobname == NULL) { 253: if (argc == 1) 254: jobname = "stdin"; 255: else 256: jobname = (arg = rindex(argv[1], '/')) ? arg+1 : argv[1]; 257: } 258: card('J', jobname); 259: card('C', class); 260: card('L', person); 261: } 262: if (iflag) 263: card('I', itoa(indent)); 264: if (mailflg) 265: card('M', person); 266: if (format == 't' || format == 'n' || format == 'd') 267: for (i = 0; i < 4; i++) 268: if (fonts[i] != NULL) 269: card('1'+i, fonts[i]); 270: if (width != NULL) 271: card('W', width); 272: 273: /* 274: * Read the files and spool them. 275: */ 276: if (argc == 1) 277: copy(0, " "); 278: else while (--argc) { 279: if ((f = test(arg = *++argv)) < 0) 280: continue; /* file unreasonable */ 281: 282: if (sflag && (cp = linked(arg)) != NULL) { 283: (void) sprintf(buf, "%d %d", statb.st_dev, statb.st_ino); 284: card('S', buf); 285: if (format == 'p') 286: card('T', title ? title : arg); 287: for (i = 0; i < ncopies; i++) 288: card(format, &dfname[inchar-2]); 289: card('U', &dfname[inchar-2]); 290: if (f) 291: card('U', cp); 292: card('N', arg); 293: dfname[inchar]++; 294: nact++; 295: continue; 296: } 297: if (sflag) 298: printf("%s: %s: not linked, copying instead\n", name, arg); 299: if ((i = open(arg, O_RDONLY)) < 0) { 300: printf("%s: cannot open %s\n", name, arg); 301: continue; 302: } 303: copy(i, arg); 304: (void) close(i); 305: if (f && unlink(arg) < 0) 306: printf("%s: %s: not removed\n", name, arg); 307: } 308: 309: if (nact) { 310: (void) close(tfd); 311: tfname[inchar]--; 312: /* 313: * Touch the control file to fix position in the queue. 314: */ 315: if ((tfd = open(tfname, O_RDWR)) >= 0) { 316: char c; 317: 318: if (read(tfd, &c, 1) == 1 && lseek(tfd, 0L, 0) == 0 && 319: write(tfd, &c, 1) != 1) { 320: printf("%s: cannot touch %s\n", name, tfname); 321: tfname[inchar]++; 322: cleanup(); 323: } 324: (void) close(tfd); 325: } 326: if (link(tfname, cfname) < 0) { 327: printf("%s: cannot rename %s\n", name, cfname); 328: tfname[inchar]++; 329: cleanup(); 330: } 331: unlink(tfname); 332: if (qflag) /* just q things up */ 333: exit(0); 334: if (!startdaemon(printer)) 335: printf("jobs queued, but cannot start daemon.\n"); 336: exit(0); 337: } 338: cleanup(); 339: /* NOTREACHED */ 340: } 341: 342: /* 343: * Create the file n and copy from file descriptor f. 344: */ 345: copy(f, n) 346: int f; 347: char n[]; 348: { 349: register int fd, i, nr, nc; 350: char buf[BUFSIZ]; 351: 352: if (format == 'p') 353: card('T', title ? title : n); 354: for (i = 0; i < ncopies; i++) 355: card(format, &dfname[inchar-2]); 356: card('U', &dfname[inchar-2]); 357: card('N', n); 358: fd = nfile(dfname); 359: nr = nc = 0; 360: while ((i = read(f, buf, BUFSIZ)) > 0) { 361: if (write(fd, buf, i) != i) { 362: printf("%s: %s: temp file write error\n", name, n); 363: break; 364: } 365: nc += i; 366: if (nc >= BUFSIZ) { 367: nc -= BUFSIZ; 368: nr++; 369: if (MX > 0 && nr > MX) { 370: printf("%s: %s: copy file is too large\n", name, n); 371: break; 372: } 373: } 374: } 375: (void) close(fd); 376: if (nc==0 && nr==0) 377: printf("%s: %s: empty input file\n", name, f ? n : "stdin"); 378: else 379: nact++; 380: } 381: 382: /* 383: * Try and link the file to dfname. Return a pointer to the full 384: * path name if successful. 385: */ 386: char * 387: linked(file) 388: register char *file; 389: { 390: register char *cp; 391: static char buf[BUFSIZ]; 392: 393: if (*file != '/') { 394: if (getwd(buf) == NULL) 395: return(NULL); 396: while (file[0] == '.') { 397: switch (file[1]) { 398: case '/': 399: file += 2; 400: continue; 401: case '.': 402: if (file[2] == '/') { 403: if ((cp = rindex(buf, '/')) != NULL) 404: *cp = '\0'; 405: file += 3; 406: continue; 407: } 408: } 409: break; 410: } 411: strcat(buf, "/"); 412: strcat(buf, file); 413: file = buf; 414: } 415: return(symlink(file, dfname) ? NULL : file); 416: } 417: 418: /* 419: * Put a line into the control file. 420: */ 421: card(c, p2) 422: register char c, *p2; 423: { 424: char buf[BUFSIZ]; 425: register char *p1 = buf; 426: register int len = 2; 427: 428: *p1++ = c; 429: while ((c = *p2++) != '\0') { 430: *p1++ = c; 431: len++; 432: } 433: *p1++ = '\n'; 434: write(tfd, buf, len); 435: } 436: 437: /* 438: * Create a new file in the spool directory. 439: */ 440: nfile(n) 441: char *n; 442: { 443: register f; 444: int oldumask = umask(0); /* should block signals */ 445: 446: f = creat(n, FILMOD); 447: (void) umask(oldumask); 448: if (f < 0) { 449: printf("%s: cannot create %s\n", name, n); 450: cleanup(); 451: } 452: if (fchown(f, userid, -1) < 0) { 453: printf("%s: cannot chown %s\n", name, n); 454: cleanup(); 455: } 456: if (++n[inchar] > 'z') { 457: if (++n[inchar-2] == 't') { 458: printf("too many files - break up the job\n"); 459: cleanup(); 460: } 461: n[inchar] = 'A'; 462: } else if (n[inchar] == '[') 463: n[inchar] = 'a'; 464: return(f); 465: } 466: 467: /* 468: * Cleanup after interrupts and errors. 469: */ 470: cleanup() 471: { 472: register i; 473: 474: signal(SIGHUP, SIG_IGN); 475: signal(SIGINT, SIG_IGN); 476: signal(SIGQUIT, SIG_IGN); 477: signal(SIGTERM, SIG_IGN); 478: i = inchar; 479: if (tfname) 480: do 481: unlink(tfname); 482: while (tfname[i]-- != 'A'); 483: if (cfname) 484: do 485: unlink(cfname); 486: while (cfname[i]-- != 'A'); 487: if (dfname) 488: do { 489: do 490: unlink(dfname); 491: while (dfname[i]-- != 'A'); 492: dfname[i] = 'z'; 493: } while (dfname[i-2]-- != 'd'); 494: exit(1); 495: } 496: 497: /* 498: * Test to see if this is a printable file. 499: * Return -1 if it is not, 0 if its printable, and 1 if 500: * we should remove it after printing. 501: */ 502: test(file) 503: char *file; 504: { 505: struct exec execb; 506: register int fd; 507: register char *cp; 508: 509: if (access(file, 4) < 0) { 510: printf("%s: cannot access %s\n", name, file); 511: return(-1); 512: } 513: if (stat(file, &statb) < 0) { 514: printf("%s: cannot stat %s\n", name, file); 515: return(-1); 516: } 517: if ((statb.st_mode & S_IFMT) == S_IFDIR) { 518: printf("%s: %s is a directory\n", name, file); 519: return(-1); 520: } 521: if (statb.st_size == 0) { 522: printf("%s: %s is an empty file\n", name, file); 523: return(-1); 524: } 525: if ((fd = open(file, O_RDONLY)) < 0) { 526: printf("%s: cannot open %s\n", name, file); 527: return(-1); 528: } 529: if (read(fd, &execb, sizeof(execb)) == sizeof(execb)) 530: switch(execb.a_magic) { 531: case A_MAGIC1: 532: case A_MAGIC2: 533: case A_MAGIC3: 534: #ifdef A_MAGIC4 535: case A_MAGIC4: 536: #endif 537: printf("%s: %s is an executable program", name, file); 538: goto error1; 539: 540: case ARMAG: 541: printf("%s: %s is an archive file", name, file); 542: goto error1; 543: } 544: (void) close(fd); 545: if (rflag) { 546: if ((cp = rindex(file, '/')) == NULL) { 547: if (access(".", 2) == 0) 548: return(1); 549: } else { 550: *cp = '\0'; 551: fd = access(file, 2); 552: *cp = '/'; 553: if (fd == 0) 554: return(1); 555: } 556: printf("%s: %s: is not removable by you\n", name, file); 557: } 558: return(0); 559: 560: error1: 561: printf(" and is unprintable\n"); 562: (void) close(fd); 563: return(-1); 564: } 565: 566: /* 567: * itoa - integer to string conversion 568: */ 569: char * 570: itoa(i) 571: register int i; 572: { 573: static char b[10] = "########"; 574: register char *p; 575: 576: p = &b[8]; 577: do 578: *p-- = i%10 + '0'; 579: while (i /= 10); 580: return(++p); 581: } 582: 583: /* 584: * Perform lookup for printer name or abbreviation -- 585: */ 586: chkprinter(s) 587: char *s; 588: { 589: int status; 590: char buf[BUFSIZ]; 591: static char pbuf[BUFSIZ/2]; 592: char *bp = pbuf; 593: extern char *pgetstr(); 594: 595: if ((status = pgetent(buf, s)) < 0) 596: fatal("cannot open printer description file"); 597: else if (status == 0) 598: fatal("%s: unknown printer", s); 599: if ((SD = pgetstr("sd", &bp)) == NULL) 600: SD = DEFSPOOL; 601: if ((LO = pgetstr("lo", &bp)) == NULL) 602: LO = DEFLOCK; 603: RG = pgetstr("rg", &bp); 604: if ((MX = pgetnum("mx")) < 0) 605: MX = DEFMX; 606: if ((MC = pgetnum("mc")) < 0) 607: MC = DEFMAXCOPIES; 608: if ((DU = pgetnum("du")) < 0) 609: DU = DEFUID; 610: SC = pgetflag("sc"); 611: } 612: 613: /* 614: * Make the temp files. 615: */ 616: mktemps() 617: { 618: register int c, len, fd, n; 619: register char *cp; 620: char buf[BUFSIZ]; 621: char *mktemp(); 622: 623: (void) sprintf(buf, "%s/.seq", SD); 624: if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) { 625: printf("%s: cannot create %s\n", name, buf); 626: exit(1); 627: } 628: if (flock(fd, LOCK_EX)) { 629: printf("%s: cannot lock %s\n", name, buf); 630: exit(1); 631: } 632: n = 0; 633: if ((len = read(fd, buf, sizeof(buf))) > 0) { 634: for (cp = buf; len--; ) { 635: if (*cp < '0' || *cp > '9') 636: break; 637: n = n * 10 + (*cp++ - '0'); 638: } 639: } 640: len = strlen(SD) + strlen(host) + 8; 641: tfname = mktemp("tf", n, len); 642: cfname = mktemp("cf", n, len); 643: dfname = mktemp("df", n, len); 644: inchar = strlen(SD) + 3; 645: n = (n + 1) % 1000; 646: (void) lseek(fd, 0L, 0); 647: sprintf(buf, "%03d\n", n); 648: (void) write(fd, buf, strlen(buf)); 649: (void) close(fd); /* unlocks as well */ 650: } 651: 652: /* 653: * Make a temp file name. 654: */ 655: char * 656: mktemp(id, num, len) 657: char *id; 658: int num, len; 659: { 660: register char *s; 661: extern char *malloc(); 662: 663: if ((s = malloc(len)) == NULL) 664: fatal("out of memory"); 665: (void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host); 666: return(s); 667: } 668: 669: /*VARARGS1*/ 670: fatal(msg, a1, a2, a3) 671: char *msg; 672: { 673: printf("%s: ", name); 674: printf(msg, a1, a2, a3); 675: putchar('\n'); 676: exit(1); 677: }