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