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 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40 #ifndef lint 41 static const char copyright[] = 42 "@(#) Copyright (c) 1983, 1989, 1993\n\ 43 The Regents of the University of California. All rights reserved.\n"; 44 #endif /* not lint */ 45 46 #if 0 47 #ifndef lint 48 static char sccsid[] = "@(#)lpr.c 8.4 (Berkeley) 4/28/95"; 49 #endif /* not lint */ 50 #endif 51 52 #include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 53 __FBSDID("$FreeBSD$"); 54 55 /* 56 * lpr -- off line print 57 * 58 * Allows multiple printers and printers on remote machines by 59 * using information from a printer data base. 60 */ 61 62 #include <sys/param.h> 63 #include <sys/stat.h> 64 65 #include <netinet/in.h> /* N_BADMAG uses ntohl() */ 66 67 #include <dirent.h> 68 #include <fcntl.h> 69 #include <a.out.h> 70 #include <err.h> 71 #include <locale.h> 72 #include <signal.h> 73 #include <syslog.h> 74 #include <pwd.h> 75 #include <grp.h> 76 #include <unistd.h> 77 #include <stdlib.h> 78 #include <stdio.h> 79 #include <ctype.h> 80 #include <string.h> 81 #include "lp.h" 82 #include "lp.local.h" 83 #include "pathnames.h" 84 85 static char *cfname; /* daemon control files, linked from tf's */ 86 static char *class = local_host; /* class title on header page */ 87 static char *dfname; /* data files */ 88 static char *fonts[4]; /* troff font names */ 89 static char format = 'f'; /* format char for printing files */ 90 static int hdr = 1; /* print header or not (default is yes) */ 91 static int iflag; /* indentation wanted */ 92 static int inchar; /* location to increment char in file names */ 93 static int indent; /* amount to indent */ 94 static const char *jobname; /* job name on header page */ 95 static int mailflg; /* send mail */ 96 static int nact; /* number of jobs to act on */ 97 static int ncopies = 1; /* # of copies to make */ 98 static char *lpr_username; /* person sending the print job(s) */ 99 static int qflag; /* q job, but don't exec daemon */ 100 static int rflag; /* remove files upon completion */ 101 static int sflag; /* symbolic link flag */ 102 static int tfd; /* control file descriptor */ 103 static char *tfname; /* tmp copy of cf before linking */ 104 static char *title; /* pr'ing title */ 105 static char *locale; /* pr'ing locale */ 106 static int userid; /* user id */ 107 static char *Uflag; /* user name specified with -U flag */ 108 static char *width; /* width for versatec printing */ 109 static char *Zflag; /* extra filter options for LPRng servers */ 110 111 static struct stat statb; 112 113 static void card(int _c, const char *_p2); 114 static int checkwriteperm(const char *_file, const char *_directory); 115 static void chkprinter(const char *_ptrname, struct printer *_pp); 116 static void cleanup(int _signo); 117 static void copy(const struct printer *_pp, int _f, const char _n[]); 118 static char *itoa(int _i); 119 static const char *linked(const char *_file); 120 int main(int _argc, char *_argv[]); 121 static char *lmktemp(const struct printer *_pp, const char *_id, 122 int _num, int len); 123 static void mktemps(const struct printer *_pp); 124 static int nfile(char *_n); 125 static int test(const char *_file); 126 static void usage(void); 127 128 uid_t uid, euid; 129 130 int 131 main(int argc, char *argv[]) 132 { 133 struct passwd *pw; 134 struct group *gptr; 135 const char *arg, *cp, *printer; 136 char *p; 137 char buf[BUFSIZ]; 138 int c, i, f, errs; 139 int ret, didlink; 140 struct stat stb; 141 struct stat statb1, statb2; 142 struct printer myprinter, *pp = &myprinter; 143 144 printer = NULL; 145 euid = geteuid(); 146 uid = getuid(); 147 seteuid(uid); 148 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 149 signal(SIGHUP, cleanup); 150 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 151 signal(SIGINT, cleanup); 152 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 153 signal(SIGQUIT, cleanup); 154 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 155 signal(SIGTERM, cleanup); 156 157 progname = argv[0]; 158 gethostname(local_host, sizeof(local_host)); 159 openlog("lpd", 0, LOG_LPR); 160 161 errs = 0; 162 while ((c = getopt(argc, argv, 163 ":#:1:2:3:4:C:J:L:P:T:U:Z:cdfghi:lnmprstvw:")) 164 != -1) 165 switch (c) { 166 case '#': /* n copies */ 167 i = strtol(optarg, &p, 10); 168 if (*p) 169 errx(1, "Bad argument to -#, number expected"); 170 if (i > 0) 171 ncopies = i; 172 break; 173 174 case '1': /* troff fonts */ 175 case '2': 176 case '3': 177 case '4': 178 fonts[optopt - '1'] = optarg; 179 break; 180 181 case 'C': /* classification spec */ 182 hdr++; 183 class = optarg; 184 break; 185 186 case 'J': /* job name */ 187 hdr++; 188 jobname = optarg; 189 break; 190 191 case 'P': /* specifiy printer name */ 192 printer = optarg; 193 break; 194 195 case 'L': /* pr's locale */ 196 locale = optarg; 197 break; 198 199 case 'T': /* pr's title line */ 200 title = optarg; 201 break; 202 203 case 'U': /* user name */ 204 hdr++; 205 Uflag = optarg; 206 break; 207 208 case 'Z': 209 Zflag = optarg; 210 break; 211 212 case 'c': /* print cifplot output */ 213 case 'd': /* print tex output (dvi files) */ 214 case 'g': /* print graph(1G) output */ 215 case 'l': /* literal output */ 216 case 'n': /* print ditroff output */ 217 case 't': /* print troff output (cat files) */ 218 case 'p': /* print using ``pr'' */ 219 case 'v': /* print vplot output */ 220 format = optopt; 221 break; 222 223 case 'f': /* print fortran output */ 224 format = 'r'; 225 break; 226 227 case 'h': /* nulifiy header page */ 228 hdr = 0; 229 break; 230 231 case 'i': /* indent output */ 232 iflag++; 233 indent = strtol(optarg, &p, 10); 234 if (*p) 235 errx(1, "Bad argument to -i, number expected"); 236 break; 237 238 case 'm': /* send mail when done */ 239 mailflg++; 240 break; 241 242 case 'q': /* just queue job */ 243 qflag++; 244 break; 245 246 case 'r': /* remove file when done */ 247 rflag++; 248 break; 249 250 case 's': /* try to link files */ 251 sflag++; 252 break; 253 254 case 'w': /* versatec page width */ 255 width = optarg; 256 break; 257 258 case ':': /* catch "missing argument" error */ 259 if (optopt == 'i') { 260 iflag++; /* -i without args is valid */ 261 indent = 8; 262 } else 263 errs++; 264 break; 265 266 default: 267 errs++; 268 } 269 argc -= optind; 270 argv += optind; 271 if (errs) 272 usage(); 273 if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 274 printer = DEFLP; 275 chkprinter(printer, pp); 276 if (pp->no_copies && ncopies > 1) 277 errx(1, "multiple copies are not allowed"); 278 if (pp->max_copies > 0 && ncopies > pp->max_copies) 279 errx(1, "only %ld copies are allowed", pp->max_copies); 280 /* 281 * Get the identity of the person doing the lpr using the same 282 * algorithm as lprm. Actually, not quite -- lprm will override 283 * the login name with "root" if the user is running as root; 284 * the daemon actually checks for the string "root" in its 285 * permission checking. Sigh. 286 */ 287 userid = getuid(); 288 if (Uflag) { 289 if (userid != 0 && userid != pp->daemon_user) 290 errx(1, "only privileged users may use the `-U' flag"); 291 lpr_username = Uflag; /* -U person doing 'lpr' */ 292 } else { 293 lpr_username = getlogin(); /* person doing 'lpr' */ 294 if (userid != pp->daemon_user || lpr_username == 0) { 295 if ((pw = getpwuid(userid)) == NULL) 296 errx(1, "Who are you?"); 297 lpr_username = pw->pw_name; 298 } 299 } 300 301 /* 302 * Check for restricted group access. 303 */ 304 if (pp->restrict_grp != NULL && userid != pp->daemon_user) { 305 if ((gptr = getgrnam(pp->restrict_grp)) == NULL) 306 errx(1, "Restricted group specified incorrectly"); 307 if (gptr->gr_gid != getgid()) { 308 while (*gptr->gr_mem != NULL) { 309 if ((strcmp(lpr_username, *gptr->gr_mem)) == 0) 310 break; 311 gptr->gr_mem++; 312 } 313 if (*gptr->gr_mem == NULL) 314 errx(1, "Not a member of the restricted group"); 315 } 316 } 317 /* 318 * Check to make sure queuing is enabled if userid is not root. 319 */ 320 lock_file_name(pp, buf, sizeof buf); 321 if (userid && stat(buf, &stb) == 0 && (stb.st_mode & LFM_QUEUE_DIS)) 322 errx(1, "Printer queue is disabled"); 323 /* 324 * Initialize the control file. 325 */ 326 mktemps(pp); 327 tfd = nfile(tfname); 328 seteuid(euid); 329 (void) fchown(tfd, pp->daemon_user, -1); 330 /* owned by daemon for protection */ 331 seteuid(uid); 332 card('H', local_host); 333 card('P', lpr_username); 334 card('C', class); 335 if (hdr && !pp->no_header) { 336 if (jobname == NULL) { 337 if (argc == 0) 338 jobname = "stdin"; 339 else 340 jobname = ((arg = strrchr(argv[0], '/')) 341 ? arg + 1 : argv[0]); 342 } 343 card('J', jobname); 344 card('L', lpr_username); 345 } 346 if (format != 'p' && Zflag != 0) 347 card('Z', Zflag); 348 if (iflag) 349 card('I', itoa(indent)); 350 if (mailflg) 351 card('M', lpr_username); 352 if (format == 't' || format == 'n' || format == 'd') 353 for (i = 0; i < 4; i++) 354 if (fonts[i] != NULL) 355 card('1'+i, fonts[i]); 356 if (width != NULL) 357 card('W', width); 358 /* 359 * XXX 360 * Our use of `Z' here is incompatible with LPRng's 361 * use. We assume that the only use of our existing 362 * `Z' card is as shown for `p' format (pr) files. 363 */ 364 if (format == 'p') { 365 char *s; 366 367 if (locale) 368 card('Z', locale); 369 else if ((s = setlocale(LC_TIME, "")) != NULL) 370 card('Z', s); 371 } 372 373 /* 374 * Read the files and spool them. 375 */ 376 if (argc == 0) 377 copy(pp, 0, " "); 378 else while (argc--) { 379 if (argv[0][0] == '-' && argv[0][1] == '\0') { 380 /* use stdin */ 381 copy(pp, 0, " "); 382 argv++; 383 continue; 384 } 385 if ((f = test(arg = *argv++)) < 0) 386 continue; /* file unreasonable */ 387 388 if (sflag && (cp = linked(arg)) != NULL) { 389 (void) snprintf(buf, sizeof(buf), "%u %u", statb.st_dev, 390 statb.st_ino); 391 card('S', buf); 392 if (format == 'p') 393 card('T', title ? title : arg); 394 for (i = 0; i < ncopies; i++) 395 card(format, &dfname[inchar-2]); 396 card('U', &dfname[inchar-2]); 397 if (f) 398 card('U', cp); 399 card('N', arg); 400 dfname[inchar]++; 401 nact++; 402 continue; 403 } 404 if (sflag) 405 printf("%s: %s: not linked, copying instead\n", 406 progname, arg); 407 408 if (f) { 409 /* 410 * The user wants the file removed after it is copied 411 * to the spool area, so see if the file can be moved 412 * instead of copy/unlink'ed. This is much faster and 413 * uses less spool space than copying the file. This 414 * can be very significant when running services like 415 * samba, pcnfs, CAP, et al. 416 */ 417 seteuid(euid); 418 didlink = 0; 419 /* 420 * There are several things to check to avoid any 421 * security issues. Some of these are redundant 422 * under BSD's, but are necessary when lpr is built 423 * under some other OS's (which I do do...) 424 */ 425 if (lstat(arg, &statb1) < 0) 426 goto nohardlink; 427 if (S_ISLNK(statb1.st_mode)) 428 goto nohardlink; 429 if (link(arg, dfname) != 0) 430 goto nohardlink; 431 didlink = 1; 432 /* 433 * Make sure the user hasn't tried to trick us via 434 * any race conditions 435 */ 436 if (lstat(dfname, &statb2) < 0) 437 goto nohardlink; 438 if (statb1.st_dev != statb2.st_dev) 439 goto nohardlink; 440 if (statb1.st_ino != statb2.st_ino) 441 goto nohardlink; 442 /* 443 * Skip if the file already had multiple hard links, 444 * because changing the owner and access-bits would 445 * change ALL versions of the file 446 */ 447 if (statb2.st_nlink > 2) 448 goto nohardlink; 449 /* 450 * If we can access and remove the original file 451 * without special setuid-ness then this method is 452 * safe. Otherwise, abandon the move and fall back 453 * to the (usual) copy method. 454 */ 455 seteuid(uid); 456 ret = access(dfname, R_OK); 457 if (ret == 0) 458 ret = unlink(arg); 459 seteuid(euid); 460 if (ret != 0) 461 goto nohardlink; 462 /* 463 * Unlink of user file was successful. Change the 464 * owner and permissions, add entries to the control 465 * file, and skip the file copying step. 466 */ 467 chown(dfname, pp->daemon_user, getegid()); 468 chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 469 seteuid(uid); 470 if (format == 'p') 471 card('T', title ? title : arg); 472 for (i = 0; i < ncopies; i++) 473 card(format, &dfname[inchar-2]); 474 card('U', &dfname[inchar-2]); 475 card('N', arg); 476 nact++; 477 continue; 478 nohardlink: 479 if (didlink) 480 unlink(dfname); 481 seteuid(uid); /* restore old uid */ 482 } /* end: if (f) */ 483 484 if ((i = open(arg, O_RDONLY)) < 0) { 485 printf("%s: cannot open %s\n", progname, arg); 486 } else { 487 copy(pp, i, arg); 488 (void) close(i); 489 if (f && unlink(arg) < 0) 490 printf("%s: %s: not removed\n", progname, arg); 491 } 492 } 493 494 if (nact) { 495 (void) close(tfd); 496 tfname[inchar]--; 497 /* 498 * Touch the control file to fix position in the queue. 499 */ 500 seteuid(euid); 501 if ((tfd = open(tfname, O_RDWR)) >= 0) { 502 char touch_c; 503 504 if (read(tfd, &touch_c, 1) == 1 && 505 lseek(tfd, (off_t)0, 0) == 0 && 506 write(tfd, &touch_c, 1) != 1) { 507 printf("%s: cannot touch %s\n", progname, 508 tfname); 509 tfname[inchar]++; 510 cleanup(0); 511 } 512 (void) close(tfd); 513 } 514 if (link(tfname, cfname) < 0) { 515 printf("%s: cannot rename %s\n", progname, cfname); 516 tfname[inchar]++; 517 cleanup(0); 518 } 519 unlink(tfname); 520 seteuid(uid); 521 if (qflag) /* just q things up */ 522 exit(0); 523 if (!startdaemon(pp)) 524 printf("jobs queued, but cannot start daemon.\n"); 525 exit(0); 526 } 527 cleanup(0); 528 return (1); 529 /* NOTREACHED */ 530 } 531 532 /* 533 * Create the file n and copy from file descriptor f. 534 */ 535 static void 536 copy(const struct printer *pp, int f, const char n[]) 537 { 538 register int fd, i, nr, nc; 539 char buf[BUFSIZ]; 540 541 if (format == 'p') 542 card('T', title ? title : n); 543 for (i = 0; i < ncopies; i++) 544 card(format, &dfname[inchar-2]); 545 card('U', &dfname[inchar-2]); 546 card('N', n); 547 fd = nfile(dfname); 548 nr = nc = 0; 549 while ((i = read(f, buf, BUFSIZ)) > 0) { 550 if (write(fd, buf, i) != i) { 551 printf("%s: %s: temp file write error\n", progname, n); 552 break; 553 } 554 nc += i; 555 if (nc >= BUFSIZ) { 556 nc -= BUFSIZ; 557 nr++; 558 if (pp->max_blocks > 0 && nr > pp->max_blocks) { 559 printf("%s: %s: copy file is too large\n", 560 progname, n); 561 break; 562 } 563 } 564 } 565 (void) close(fd); 566 if (nc==0 && nr==0) 567 printf("%s: %s: empty input file\n", progname, 568 f ? n : "stdin"); 569 else 570 nact++; 571 } 572 573 /* 574 * Try and link the file to dfname. Return a pointer to the full 575 * path name if successful. 576 */ 577 static const char * 578 linked(const char *file) 579 { 580 register char *cp; 581 static char buf[MAXPATHLEN]; 582 register int ret; 583 584 if (*file != '/') { 585 if (getcwd(buf, sizeof(buf)) == NULL) 586 return(NULL); 587 while (file[0] == '.') { 588 switch (file[1]) { 589 case '/': 590 file += 2; 591 continue; 592 case '.': 593 if (file[2] == '/') { 594 if ((cp = strrchr(buf, '/')) != NULL) 595 *cp = '\0'; 596 file += 3; 597 continue; 598 } 599 } 600 break; 601 } 602 strncat(buf, "/", sizeof(buf) - strlen(buf) - 1); 603 strncat(buf, file, sizeof(buf) - strlen(buf) - 1); 604 file = buf; 605 } 606 seteuid(euid); 607 ret = symlink(file, dfname); 608 seteuid(uid); 609 return(ret ? NULL : file); 610 } 611 612 /* 613 * Put a line into the control file. 614 */ 615 static void 616 card(int c, const char *p2) 617 { 618 char buf[BUFSIZ]; 619 register char *p1 = buf; 620 size_t len = 2; 621 622 *p1++ = c; 623 while ((c = *p2++) != '\0' && len < sizeof(buf)) { 624 *p1++ = (c == '\n') ? ' ' : c; 625 len++; 626 } 627 *p1++ = '\n'; 628 write(tfd, buf, len); 629 } 630 631 /* 632 * Create a new file in the spool directory. 633 */ 634 static int 635 nfile(char *n) 636 { 637 register int f; 638 int oldumask = umask(0); /* should block signals */ 639 640 seteuid(euid); 641 f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD); 642 (void) umask(oldumask); 643 if (f < 0) { 644 printf("%s: cannot create %s\n", progname, n); 645 cleanup(0); 646 } 647 if (fchown(f, userid, -1) < 0) { 648 printf("%s: cannot chown %s\n", progname, n); 649 cleanup(0); /* cleanup does exit */ 650 } 651 seteuid(uid); 652 if (++n[inchar] > 'z') { 653 if (++n[inchar-2] == 't') { 654 printf("too many files - break up the job\n"); 655 cleanup(0); 656 } 657 n[inchar] = 'A'; 658 } else if (n[inchar] == '[') 659 n[inchar] = 'a'; 660 return(f); 661 } 662 663 /* 664 * Cleanup after interrupts and errors. 665 */ 666 static void 667 cleanup(int signo __unused) 668 { 669 register int i; 670 671 signal(SIGHUP, SIG_IGN); 672 signal(SIGINT, SIG_IGN); 673 signal(SIGQUIT, SIG_IGN); 674 signal(SIGTERM, SIG_IGN); 675 i = inchar; 676 seteuid(euid); 677 if (tfname) 678 do 679 unlink(tfname); 680 while (tfname[i]-- != 'A'); 681 if (cfname) 682 do 683 unlink(cfname); 684 while (cfname[i]-- != 'A'); 685 if (dfname) 686 do { 687 do 688 unlink(dfname); 689 while (dfname[i]-- != 'A'); 690 dfname[i] = 'z'; 691 } while (dfname[i-2]-- != 'd'); 692 exit(1); 693 } 694 695 /* 696 * Test to see if this is a printable file. 697 * Return -1 if it is not, 0 if its printable, and 1 if 698 * we should remove it after printing. 699 */ 700 static int 701 test(const char *file) 702 { 703 struct exec execb; 704 size_t dlen; 705 int fd; 706 char *cp, *dirpath; 707 708 if (access(file, 4) < 0) { 709 printf("%s: cannot access %s\n", progname, file); 710 return(-1); 711 } 712 if (stat(file, &statb) < 0) { 713 printf("%s: cannot stat %s\n", progname, file); 714 return(-1); 715 } 716 if ((statb.st_mode & S_IFMT) == S_IFDIR) { 717 printf("%s: %s is a directory\n", progname, file); 718 return(-1); 719 } 720 if (statb.st_size == 0) { 721 printf("%s: %s is an empty file\n", progname, file); 722 return(-1); 723 } 724 if ((fd = open(file, O_RDONLY)) < 0) { 725 printf("%s: cannot open %s\n", progname, file); 726 return(-1); 727 } 728 /* 729 * XXX Shall we add a similar test for ELF? 730 */ 731 if (read(fd, &execb, sizeof(execb)) == sizeof(execb) && 732 !N_BADMAG(execb)) { 733 printf("%s: %s is an executable program", progname, file); 734 goto error1; 735 } 736 (void) close(fd); 737 if (rflag) { 738 /* 739 * aside: note that 'cp' is technically a 'const char *' 740 * (because it points into 'file'), even though strrchr 741 * returns a value of type 'char *'. 742 */ 743 if ((cp = strrchr(file, '/')) == NULL) { 744 if (checkwriteperm(file,".") == 0) 745 return(1); 746 } else { 747 if (cp == file) { 748 fd = checkwriteperm(file,"/"); 749 } else { 750 /* strlcpy will change the '/' to '\0' */ 751 dlen = cp - file + 1; 752 dirpath = malloc(dlen); 753 strlcpy(dirpath, file, dlen); 754 fd = checkwriteperm(file, dirpath); 755 free(dirpath); 756 } 757 if (fd == 0) 758 return(1); 759 } 760 printf("%s: %s: is not removable by you\n", progname, file); 761 } 762 return(0); 763 764 error1: 765 printf(" and is unprintable\n"); 766 (void) close(fd); 767 return(-1); 768 } 769 770 static int 771 checkwriteperm(const char *file, const char *directory) 772 { 773 struct stat stats; 774 if (access(directory, W_OK) == 0) { 775 stat(directory, &stats); 776 if (stats.st_mode & S_ISVTX) { 777 stat(file, &stats); 778 if(stats.st_uid == userid) { 779 return(0); 780 } 781 } else return(0); 782 } 783 return(-1); 784 } 785 786 /* 787 * itoa - integer to string conversion 788 */ 789 static char * 790 itoa(int i) 791 { 792 static char b[10] = "########"; 793 register char *p; 794 795 p = &b[8]; 796 do 797 *p-- = i%10 + '0'; 798 while (i /= 10); 799 return(++p); 800 } 801 802 /* 803 * Perform lookup for printer name or abbreviation -- 804 */ 805 static void 806 chkprinter(const char *ptrname, struct printer *pp) 807 { 808 int status; 809 810 init_printer(pp); 811 status = getprintcap(ptrname, pp); 812 switch(status) { 813 case PCAPERR_OSERR: 814 case PCAPERR_TCLOOP: 815 errx(1, "%s: %s", ptrname, pcaperr(status)); 816 case PCAPERR_NOTFOUND: 817 errx(1, "%s: unknown printer", ptrname); 818 case PCAPERR_TCOPEN: 819 warnx("%s: unresolved tc= reference(s)", ptrname); 820 } 821 } 822 823 /* 824 * Tell the user what we wanna get. 825 */ 826 static void 827 usage(void) 828 { 829 fprintf(stderr, "%s\n", 830 "usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]\n" 831 "\t[-Z daemon-options] [-i[numcols]] [-i[numcols]] [-1234 font]\n" 832 "\t[-L locale] [-wnum] [-cdfghlnmprstv] [name ...]"); 833 exit(1); 834 } 835 836 837 /* 838 * Make the temp files. 839 */ 840 static void 841 mktemps(const struct printer *pp) 842 { 843 register int len, fd, n; 844 register char *cp; 845 char buf[BUFSIZ]; 846 847 (void) snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir); 848 seteuid(euid); 849 if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) { 850 printf("%s: cannot create %s\n", progname, buf); 851 exit(1); 852 } 853 if (flock(fd, LOCK_EX)) { 854 printf("%s: cannot lock %s\n", progname, buf); 855 exit(1); 856 } 857 seteuid(uid); 858 n = 0; 859 if ((len = read(fd, buf, sizeof(buf))) > 0) { 860 for (cp = buf; len--; ) { 861 if (*cp < '0' || *cp > '9') 862 break; 863 n = n * 10 + (*cp++ - '0'); 864 } 865 } 866 len = strlen(pp->spool_dir) + strlen(local_host) + 8; 867 tfname = lmktemp(pp, "tf", n, len); 868 cfname = lmktemp(pp, "cf", n, len); 869 dfname = lmktemp(pp, "df", n, len); 870 inchar = strlen(pp->spool_dir) + 3; 871 n = (n + 1) % 1000; 872 (void) lseek(fd, (off_t)0, 0); 873 snprintf(buf, sizeof(buf), "%03d\n", n); 874 (void) write(fd, buf, strlen(buf)); 875 (void) close(fd); /* unlocks as well */ 876 } 877 878 /* 879 * Make a temp file name. 880 */ 881 static char * 882 lmktemp(const struct printer *pp, const char *id, int num, int len) 883 { 884 register char *s; 885 886 if ((s = malloc(len)) == NULL) 887 errx(1, "out of memory"); 888 (void) snprintf(s, len, "%s/%sA%03d%s", pp->spool_dir, id, num, 889 local_host); 890 return(s); 891 } 892