2.9BSD/usr/src/ucb/lpr/src/lpr.c

Compare this file to the similar file:
Show the results in this format:

/*	lpr.c	4.3	81/05/19	*/
/*
 *      lpr -- off line print
 *              also known as print
 */

#include	<sys/types.h>
#include	<sys/stat.h>
#include	<signal.h>
#include	<pwd.h>
#include	<stdio.h>
#include	<ctype.h>
#include	"lp.local.h"
#include	<whoami.h>

/*
 * Multiple printer scheme using info from printer data base:
 *
 *	DN		daemon to invoke to print stuff
 *	SD		directory used as spool queue
 *
 * daemon identifies what printer it should use (in the case of the
 *  same code being shared) by inspecting its argv[1].
 */
char    *tfname;		/* tmp copy of df before linking */
char    *cfname;		/* copy files */
char    *lfname;		/* linked files */
char    *dfname;		/* daemon files, linked from tf's */

int	nact;			/* number of jobs to act on */
int	tff;			/* daemon file descriptor */
int     mailflg;		/* send mail */
int	jflag;			/* job name specified */
int	qflg;			/* q job, but don't exec daemon */
int	prflag;			/* ``pr'' files */
char	*person;		/* user name */
int	inchar;			/* location to increment char in file names */
int     ncopies = 1;		/* # of copies to make */
int	iflag;			/* indentation wanted */
int	indent;			/* amount to indent */
char	*daemname;		/* path name to daemon program */
char	*DN;			/* daemon name */
char	*SD;			/* spooling directory */
char	*LP;			/* line printer device */
int     MX;			/* max size in BUFSIZ blocks of a print file */
int	hdr = 1;		/* 1 =>'s default header */
int     user;			/* user id */
int	spgroup;		/* daemon's group for creating spool files */
char	*title;			/* pr'ing title */
char	hostname[32];		/* name of local host */
char	*class = hostname;	/* class title on header page */
char    *jobname;		/* job name on header page */
char	*name;			/* program name */
char	*printer;		/* printer name */

char	*pgetstr();
char	*mktmp();
char	*malloc();
char	*getenv();
char	*rindex();

main(argc, argv)
int argc;
char *argv[];
{
	register char *arg;
	int i, c, f, flag, out();
	char *sp;
	struct stat sbuf;

	/*
	 * Strategy to maintain protected spooling area:
	 *	1. Spooling area is writable only by daemon and spooling group
	 *	2. lpr runs setuid root and setgrp spooling group; it uses
	 *	   root to access any file it wants (verifying things before
	 *	   with an access call) and group id to know how it should
	 *	   set up ownership of files in spooling area.
	 *	3. Files in spooling area are owned by printer and spooling
	 *	   group, with mode 660.
	 *	4. lpd runs setuid daemon and setgrp spooling group to
	 *	   access files and printer.  Users can't get to anything
	 *	   w/o help of lpq and lprm programs.
	 */
	user = getuid();
	spgroup = getegid();
	if(signal(SIGHUP, SIG_IGN) != SIG_IGN)
		signal(SIGHUP, out);
	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, out);
	if(signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		signal(SIGQUIT, out);
	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
		signal(SIGTERM, out);
	flag = 0;
	if ((printer = getenv("PRINTER")) == NULL)
		printer = DEFLP;
	name = argv[0];
	gethostname(hostname, sizeof (hostname));

	while (argc>1 && (arg = argv[1])[0]=='-') {
		switch (arg[1]) {

		case 'c':		/* force copy of files */
			flag = '+';
			break;

		case 'C':		/* classification spec */
			hdr++;
			if (arg[2])
				class = &arg[2];
			else if (argc >= 2) {
				++argv;
				arg = argv[1];
				argc--;
				class = arg;
			}
			break;

		case 'r':		/* remove file when done */
			flag = '-';
			break;

		case 'm':		/* send mail when done */
			mailflg++;
			break;

		case 'q':		/* just q job */
			qflg++;
			break;

		case 'J':		/* job spec */
			jflag++, hdr++;
			if (arg[2]) {
				jobname = &arg[2];
				break;
			}
			if (argc>=2) {
				++argv;
				arg = argv[1];
				jobname = &arg[0];
				argc--;
			}
			break;

		case 'i':		/* indent output */
			iflag++;
			indent = arg[2] ? atoi(&arg[2]) : 8;
			break;

		case 'p':		/* use pr to print files */
			prflag++;
			break;

		case 'h':		/* pr's title line */
			if (arg[2])
				title = &arg[2];
			else if (argc >= 2) {
				++argv;
				arg = argv[1];
				argc--;
				title = arg;
			}
			break;

		case 'P':		/* specifiy printer name */
			printer = &arg[2];
			break;

		case 'H':		/* toggle want of header page */
			hdr = !hdr;
			break;

		default:		/* n copies ? */
			if (isdigit(arg[1]))
				ncopies = atoi(&arg[1]);
		}
		argc--;
		argv++;
	}
	if (!chkprinter(printer)) {
		fprintf(stderr, "%s: no entry for default printer %s\n", name,
			printer);
		exit(2);
	}
	i = getpid();
	f = strlen(SD)+11;
	tfname = mktmp("tf", i, f);
	cfname = mktmp("cf", i, f);
	lfname = mktmp("lf", i, f);
	dfname = mktmp("df", i, f);
	inchar = f-7;
	tff = nfile(tfname);
	if (jflag == 0) {
		if(argc == 1)
			jobname = &dfname[f-10];
		else
			jobname = argv[1];
	}
	ident();

	if(argc == 1)
		copy(0, " ");
	else while(--argc) {
		if(test(arg = *++argv) == -1)	/* file reasonable */
			continue;

		if (flag == '+')		/* force copy flag */
			goto cf;
		if (stat(arg, &sbuf) < 0) {
			printf("lpr:");
			perror(arg);
			continue;
		}
		if((sbuf.st_mode&04) == 0)
			goto cf;
		if(*arg == '/' && flag != '-') {
			for(i=0;i<ncopies;i++) {
				if (prflag) {
					if (title)
						card('H', title);
					card('R', arg);
				} else
					card('F', arg);
				card('N', arg);
			}
			nact++;
			continue;
		}
		if(link(arg, lfname) < 0)
			goto cf;
		for(i=0;i<ncopies;i++) {
			if (prflag) {
				card('H', title ? title : arg);
				card('R', lfname);
			} else 
				card('F', lfname);
			card('N', arg);
		}
		card('U', lfname);
		lfname[inchar]++;
		nact++;
		goto df;

	cf:
		if((f = open(arg, 0)) < 0) {
			printf("%s: cannot open %s\n", name, arg);
			continue;
		}
		copy(f, arg);
		close(f);

	df:
		if(flag == '-' && unlink(arg))
			printf("%s: cannot remove %s\n", name, arg);
	}

	if(nact) {
		tfname[inchar]--;
		if(link(tfname, dfname) < 0) {
			printf("%s: cannot rename %s\n", name, dfname);
			tfname[inchar]++;
			out();
		}
		unlink(tfname);
		if (qflg)		/* just q things up */
			exit(0);
		if (stat(LP, &sbuf) >= 0 && (sbuf.st_mode&0777) == 0) {
			printf("job queued, but printer down\n");
			exit(0);
		}
		for(f = 0; f < NOFILE; close(f++))
			;
		open("/dev/tty", 0);
		open("/dev/tty", 1);
		dup2(1, 2);
		execl(DN, rindex(DN, '/') ? rindex(DN, '/')+1 : DN, printer, 0);
		dfname[inchar]++;
	}
	out();
}

copy(f, n)
int f;
char n[];
{
	int ff, i, nr, nc;
	char buf[BUFSIZ];

	for(i=0;i<ncopies;i++) {
		if (prflag) {
			card('H', title ? title : n);
			card('R', cfname);
		} else 
			card('F', cfname);
		card('N', n);
	}
	card('U', cfname);
	ff = nfile(cfname);
	nr = nc = 0;
	while((i = read(f, buf, BUFSIZ)) > 0) {
		if (write(ff, buf, i) != i) {
			printf("%s: %s: temp file write error\n", name, n);
			break;
		}
		nc += i;
		if(nc >= BUFSIZ) {
			nc -= BUFSIZ;
			if(nr++ > MX) {
				printf("%s: %s: copy file is too large\n", name, n);
				break;
			}
		}
	}
	close(ff);
	nact++;
}

card(c, p2)
register char c, *p2;
{
	char buf[BUFSIZ];
	register char *p1 = buf;
	int col = 0;

	*p1++ = c;
	while((c = *p2++) != '\0') {
		*p1++ = c;
		col++;
	}
	*p1++ = '\n';
	write(tff, buf, col+2);
}

ident()
{
	extern char *getlogin();
	extern struct passwd *getpwuid();
	struct passwd *pw;
	extern char *itoa();

	if ((person = getlogin()) == NULL) {
		if ((pw = getpwuid(user)) == NULL)
			person = "Unknown User";
		else
			person = pw->pw_name;
	}

	card('J',jobname);
	card('C',class);
	card('L', person);
	if (iflag)
		card('I', itoa(indent));
	if (mailflg)
		card('M', person);
}

nfile(n)
char *n;
{
	register f;
	int oldumask = umask(022);		/* should hold signals */

	f = creat(n, FILMOD);
	(void) umask(oldumask);
	if (f < 0) {
		printf("%s: cannot create %s\n", name, n);
		out();
	}
	if (chown(n, user, spgroup) < 0) {
		unlink(n);
		printf("%s: cannot chown %s\n", name, n);
		out();
	}
	n[inchar]++;
	return(f);
}

out()
{
	register i;

	signal(SIGHUP, SIG_IGN);
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	i = inchar;
	if (tfname)
		while(tfname[i] != 'A') {
			tfname[i]--;
			unlink(tfname);
		}
	if (cfname)
		while(cfname[i] != 'A') {
			cfname[i]--;
			unlink(cfname);
		}
	if (lfname)
		while(lfname[i] != 'A') {
			lfname[i]--;
			unlink(lfname);
		}
	if (dfname)
		while(dfname[i] != 'A') {
			dfname[i]--;
			unlink(dfname);
		}
	exit();
}

test(file)
char *file;
{
	struct exec buf;
	struct stat mbuf;
	int fd;

	if (access(file, 4) < 0) {
		printf("%s: cannot access %s\n", name, file);
		return(-1);
	}
	if(stat(file, &mbuf) < 0) {
		printf("%s: cannot stat %s\n", name, file);
		return (-1);
	}
	if ((mbuf.st_mode&S_IFMT) == S_IFDIR) {
		printf("%s: %s is a directory\n", name, file);
		return(-1);
	}

	if((fd = open(file, 0)) < 0) {
		printf("%s: cannot open %s\n", name, file);
		return(-1);
	}
	if (read(fd, &buf, sizeof(buf)) == sizeof(buf))
		switch(buf.a_magic) {
		case A_MAGIC1:
		case A_MAGIC2:
		case A_MAGIC3:
#ifdef A_MAGIC4
		case A_MAGIC4:
#endif
			printf("%s: %s is an executable program", name, file);
			goto error1;

		case ARMAG:
			printf("%s: %s is an archive file", name, file);
			goto error1;
		}

	close(fd);
	return(0);
error1:
	printf(" and is unprintable\n");
	close(fd);
	return(-1);
}

/*
 * itoa - integer to string conversion
 */
char *
itoa(i)
register int i;
{
	static char b[10] = "########";
	register char *p;

	p = &b[8];
	do
		*p-- = i%10 + '0';
	while (i /= 10);
	return(++p);
}

/*
 * Perform lookup for printer name or abbreviation --
 *   return pointer to daemon structure
 */
chkprinter(s)
register char *s;
{
	static char buf[BUFSIZ/2];
	char b[BUFSIZ];
	int stat;
	char *bp = buf;

	if ((stat = pgetent(b, s)) < 0) {
		fprintf(stderr, "%s: can't open printer description file\n", name);
		exit(3);
	} else if (stat == 0)
		return(NULL);
	if ((DN = pgetstr("dn", &bp)) == NULL)
		DN = DEFDAEMON;
	if ((LP = pgetstr("lp", &bp)) == NULL)
		LP = DEFDEVLP;
	if ((SD = pgetstr("sd", &bp)) == NULL)
		SD = DEFSPOOL;
	if ((MX = pgetnum("mx")) < 0)
		MX = DEFMX;
	return(1);
}

/*
 * Make a temp file
 */
char *
mktmp(id, pid, n)
char *id;
{
	register char *s;

	if ((s = malloc(n)) == NULL) {
		fprintf(stderr, "%s: out of memory\n", name);
		exit(1);
	}
	sprintf(s, "%s/%sA%05d", SD, id, pid);
	return(s);
}