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