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