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