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