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