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 are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1983, 1989 Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)lpr.c 5.5 (Berkeley) 12/21/88"; 26 #endif /* not lint */ 27 /* 28 * lpr -- off line print 29 * 30 * Allows multiple printers and printers on remote machines by 31 * using information from a printer data base. 32 */ 33 34 #include <stdio.h> 35 #include <sys/types.h> 36 #include <sys/file.h> 37 #include <sys/stat.h> 38 #include <pwd.h> 39 #include <grp.h> 40 #include <signal.h> 41 #include <ctype.h> 42 #include <syslog.h> 43 #include "lp.local.h" 44 45 char *tfname; /* tmp copy of cf before linking */ 46 char *cfname; /* daemon control files, linked from tf's */ 47 char *dfname; /* data files */ 48 49 int nact; /* number of jobs to act on */ 50 int tfd; /* control file descriptor */ 51 int mailflg; /* send mail */ 52 int qflag; /* q job, but don't exec daemon */ 53 char format = 'f'; /* format char for printing files */ 54 int rflag; /* remove files upon completion */ 55 int sflag; /* symbolic link flag */ 56 int inchar; /* location to increment char in file names */ 57 int ncopies = 1; /* # of copies to make */ 58 int iflag; /* indentation wanted */ 59 int indent; /* amount to indent */ 60 int hdr = 1; /* print header or not (default is yes) */ 61 int userid; /* user id */ 62 char *person; /* user name */ 63 char *title; /* pr'ing title */ 64 char *fonts[4]; /* troff font names */ 65 char *width; /* width for versatec printing */ 66 char host[32]; /* host name */ 67 char *class = host; /* class title on header page */ 68 char *jobname; /* job name on header page */ 69 char *name; /* program name */ 70 char *printer; /* printer name */ 71 struct stat statb; 72 73 int MX; /* maximum number of blocks to copy */ 74 int MC; /* maximum number of copies allowed */ 75 int DU; /* daemon user-id */ 76 char *SD; /* spool directory */ 77 char *LO; /* lock file name */ 78 char *RG; /* restrict group */ 79 short SC; /* suppress multiple copies */ 80 81 char *getenv(); 82 char *rindex(); 83 char *linked(); 84 int cleanup(); 85 86 /*ARGSUSED*/ 87 main(argc, argv) 88 int argc; 89 char *argv[]; 90 { 91 extern struct passwd *getpwuid(); 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 fatal("multiple copies are not allowed"); 233 if (MC > 0 && ncopies > MC) 234 fatal("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 fatal("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 fatal("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 fatal("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 fatal("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 && lseek(tfd, 0L, 0) == 0 && 343 write(tfd, &c, 1) != 1) { 344 printf("%s: cannot touch %s\n", name, tfname); 345 tfname[inchar]++; 346 cleanup(); 347 } 348 (void) close(tfd); 349 } 350 if (link(tfname, cfname) < 0) { 351 printf("%s: cannot rename %s\n", name, cfname); 352 tfname[inchar]++; 353 cleanup(); 354 } 355 unlink(tfname); 356 if (qflag) /* just q things up */ 357 exit(0); 358 if (!startdaemon(printer)) 359 printf("jobs queued, but cannot start daemon.\n"); 360 exit(0); 361 } 362 cleanup(); 363 /* NOTREACHED */ 364 } 365 366 /* 367 * Create the file n and copy from file descriptor f. 368 */ 369 copy(f, n) 370 int f; 371 char n[]; 372 { 373 register int fd, i, nr, nc; 374 char buf[BUFSIZ]; 375 376 if (format == 'p') 377 card('T', title ? title : n); 378 for (i = 0; i < ncopies; i++) 379 card(format, &dfname[inchar-2]); 380 card('U', &dfname[inchar-2]); 381 card('N', n); 382 fd = nfile(dfname); 383 nr = nc = 0; 384 while ((i = read(f, buf, BUFSIZ)) > 0) { 385 if (write(fd, buf, i) != i) { 386 printf("%s: %s: temp file write error\n", name, n); 387 break; 388 } 389 nc += i; 390 if (nc >= BUFSIZ) { 391 nc -= BUFSIZ; 392 nr++; 393 if (MX > 0 && nr > MX) { 394 printf("%s: %s: copy file is too large\n", name, n); 395 break; 396 } 397 } 398 } 399 (void) close(fd); 400 if (nc==0 && nr==0) 401 printf("%s: %s: empty input file\n", name, f ? n : "stdin"); 402 else 403 nact++; 404 } 405 406 /* 407 * Try and link the file to dfname. Return a pointer to the full 408 * path name if successful. 409 */ 410 char * 411 linked(file) 412 register char *file; 413 { 414 register char *cp; 415 static char buf[BUFSIZ]; 416 417 if (*file != '/') { 418 if (getwd(buf) == NULL) 419 return(NULL); 420 while (file[0] == '.') { 421 switch (file[1]) { 422 case '/': 423 file += 2; 424 continue; 425 case '.': 426 if (file[2] == '/') { 427 if ((cp = rindex(buf, '/')) != NULL) 428 *cp = '\0'; 429 file += 3; 430 continue; 431 } 432 } 433 break; 434 } 435 strcat(buf, "/"); 436 strcat(buf, file); 437 file = buf; 438 } 439 return(symlink(file, dfname) ? NULL : file); 440 } 441 442 /* 443 * Put a line into the control file. 444 */ 445 card(c, p2) 446 register char c, *p2; 447 { 448 char buf[BUFSIZ]; 449 register char *p1 = buf; 450 register int len = 2; 451 452 *p1++ = c; 453 while ((c = *p2++) != '\0') { 454 *p1++ = c; 455 len++; 456 } 457 *p1++ = '\n'; 458 write(tfd, buf, len); 459 } 460 461 /* 462 * Create a new file in the spool directory. 463 */ 464 nfile(n) 465 char *n; 466 { 467 register 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(); 475 } 476 if (fchown(f, userid, -1) < 0) { 477 printf("%s: cannot chown %s\n", name, n); 478 cleanup(); 479 } 480 if (++n[inchar] > 'z') { 481 if (++n[inchar-2] == 't') { 482 printf("too many files - break up the job\n"); 483 cleanup(); 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 cleanup() 495 { 496 register i; 497 498 signal(SIGHUP, SIG_IGN); 499 signal(SIGINT, SIG_IGN); 500 signal(SIGQUIT, SIG_IGN); 501 signal(SIGTERM, SIG_IGN); 502 i = inchar; 503 if (tfname) 504 do 505 unlink(tfname); 506 while (tfname[i]-- != 'A'); 507 if (cfname) 508 do 509 unlink(cfname); 510 while (cfname[i]-- != 'A'); 511 if (dfname) 512 do { 513 do 514 unlink(dfname); 515 while (dfname[i]-- != 'A'); 516 dfname[i] = 'z'; 517 } while (dfname[i-2]-- != 'd'); 518 exit(1); 519 } 520 521 /* 522 * Test to see if this is a printable file. 523 * Return -1 if it is not, 0 if its printable, and 1 if 524 * we should remove it after printing. 525 */ 526 test(file) 527 char *file; 528 { 529 struct exec execb; 530 register int fd; 531 register char *cp; 532 533 if (access(file, 4) < 0) { 534 printf("%s: cannot access %s\n", name, file); 535 return(-1); 536 } 537 if (stat(file, &statb) < 0) { 538 printf("%s: cannot stat %s\n", name, file); 539 return(-1); 540 } 541 if ((statb.st_mode & S_IFMT) == S_IFDIR) { 542 printf("%s: %s is a directory\n", name, file); 543 return(-1); 544 } 545 if (statb.st_size == 0) { 546 printf("%s: %s is an empty file\n", name, file); 547 return(-1); 548 } 549 if ((fd = open(file, O_RDONLY)) < 0) { 550 printf("%s: cannot open %s\n", name, file); 551 return(-1); 552 } 553 if (read(fd, &execb, sizeof(execb)) == sizeof(execb)) 554 switch(execb.a_magic) { 555 case A_MAGIC1: 556 case A_MAGIC2: 557 case A_MAGIC3: 558 #ifdef A_MAGIC4 559 case A_MAGIC4: 560 #endif 561 printf("%s: %s is an executable program", name, file); 562 goto error1; 563 564 case ARMAG: 565 printf("%s: %s is an archive file", name, file); 566 goto error1; 567 } 568 (void) close(fd); 569 if (rflag) { 570 if ((cp = rindex(file, '/')) == NULL) { 571 if (access(".", 2) == 0) 572 return(1); 573 } else { 574 *cp = '\0'; 575 fd = access(file, 2); 576 *cp = '/'; 577 if (fd == 0) 578 return(1); 579 } 580 printf("%s: %s: is not removable by you\n", name, file); 581 } 582 return(0); 583 584 error1: 585 printf(" and is unprintable\n"); 586 (void) close(fd); 587 return(-1); 588 } 589 590 /* 591 * itoa - integer to string conversion 592 */ 593 char * 594 itoa(i) 595 register int i; 596 { 597 static char b[10] = "########"; 598 register char *p; 599 600 p = &b[8]; 601 do 602 *p-- = i%10 + '0'; 603 while (i /= 10); 604 return(++p); 605 } 606 607 /* 608 * Perform lookup for printer name or abbreviation -- 609 */ 610 chkprinter(s) 611 char *s; 612 { 613 int status; 614 char buf[BUFSIZ]; 615 static char pbuf[BUFSIZ/2]; 616 char *bp = pbuf; 617 extern char *pgetstr(); 618 619 if ((status = pgetent(buf, s)) < 0) 620 fatal("cannot open printer description file"); 621 else if (status == 0) 622 fatal("%s: unknown printer", s); 623 if ((SD = pgetstr("sd", &bp)) == NULL) 624 SD = DEFSPOOL; 625 if ((LO = pgetstr("lo", &bp)) == NULL) 626 LO = DEFLOCK; 627 RG = pgetstr("rg", &bp); 628 if ((MX = pgetnum("mx")) < 0) 629 MX = DEFMX; 630 if ((MC = pgetnum("mc")) < 0) 631 MC = DEFMAXCOPIES; 632 if ((DU = pgetnum("du")) < 0) 633 DU = DEFUID; 634 SC = pgetflag("sc"); 635 } 636 637 /* 638 * Make the temp files. 639 */ 640 mktemps() 641 { 642 register int c, len, fd, n; 643 register char *cp; 644 char buf[BUFSIZ]; 645 char *mktemp(); 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 = mktemp("tf", n, len); 666 cfname = mktemp("cf", n, len); 667 dfname = mktemp("df", n, len); 668 inchar = strlen(SD) + 3; 669 n = (n + 1) % 1000; 670 (void) lseek(fd, 0L, 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 char * 680 mktemp(id, num, len) 681 char *id; 682 int num, len; 683 { 684 register char *s; 685 extern char *malloc(); 686 687 if ((s = malloc(len)) == NULL) 688 fatal("out of memory"); 689 (void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host); 690 return(s); 691 } 692 693 /*VARARGS1*/ 694 fatal(msg, a1, a2, a3) 695 char *msg; 696 { 697 printf("%s: ", name); 698 printf(msg, a1, a2, a3); 699 putchar('\n'); 700 exit(1); 701 } 702