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