11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1989, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * (c) UNIX System Laboratories, Inc. 51553Srgrimes * All or some portions of this file are derived from material licensed 61553Srgrimes * to the University of California by American Telephone and Telegraph 71553Srgrimes * Co. or Unix System Laboratories, Inc. and are reproduced herein with 81553Srgrimes * the permission of UNIX System Laboratories, Inc. 91553Srgrimes * 101553Srgrimes * 111553Srgrimes * Redistribution and use in source and binary forms, with or without 121553Srgrimes * modification, are permitted provided that the following conditions 131553Srgrimes * are met: 141553Srgrimes * 1. Redistributions of source code must retain the above copyright 151553Srgrimes * notice, this list of conditions and the following disclaimer. 161553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171553Srgrimes * notice, this list of conditions and the following disclaimer in the 181553Srgrimes * documentation and/or other materials provided with the distribution. 191553Srgrimes * 3. All advertising materials mentioning features or use of this software 201553Srgrimes * must display the following acknowledgement: 211553Srgrimes * This product includes software developed by the University of 221553Srgrimes * California, Berkeley and its contributors. 231553Srgrimes * 4. Neither the name of the University nor the names of its contributors 241553Srgrimes * may be used to endorse or promote products derived from this software 251553Srgrimes * without specific prior written permission. 261553Srgrimes * 271553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 281553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 291553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 301553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 311553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 321553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 331553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 341553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 351553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 361553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 371553Srgrimes * SUCH DAMAGE. 381553Srgrimes */ 391553Srgrimes 401553Srgrimes#ifndef lint 4129780Scharnierstatic const char copyright[] = 421553Srgrimes"@(#) Copyright (c) 1983, 1989, 1993\n\ 431553Srgrimes The Regents of the University of California. All rights reserved.\n"; 441553Srgrimes#endif /* not lint */ 451553Srgrimes 46117621Sgad#if 0 471553Srgrimes#ifndef lint 48117621Sgadstatic char sccsid[] = "@(#)lpr.c 8.4 (Berkeley) 4/28/95"; 49117621Sgad#endif /* not lint */ 5029780Scharnier#endif 511553Srgrimes 52117621Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 53117621Sgad__FBSDID("$FreeBSD: releng/10.3/usr.sbin/lpr/lpr/lpr.c 241852 2012-10-22 03:31:22Z eadler $"); 54117621Sgad 551553Srgrimes/* 561553Srgrimes * lpr -- off line print 571553Srgrimes * 581553Srgrimes * Allows multiple printers and printers on remote machines by 591553Srgrimes * using information from a printer data base. 601553Srgrimes */ 611553Srgrimes 621553Srgrimes#include <sys/param.h> 631553Srgrimes#include <sys/stat.h> 641553Srgrimes 6595434Sgad#include <netinet/in.h> /* N_BADMAG uses ntohl() */ 6695434Sgad 671553Srgrimes#include <dirent.h> 681553Srgrimes#include <fcntl.h> 691553Srgrimes#include <a.out.h> 7031492Swollman#include <err.h> 7153956Sache#include <locale.h> 721553Srgrimes#include <signal.h> 731553Srgrimes#include <syslog.h> 741553Srgrimes#include <pwd.h> 751553Srgrimes#include <grp.h> 761553Srgrimes#include <unistd.h> 771553Srgrimes#include <stdlib.h> 78241015Smdf#include <stdint.h> 791553Srgrimes#include <stdio.h> 801553Srgrimes#include <ctype.h> 811553Srgrimes#include <string.h> 821553Srgrimes#include "lp.h" 831553Srgrimes#include "lp.local.h" 841553Srgrimes#include "pathnames.h" 851553Srgrimes 861553Srgrimesstatic char *cfname; /* daemon control files, linked from tf's */ 8778300Sgadstatic char *class = local_host; /* class title on header page */ 8823122Smppstatic char *dfname; /* data files */ 891553Srgrimesstatic char *fonts[4]; /* troff font names */ 901553Srgrimesstatic char format = 'f'; /* format char for printing files */ 911553Srgrimesstatic int hdr = 1; /* print header or not (default is yes) */ 921553Srgrimesstatic int iflag; /* indentation wanted */ 931553Srgrimesstatic int inchar; /* location to increment char in file names */ 941553Srgrimesstatic int indent; /* amount to indent */ 9578146Sgadstatic const char *jobname; /* job name on header page */ 961553Srgrimesstatic int mailflg; /* send mail */ 971553Srgrimesstatic int nact; /* number of jobs to act on */ 981553Srgrimesstatic int ncopies = 1; /* # of copies to make */ 9984695Sgadstatic char *lpr_username; /* person sending the print job(s) */ 1001553Srgrimesstatic int qflag; /* q job, but don't exec daemon */ 1018857Srgrimesstatic int rflag; /* remove files upon completion */ 1021553Srgrimesstatic int sflag; /* symbolic link flag */ 1031553Srgrimesstatic int tfd; /* control file descriptor */ 1041553Srgrimesstatic char *tfname; /* tmp copy of cf before linking */ 1051553Srgrimesstatic char *title; /* pr'ing title */ 10653956Sachestatic char *locale; /* pr'ing locale */ 1071553Srgrimesstatic int userid; /* user id */ 10843507Swollmanstatic char *Uflag; /* user name specified with -U flag */ 1091553Srgrimesstatic char *width; /* width for versatec printing */ 11061913Swollmanstatic char *Zflag; /* extra filter options for LPRng servers */ 1111553Srgrimes 1121553Srgrimesstatic struct stat statb; 1131553Srgrimes 11478146Sgadstatic void card(int _c, const char *_p2); 11578146Sgadstatic int checkwriteperm(const char *_file, const char *_directory); 11678146Sgadstatic void chkprinter(const char *_ptrname, struct printer *_pp); 11778146Sgadstatic void cleanup(int _signo); 11878146Sgadstatic void copy(const struct printer *_pp, int _f, const char _n[]); 11978146Sgadstatic char *itoa(int _i); 12078146Sgadstatic const char *linked(const char *_file); 12178146Sgadint main(int _argc, char *_argv[]); 12278146Sgadstatic char *lmktemp(const struct printer *_pp, const char *_id, 12378146Sgad int _num, int len); 12478146Sgadstatic void mktemps(const struct printer *_pp); 12578146Sgadstatic int nfile(char *_n); 12678146Sgadstatic int test(const char *_file); 12778146Sgadstatic void usage(void); 1281553Srgrimes 12927618Simpuid_t uid, euid; 13027618Simp 13119202Simpint 13278146Sgadmain(int argc, char *argv[]) 1331553Srgrimes{ 1341553Srgrimes struct passwd *pw; 1351553Srgrimes struct group *gptr; 13678146Sgad const char *arg, *cp, *printer; 13778146Sgad char *p; 1381553Srgrimes char buf[BUFSIZ]; 13915733Sjoerg int c, i, f, errs; 14068275Sgad int ret, didlink; 1411553Srgrimes struct stat stb; 14268275Sgad struct stat statb1, statb2; 14331492Swollman struct printer myprinter, *pp = &myprinter; 1441553Srgrimes 14531492Swollman printer = NULL; 14627618Simp euid = geteuid(); 14727618Simp uid = getuid(); 148241852Seadler PRIV_END 1491553Srgrimes if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 1501553Srgrimes signal(SIGHUP, cleanup); 1511553Srgrimes if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1521553Srgrimes signal(SIGINT, cleanup); 1531553Srgrimes if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1541553Srgrimes signal(SIGQUIT, cleanup); 1551553Srgrimes if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1561553Srgrimes signal(SIGTERM, cleanup); 1571553Srgrimes 15878280Sgad progname = argv[0]; 15978300Sgad gethostname(local_host, sizeof(local_host)); 1601553Srgrimes openlog("lpd", 0, LOG_LPR); 1611553Srgrimes 16215733Sjoerg errs = 0; 16315733Sjoerg while ((c = getopt(argc, argv, 16461913Swollman ":#:1:2:3:4:C:J:L:P:T:U:Z:cdfghi:lnmprstvw:")) 16561913Swollman != -1) 16615733Sjoerg switch (c) { 16715733Sjoerg case '#': /* n copies */ 16842340Simp i = strtol(optarg, &p, 10); 16942339Simp if (*p) 17042339Simp errx(1, "Bad argument to -#, number expected"); 17115733Sjoerg if (i > 0) 17215733Sjoerg ncopies = i; 17325787Sbrian break; 1741553Srgrimes 17515733Sjoerg case '1': /* troff fonts */ 17615733Sjoerg case '2': 17715733Sjoerg case '3': 17815733Sjoerg case '4': 17915733Sjoerg fonts[optopt - '1'] = optarg; 1801553Srgrimes break; 1811553Srgrimes 1821553Srgrimes case 'C': /* classification spec */ 1831553Srgrimes hdr++; 18415733Sjoerg class = optarg; 1851553Srgrimes break; 1861553Srgrimes 18715733Sjoerg case 'J': /* job name */ 1881553Srgrimes hdr++; 18915733Sjoerg jobname = optarg; 1901553Srgrimes break; 1911553Srgrimes 19215733Sjoerg case 'P': /* specifiy printer name */ 19315733Sjoerg printer = optarg; 1941553Srgrimes break; 1951553Srgrimes 19653956Sache case 'L': /* pr's locale */ 19753956Sache locale = optarg; 19853956Sache break; 19953956Sache 2001553Srgrimes case 'T': /* pr's title line */ 20115733Sjoerg title = optarg; 2021553Srgrimes break; 2031553Srgrimes 20415733Sjoerg case 'U': /* user name */ 20515733Sjoerg hdr++; 20643519Swollman Uflag = optarg; 20715733Sjoerg break; 20815733Sjoerg 20961913Swollman case 'Z': 21061913Swollman Zflag = optarg; 21161913Swollman break; 21261913Swollman 21315733Sjoerg case 'c': /* print cifplot output */ 21415733Sjoerg case 'd': /* print tex output (dvi files) */ 21515733Sjoerg case 'g': /* print graph(1G) output */ 2161553Srgrimes case 'l': /* literal output */ 21715733Sjoerg case 'n': /* print ditroff output */ 21815733Sjoerg case 't': /* print troff output (cat files) */ 2191553Srgrimes case 'p': /* print using ``pr'' */ 2201553Srgrimes case 'v': /* print vplot output */ 22115733Sjoerg format = optopt; 2221553Srgrimes break; 2231553Srgrimes 2241553Srgrimes case 'f': /* print fortran output */ 2251553Srgrimes format = 'r'; 2261553Srgrimes break; 2271553Srgrimes 22835251Sobrien case 'h': /* nulifiy header page */ 22935251Sobrien hdr = 0; 2301553Srgrimes break; 2311553Srgrimes 23215733Sjoerg case 'i': /* indent output */ 23315733Sjoerg iflag++; 23442340Simp indent = strtol(optarg, &p, 10); 23542339Simp if (*p) 23642339Simp errx(1, "Bad argument to -i, number expected"); 2371553Srgrimes break; 2381553Srgrimes 2391553Srgrimes case 'm': /* send mail when done */ 2401553Srgrimes mailflg++; 2411553Srgrimes break; 2421553Srgrimes 24315733Sjoerg case 'q': /* just queue job */ 24415733Sjoerg qflag++; 2451553Srgrimes break; 2461553Srgrimes 24715733Sjoerg case 'r': /* remove file when done */ 24815733Sjoerg rflag++; 24915733Sjoerg break; 25015733Sjoerg 2511553Srgrimes case 's': /* try to link files */ 2521553Srgrimes sflag++; 2531553Srgrimes break; 2541553Srgrimes 25515733Sjoerg case 'w': /* versatec page width */ 25615733Sjoerg width = optarg; 2571553Srgrimes break; 2581553Srgrimes 25915733Sjoerg case ':': /* catch "missing argument" error */ 26015733Sjoerg if (optopt == 'i') { 26115733Sjoerg iflag++; /* -i without args is valid */ 26215733Sjoerg indent = 8; 26315733Sjoerg } else 26415733Sjoerg errs++; 2651553Srgrimes break; 2661553Srgrimes 26715733Sjoerg default: 26815733Sjoerg errs++; 2691553Srgrimes } 27015733Sjoerg argc -= optind; 27115733Sjoerg argv += optind; 27215733Sjoerg if (errs) 27315733Sjoerg usage(); 2741553Srgrimes if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 2751553Srgrimes printer = DEFLP; 27631492Swollman chkprinter(printer, pp); 27731492Swollman if (pp->no_copies && ncopies > 1) 27831492Swollman errx(1, "multiple copies are not allowed"); 27931492Swollman if (pp->max_copies > 0 && ncopies > pp->max_copies) 28031583Sjdp errx(1, "only %ld copies are allowed", pp->max_copies); 2811553Srgrimes /* 2821553Srgrimes * Get the identity of the person doing the lpr using the same 28331492Swollman * algorithm as lprm. Actually, not quite -- lprm will override 28431492Swollman * the login name with "root" if the user is running as root; 28531492Swollman * the daemon actually checks for the string "root" in its 28631492Swollman * permission checking. Sigh. 2871553Srgrimes */ 28843507Swollman userid = getuid(); 28943507Swollman if (Uflag) { 29043507Swollman if (userid != 0 && userid != pp->daemon_user) 29143507Swollman errx(1, "only privileged users may use the `-U' flag"); 29284695Sgad lpr_username = Uflag; /* -U person doing 'lpr' */ 29343507Swollman } else { 29484695Sgad lpr_username = getlogin(); /* person doing 'lpr' */ 29584695Sgad if (userid != pp->daemon_user || lpr_username == 0) { 29631492Swollman if ((pw = getpwuid(userid)) == NULL) 29731492Swollman errx(1, "Who are you?"); 29884695Sgad lpr_username = pw->pw_name; 29931492Swollman } 3001553Srgrimes } 30143507Swollman 3021553Srgrimes /* 3031553Srgrimes * Check for restricted group access. 3041553Srgrimes */ 30531492Swollman if (pp->restrict_grp != NULL && userid != pp->daemon_user) { 30631492Swollman if ((gptr = getgrnam(pp->restrict_grp)) == NULL) 30731492Swollman errx(1, "Restricted group specified incorrectly"); 3081553Srgrimes if (gptr->gr_gid != getgid()) { 3091553Srgrimes while (*gptr->gr_mem != NULL) { 31084695Sgad if ((strcmp(lpr_username, *gptr->gr_mem)) == 0) 3111553Srgrimes break; 3121553Srgrimes gptr->gr_mem++; 3131553Srgrimes } 3141553Srgrimes if (*gptr->gr_mem == NULL) 31531492Swollman errx(1, "Not a member of the restricted group"); 3161553Srgrimes } 3171553Srgrimes } 3181553Srgrimes /* 3191553Srgrimes * Check to make sure queuing is enabled if userid is not root. 3201553Srgrimes */ 32131492Swollman lock_file_name(pp, buf, sizeof buf); 32231492Swollman if (userid && stat(buf, &stb) == 0 && (stb.st_mode & LFM_QUEUE_DIS)) 32331492Swollman errx(1, "Printer queue is disabled"); 3241553Srgrimes /* 3251553Srgrimes * Initialize the control file. 3261553Srgrimes */ 32731492Swollman mktemps(pp); 3281553Srgrimes tfd = nfile(tfname); 329241852Seadler PRIV_START 33031492Swollman (void) fchown(tfd, pp->daemon_user, -1); 33131492Swollman /* owned by daemon for protection */ 332241852Seadler PRIV_END 33378300Sgad card('H', local_host); 33484695Sgad card('P', lpr_username); 33568149Sgad card('C', class); 33656287Sjoe if (hdr && !pp->no_header) { 3371553Srgrimes if (jobname == NULL) { 33823122Smpp if (argc == 0) 3391553Srgrimes jobname = "stdin"; 3401553Srgrimes else 34131492Swollman jobname = ((arg = strrchr(argv[0], '/')) 34231492Swollman ? arg + 1 : argv[0]); 3431553Srgrimes } 3441553Srgrimes card('J', jobname); 34584695Sgad card('L', lpr_username); 3461553Srgrimes } 34761913Swollman if (format != 'p' && Zflag != 0) 34861913Swollman card('Z', Zflag); 3491553Srgrimes if (iflag) 3501553Srgrimes card('I', itoa(indent)); 3511553Srgrimes if (mailflg) 35284695Sgad card('M', lpr_username); 3531553Srgrimes if (format == 't' || format == 'n' || format == 'd') 3541553Srgrimes for (i = 0; i < 4; i++) 3551553Srgrimes if (fonts[i] != NULL) 3561553Srgrimes card('1'+i, fonts[i]); 3571553Srgrimes if (width != NULL) 3581553Srgrimes card('W', width); 35961913Swollman /* 36061913Swollman * XXX 36161913Swollman * Our use of `Z' here is incompatible with LPRng's 36261913Swollman * use. We assume that the only use of our existing 36361913Swollman * `Z' card is as shown for `p' format (pr) files. 36461913Swollman */ 36553956Sache if (format == 'p') { 36653956Sache char *s; 3671553Srgrimes 36853956Sache if (locale) 36953956Sache card('Z', locale); 37053956Sache else if ((s = setlocale(LC_TIME, "")) != NULL) 37153956Sache card('Z', s); 37253956Sache } 37353956Sache 3741553Srgrimes /* 3751553Srgrimes * Read the files and spool them. 3761553Srgrimes */ 37715733Sjoerg if (argc == 0) 37831492Swollman copy(pp, 0, " "); 37915733Sjoerg else while (argc--) { 38015733Sjoerg if (argv[0][0] == '-' && argv[0][1] == '\0') { 38115733Sjoerg /* use stdin */ 38231492Swollman copy(pp, 0, " "); 38315733Sjoerg argv++; 38415733Sjoerg continue; 38515733Sjoerg } 38615733Sjoerg if ((f = test(arg = *argv++)) < 0) 3871553Srgrimes continue; /* file unreasonable */ 3881553Srgrimes 3891553Srgrimes if (sflag && (cp = linked(arg)) != NULL) { 390241015Smdf (void)snprintf(buf, sizeof(buf), "%u %ju", 391241015Smdf statb.st_dev, (uintmax_t)statb.st_ino); 3921553Srgrimes card('S', buf); 3931553Srgrimes if (format == 'p') 3941553Srgrimes card('T', title ? title : arg); 3951553Srgrimes for (i = 0; i < ncopies; i++) 3961553Srgrimes card(format, &dfname[inchar-2]); 3971553Srgrimes card('U', &dfname[inchar-2]); 3981553Srgrimes if (f) 3991553Srgrimes card('U', cp); 4001553Srgrimes card('N', arg); 4011553Srgrimes dfname[inchar]++; 4021553Srgrimes nact++; 4031553Srgrimes continue; 4041553Srgrimes } 4051553Srgrimes if (sflag) 40678280Sgad printf("%s: %s: not linked, copying instead\n", 40778280Sgad progname, arg); 40868275Sgad 40968275Sgad if (f) { 41068275Sgad /* 41168275Sgad * The user wants the file removed after it is copied 41268275Sgad * to the spool area, so see if the file can be moved 41368275Sgad * instead of copy/unlink'ed. This is much faster and 41468275Sgad * uses less spool space than copying the file. This 41568275Sgad * can be very significant when running services like 41668275Sgad * samba, pcnfs, CAP, et al. 41768275Sgad */ 418241852Seadler PRIV_START 41968275Sgad didlink = 0; 42068275Sgad /* 42168275Sgad * There are several things to check to avoid any 42268275Sgad * security issues. Some of these are redundant 42368275Sgad * under BSD's, but are necessary when lpr is built 42468275Sgad * under some other OS's (which I do do...) 42568275Sgad */ 42668275Sgad if (lstat(arg, &statb1) < 0) 42768275Sgad goto nohardlink; 42868275Sgad if (S_ISLNK(statb1.st_mode)) 42968275Sgad goto nohardlink; 43068275Sgad if (link(arg, dfname) != 0) 43168275Sgad goto nohardlink; 43268275Sgad didlink = 1; 43368275Sgad /* 43468275Sgad * Make sure the user hasn't tried to trick us via 43568275Sgad * any race conditions 43668275Sgad */ 43768275Sgad if (lstat(dfname, &statb2) < 0) 43868275Sgad goto nohardlink; 43968275Sgad if (statb1.st_dev != statb2.st_dev) 44068275Sgad goto nohardlink; 44168275Sgad if (statb1.st_ino != statb2.st_ino) 44268275Sgad goto nohardlink; 44368275Sgad /* 44468275Sgad * Skip if the file already had multiple hard links, 44568275Sgad * because changing the owner and access-bits would 44668275Sgad * change ALL versions of the file 44768275Sgad */ 44868275Sgad if (statb2.st_nlink > 2) 44968275Sgad goto nohardlink; 45068275Sgad /* 45168275Sgad * If we can access and remove the original file 45268275Sgad * without special setuid-ness then this method is 45368275Sgad * safe. Otherwise, abandon the move and fall back 45468275Sgad * to the (usual) copy method. 45568275Sgad */ 456241852Seadler PRIV_END 45768275Sgad ret = access(dfname, R_OK); 45868275Sgad if (ret == 0) 45968275Sgad ret = unlink(arg); 460241852Seadler PRIV_START 46168275Sgad if (ret != 0) 46268275Sgad goto nohardlink; 46368275Sgad /* 46468275Sgad * Unlink of user file was successful. Change the 46568275Sgad * owner and permissions, add entries to the control 46668275Sgad * file, and skip the file copying step. 46768275Sgad */ 46868275Sgad chown(dfname, pp->daemon_user, getegid()); 46968275Sgad chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 470241852Seadler PRIV_END 47168275Sgad if (format == 'p') 47268275Sgad card('T', title ? title : arg); 47368275Sgad for (i = 0; i < ncopies; i++) 47468275Sgad card(format, &dfname[inchar-2]); 47568275Sgad card('U', &dfname[inchar-2]); 47668275Sgad card('N', arg); 47768275Sgad nact++; 47868275Sgad continue; 47968275Sgad nohardlink: 48068275Sgad if (didlink) 48168275Sgad unlink(dfname); 482241852Seadler PRIV_END /* restore old uid */ 48368275Sgad } /* end: if (f) */ 48468275Sgad 4851553Srgrimes if ((i = open(arg, O_RDONLY)) < 0) { 48678280Sgad printf("%s: cannot open %s\n", progname, arg); 48719202Simp } else { 48831492Swollman copy(pp, i, arg); 48919202Simp (void) close(i); 49019202Simp if (f && unlink(arg) < 0) 49178280Sgad printf("%s: %s: not removed\n", progname, arg); 4921553Srgrimes } 4931553Srgrimes } 4941553Srgrimes 4951553Srgrimes if (nact) { 4961553Srgrimes (void) close(tfd); 4971553Srgrimes tfname[inchar]--; 4981553Srgrimes /* 4991553Srgrimes * Touch the control file to fix position in the queue. 5001553Srgrimes */ 501241852Seadler PRIV_START 5021553Srgrimes if ((tfd = open(tfname, O_RDWR)) >= 0) { 50384697Sgad char touch_c; 5041553Srgrimes 50584697Sgad if (read(tfd, &touch_c, 1) == 1 && 5061553Srgrimes lseek(tfd, (off_t)0, 0) == 0 && 50784697Sgad write(tfd, &touch_c, 1) != 1) { 50878280Sgad printf("%s: cannot touch %s\n", progname, 50978280Sgad tfname); 5101553Srgrimes tfname[inchar]++; 5111553Srgrimes cleanup(0); 5121553Srgrimes } 5131553Srgrimes (void) close(tfd); 5141553Srgrimes } 5151553Srgrimes if (link(tfname, cfname) < 0) { 51678280Sgad printf("%s: cannot rename %s\n", progname, cfname); 5171553Srgrimes tfname[inchar]++; 5181553Srgrimes cleanup(0); 5191553Srgrimes } 5201553Srgrimes unlink(tfname); 521241852Seadler PRIV_END 5221553Srgrimes if (qflag) /* just q things up */ 5231553Srgrimes exit(0); 52431492Swollman if (!startdaemon(pp)) 5251553Srgrimes printf("jobs queued, but cannot start daemon.\n"); 5261553Srgrimes exit(0); 5271553Srgrimes } 5281553Srgrimes cleanup(0); 52927618Simp return (1); 5301553Srgrimes /* NOTREACHED */ 5311553Srgrimes} 5321553Srgrimes 5331553Srgrimes/* 5341553Srgrimes * Create the file n and copy from file descriptor f. 5351553Srgrimes */ 5361553Srgrimesstatic void 53778146Sgadcopy(const struct printer *pp, int f, const char n[]) 5381553Srgrimes{ 5391553Srgrimes register int fd, i, nr, nc; 5401553Srgrimes char buf[BUFSIZ]; 5411553Srgrimes 5421553Srgrimes if (format == 'p') 5431553Srgrimes card('T', title ? title : n); 5441553Srgrimes for (i = 0; i < ncopies; i++) 5451553Srgrimes card(format, &dfname[inchar-2]); 5461553Srgrimes card('U', &dfname[inchar-2]); 5471553Srgrimes card('N', n); 5481553Srgrimes fd = nfile(dfname); 5491553Srgrimes nr = nc = 0; 5501553Srgrimes while ((i = read(f, buf, BUFSIZ)) > 0) { 5511553Srgrimes if (write(fd, buf, i) != i) { 55278280Sgad printf("%s: %s: temp file write error\n", progname, n); 5531553Srgrimes break; 5541553Srgrimes } 5551553Srgrimes nc += i; 5561553Srgrimes if (nc >= BUFSIZ) { 5571553Srgrimes nc -= BUFSIZ; 5581553Srgrimes nr++; 55931492Swollman if (pp->max_blocks > 0 && nr > pp->max_blocks) { 56031492Swollman printf("%s: %s: copy file is too large\n", 56178280Sgad progname, n); 5621553Srgrimes break; 5631553Srgrimes } 5641553Srgrimes } 5651553Srgrimes } 5661553Srgrimes (void) close(fd); 5678857Srgrimes if (nc==0 && nr==0) 56878280Sgad printf("%s: %s: empty input file\n", progname, 56978280Sgad f ? n : "stdin"); 5701553Srgrimes else 5711553Srgrimes nact++; 5721553Srgrimes} 5731553Srgrimes 5741553Srgrimes/* 5751553Srgrimes * Try and link the file to dfname. Return a pointer to the full 5761553Srgrimes * path name if successful. 5771553Srgrimes */ 57878146Sgadstatic const char * 57978146Sgadlinked(const char *file) 5801553Srgrimes{ 5811553Srgrimes register char *cp; 58227282Sdima static char buf[MAXPATHLEN]; 58327618Simp register int ret; 5841553Srgrimes 5851553Srgrimes if (*file != '/') { 58627282Sdima if (getcwd(buf, sizeof(buf)) == NULL) 5871553Srgrimes return(NULL); 5881553Srgrimes while (file[0] == '.') { 5891553Srgrimes switch (file[1]) { 5901553Srgrimes case '/': 5911553Srgrimes file += 2; 5921553Srgrimes continue; 5931553Srgrimes case '.': 5941553Srgrimes if (file[2] == '/') { 59527635Simp if ((cp = strrchr(buf, '/')) != NULL) 5961553Srgrimes *cp = '\0'; 5971553Srgrimes file += 3; 5981553Srgrimes continue; 5991553Srgrimes } 6001553Srgrimes } 6011553Srgrimes break; 6021553Srgrimes } 60327282Sdima strncat(buf, "/", sizeof(buf) - strlen(buf) - 1); 60427282Sdima strncat(buf, file, sizeof(buf) - strlen(buf) - 1); 6051553Srgrimes file = buf; 6061553Srgrimes } 607241852Seadler PRIV_START 60827618Simp ret = symlink(file, dfname); 609241852Seadler PRIV_END 61027618Simp return(ret ? NULL : file); 6111553Srgrimes} 6121553Srgrimes 6131553Srgrimes/* 6141553Srgrimes * Put a line into the control file. 6151553Srgrimes */ 6161553Srgrimesstatic void 61778146Sgadcard(int c, const char *p2) 6181553Srgrimes{ 6191553Srgrimes char buf[BUFSIZ]; 6201553Srgrimes register char *p1 = buf; 62184696Sgad size_t len = 2; 6221553Srgrimes 6231553Srgrimes *p1++ = c; 62419187Simp while ((c = *p2++) != '\0' && len < sizeof(buf)) { 6251553Srgrimes *p1++ = (c == '\n') ? ' ' : c; 6261553Srgrimes len++; 6271553Srgrimes } 6281553Srgrimes *p1++ = '\n'; 6291553Srgrimes write(tfd, buf, len); 6301553Srgrimes} 6311553Srgrimes 6321553Srgrimes/* 6331553Srgrimes * Create a new file in the spool directory. 6341553Srgrimes */ 6351553Srgrimesstatic int 63678146Sgadnfile(char *n) 6371553Srgrimes{ 6381553Srgrimes register int f; 6391553Srgrimes int oldumask = umask(0); /* should block signals */ 6401553Srgrimes 641241852Seadler PRIV_START 6421553Srgrimes f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD); 6431553Srgrimes (void) umask(oldumask); 6441553Srgrimes if (f < 0) { 64578280Sgad printf("%s: cannot create %s\n", progname, n); 6461553Srgrimes cleanup(0); 6471553Srgrimes } 6481553Srgrimes if (fchown(f, userid, -1) < 0) { 64978280Sgad printf("%s: cannot chown %s\n", progname, n); 65027618Simp cleanup(0); /* cleanup does exit */ 6511553Srgrimes } 652241852Seadler PRIV_END 6531553Srgrimes if (++n[inchar] > 'z') { 6541553Srgrimes if (++n[inchar-2] == 't') { 6551553Srgrimes printf("too many files - break up the job\n"); 6561553Srgrimes cleanup(0); 6571553Srgrimes } 6581553Srgrimes n[inchar] = 'A'; 6591553Srgrimes } else if (n[inchar] == '[') 6601553Srgrimes n[inchar] = 'a'; 6611553Srgrimes return(f); 6621553Srgrimes} 6631553Srgrimes 6641553Srgrimes/* 6651553Srgrimes * Cleanup after interrupts and errors. 6661553Srgrimes */ 6671553Srgrimesstatic void 66878146Sgadcleanup(int signo __unused) 6691553Srgrimes{ 67039084Swollman register int i; 6711553Srgrimes 6721553Srgrimes signal(SIGHUP, SIG_IGN); 6731553Srgrimes signal(SIGINT, SIG_IGN); 6741553Srgrimes signal(SIGQUIT, SIG_IGN); 6751553Srgrimes signal(SIGTERM, SIG_IGN); 6761553Srgrimes i = inchar; 677241852Seadler PRIV_START 6781553Srgrimes if (tfname) 6791553Srgrimes do 6801553Srgrimes unlink(tfname); 6811553Srgrimes while (tfname[i]-- != 'A'); 6821553Srgrimes if (cfname) 6831553Srgrimes do 6841553Srgrimes unlink(cfname); 6851553Srgrimes while (cfname[i]-- != 'A'); 6861553Srgrimes if (dfname) 6871553Srgrimes do { 6881553Srgrimes do 6891553Srgrimes unlink(dfname); 6901553Srgrimes while (dfname[i]-- != 'A'); 6911553Srgrimes dfname[i] = 'z'; 6921553Srgrimes } while (dfname[i-2]-- != 'd'); 6931553Srgrimes exit(1); 6941553Srgrimes} 6951553Srgrimes 6961553Srgrimes/* 6971553Srgrimes * Test to see if this is a printable file. 6981553Srgrimes * Return -1 if it is not, 0 if its printable, and 1 if 6991553Srgrimes * we should remove it after printing. 7001553Srgrimes */ 7011553Srgrimesstatic int 70278146Sgadtest(const char *file) 7031553Srgrimes{ 7041553Srgrimes struct exec execb; 70579743Sgad size_t dlen; 70679743Sgad int fd; 70779743Sgad char *cp, *dirpath; 7081553Srgrimes 7091553Srgrimes if (access(file, 4) < 0) { 71078280Sgad printf("%s: cannot access %s\n", progname, file); 7111553Srgrimes return(-1); 7121553Srgrimes } 7131553Srgrimes if (stat(file, &statb) < 0) { 71478280Sgad printf("%s: cannot stat %s\n", progname, file); 7151553Srgrimes return(-1); 7161553Srgrimes } 7171553Srgrimes if ((statb.st_mode & S_IFMT) == S_IFDIR) { 71878280Sgad printf("%s: %s is a directory\n", progname, file); 7191553Srgrimes return(-1); 7201553Srgrimes } 7211553Srgrimes if (statb.st_size == 0) { 72278280Sgad printf("%s: %s is an empty file\n", progname, file); 7231553Srgrimes return(-1); 7241553Srgrimes } 7251553Srgrimes if ((fd = open(file, O_RDONLY)) < 0) { 72678280Sgad printf("%s: cannot open %s\n", progname, file); 7271553Srgrimes return(-1); 7281553Srgrimes } 72939084Swollman /* 73039084Swollman * XXX Shall we add a similar test for ELF? 73139084Swollman */ 7321553Srgrimes if (read(fd, &execb, sizeof(execb)) == sizeof(execb) && 7331553Srgrimes !N_BADMAG(execb)) { 73478280Sgad printf("%s: %s is an executable program", progname, file); 73527618Simp goto error1; 73627618Simp } 7371553Srgrimes (void) close(fd); 7381553Srgrimes if (rflag) { 73979743Sgad /* 74079743Sgad * aside: note that 'cp' is technically a 'const char *' 74179743Sgad * (because it points into 'file'), even though strrchr 74279743Sgad * returns a value of type 'char *'. 74379743Sgad */ 74427635Simp if ((cp = strrchr(file, '/')) == NULL) { 7459568Storstenb if (checkwriteperm(file,".") == 0) 7461553Srgrimes return(1); 7471553Srgrimes } else { 7481553Srgrimes if (cp == file) { 7499568Storstenb fd = checkwriteperm(file,"/"); 7501553Srgrimes } else { 75179743Sgad /* strlcpy will change the '/' to '\0' */ 75279743Sgad dlen = cp - file + 1; 75379743Sgad dirpath = malloc(dlen); 75479743Sgad strlcpy(dirpath, file, dlen); 75579743Sgad fd = checkwriteperm(file, dirpath); 75679743Sgad free(dirpath); 7571553Srgrimes } 7581553Srgrimes if (fd == 0) 7591553Srgrimes return(1); 7601553Srgrimes } 76178280Sgad printf("%s: %s: is not removable by you\n", progname, file); 7621553Srgrimes } 7631553Srgrimes return(0); 7641553Srgrimes 7651553Srgrimeserror1: 7661553Srgrimes printf(" and is unprintable\n"); 7671553Srgrimes (void) close(fd); 7681553Srgrimes return(-1); 7691553Srgrimes} 7701553Srgrimes 7719568Storstenbstatic int 77278146Sgadcheckwriteperm(const char *file, const char *directory) 7739568Storstenb{ 7749568Storstenb struct stat stats; 7759568Storstenb if (access(directory, W_OK) == 0) { 7769568Storstenb stat(directory, &stats); 7779568Storstenb if (stats.st_mode & S_ISVTX) { 7789568Storstenb stat(file, &stats); 7799568Storstenb if(stats.st_uid == userid) { 7809568Storstenb return(0); 7819568Storstenb } 7829568Storstenb } else return(0); 7839568Storstenb } 7849568Storstenb return(-1); 7859568Storstenb} 7869568Storstenb 7871553Srgrimes/* 7881553Srgrimes * itoa - integer to string conversion 7891553Srgrimes */ 7901553Srgrimesstatic char * 79178146Sgaditoa(int i) 7921553Srgrimes{ 7931553Srgrimes static char b[10] = "########"; 7941553Srgrimes register char *p; 7951553Srgrimes 7961553Srgrimes p = &b[8]; 7971553Srgrimes do 7981553Srgrimes *p-- = i%10 + '0'; 7991553Srgrimes while (i /= 10); 8001553Srgrimes return(++p); 8011553Srgrimes} 8021553Srgrimes 8031553Srgrimes/* 8041553Srgrimes * Perform lookup for printer name or abbreviation -- 8051553Srgrimes */ 8061553Srgrimesstatic void 80778146Sgadchkprinter(const char *ptrname, struct printer *pp) 8081553Srgrimes{ 8091553Srgrimes int status; 8101553Srgrimes 81131492Swollman init_printer(pp); 81278146Sgad status = getprintcap(ptrname, pp); 81331492Swollman switch(status) { 81431492Swollman case PCAPERR_OSERR: 81531492Swollman case PCAPERR_TCLOOP: 81678146Sgad errx(1, "%s: %s", ptrname, pcaperr(status)); 81731492Swollman case PCAPERR_NOTFOUND: 81878146Sgad errx(1, "%s: unknown printer", ptrname); 81931492Swollman case PCAPERR_TCOPEN: 82078146Sgad warnx("%s: unresolved tc= reference(s)", ptrname); 82131492Swollman } 8221553Srgrimes} 8231553Srgrimes 8241553Srgrimes/* 82515733Sjoerg * Tell the user what we wanna get. 82615733Sjoerg */ 82715733Sjoergstatic void 82878146Sgadusage(void) 82915733Sjoerg{ 83061913Swollman fprintf(stderr, "%s\n", 83161913Swollman"usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]\n" 83261913Swollman "\t[-Z daemon-options] [-i[numcols]] [-i[numcols]] [-1234 font]\n" 83361913Swollman "\t[-L locale] [-wnum] [-cdfghlnmprstv] [name ...]"); 83415733Sjoerg exit(1); 83515733Sjoerg} 83615733Sjoerg 83715733Sjoerg 83815733Sjoerg/* 8391553Srgrimes * Make the temp files. 8401553Srgrimes */ 8411553Srgrimesstatic void 84278146Sgadmktemps(const struct printer *pp) 8431553Srgrimes{ 8441553Srgrimes register int len, fd, n; 8451553Srgrimes register char *cp; 8461553Srgrimes char buf[BUFSIZ]; 8471553Srgrimes 84831492Swollman (void) snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir); 849241852Seadler PRIV_START 850236289Seadler if ((fd = open(buf, O_RDWR|O_CREAT, 0664)) < 0) { 85178280Sgad printf("%s: cannot create %s\n", progname, buf); 8521553Srgrimes exit(1); 8531553Srgrimes } 8541553Srgrimes if (flock(fd, LOCK_EX)) { 85578280Sgad printf("%s: cannot lock %s\n", progname, buf); 8561553Srgrimes exit(1); 8571553Srgrimes } 858241852Seadler PRIV_END 8591553Srgrimes n = 0; 8601553Srgrimes if ((len = read(fd, buf, sizeof(buf))) > 0) { 8611553Srgrimes for (cp = buf; len--; ) { 8621553Srgrimes if (*cp < '0' || *cp > '9') 8631553Srgrimes break; 8641553Srgrimes n = n * 10 + (*cp++ - '0'); 8651553Srgrimes } 8661553Srgrimes } 86778300Sgad len = strlen(pp->spool_dir) + strlen(local_host) + 8; 86831492Swollman tfname = lmktemp(pp, "tf", n, len); 86931492Swollman cfname = lmktemp(pp, "cf", n, len); 87031492Swollman dfname = lmktemp(pp, "df", n, len); 87131492Swollman inchar = strlen(pp->spool_dir) + 3; 8721553Srgrimes n = (n + 1) % 1000; 8731553Srgrimes (void) lseek(fd, (off_t)0, 0); 87419202Simp snprintf(buf, sizeof(buf), "%03d\n", n); 8751553Srgrimes (void) write(fd, buf, strlen(buf)); 8761553Srgrimes (void) close(fd); /* unlocks as well */ 8771553Srgrimes} 8781553Srgrimes 8791553Srgrimes/* 8801553Srgrimes * Make a temp file name. 8811553Srgrimes */ 8821553Srgrimesstatic char * 88378146Sgadlmktemp(const struct printer *pp, const char *id, int num, int len) 8841553Srgrimes{ 8851553Srgrimes register char *s; 8861553Srgrimes 8871553Srgrimes if ((s = malloc(len)) == NULL) 88831492Swollman errx(1, "out of memory"); 88978300Sgad (void) snprintf(s, len, "%s/%sA%03d%s", pp->spool_dir, id, num, 89078300Sgad local_host); 8911553Srgrimes return(s); 8921553Srgrimes} 893