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