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