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