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