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