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