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