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