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