Subject: bin/36540: a mass of fixes for lpr
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: Greg A. Woods <woods@planix.com>
List: netbsd-bugs
Date: 06/23/2007 21:05:01
>Number:         36540
>Category:       bin
>Synopsis:       a mass of fixes for lpr
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Jun 23 21:05:00 +0000 2007
>Originator:     Greg A. Woods
>Release:        netbsd-current 2007/06/22
>Organization:
Planix, Inc.; Toronto, Ontario; Canada
>Environment:
	
	
System: NetBSD
>Description:

	these changes are mostly self-explanitory.

	the one that might need some hints is the removal of the
	dangerous UID-swapping and replacing it with safe access
	controls and checks.  For me this was mandated because in my own
	kernels there is no more functional seteuid() call, just a
	non-functional stub that prints a warning about abusers of this
	dangerous "feature".

	the EX feature was inspired by Netatalk's pap(1) interface

>How-To-Repeat:

	require a working, safe, LPR

>Fix:

cvs diff: Diffing usr.sbin/lpr
Index: usr.sbin/lpr/README.woods
===================================================================
RCS file: usr.sbin/lpr/README.woods
diff -N usr.sbin/lpr/README.woods
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.sbin/lpr/README.woods	31 May 2007 21:57:11 -0000
@@ -0,0 +1,7 @@
+Keep lpr/lpd up-to-date
+
+	cd /usr/src-current/usr.sbin/lpr
+	cvs -q update
+
+	find . -type f ! -name '.#*' -print -o -name CVS -prune | pax -rw /usr/src/usr.sbin/lpr
+	find . -type f ! -name '.#*' -print -o -name CVS -prune | pax -rw /usr/src-4/usr.sbin/lpr
cvs diff: Diffing usr.sbin/lpr/SMM.doc
Index: usr.sbin/lpr/SMM.doc/4.t
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/SMM.doc/4.t,v
retrieving revision 1.6
diff -u -r1.6 4.t
--- usr.sbin/lpr/SMM.doc/4.t	12 Jan 2006 21:17:49 -0000	1.6
+++ usr.sbin/lpr/SMM.doc/4.t	31 May 2007 21:44:38 -0000
@@ -66,7 +66,7 @@
 .DT
 lp|LA-180 DecWriter III:\e
 	:lp=/dev/lp:br#1200:fs#06320:\e
-	:tr=\ef:of=/usr/lib/lpf:lf=/usr/adm/lpd-errs:
+	:tr=\ef:of=/usr/lib/lpf:lf=/usr/log/lpd-la180:
 .DE
 The
 .B lp
@@ -89,8 +89,8 @@
 should be used for printing the files;
 more will be said about filters later.
 The last entry causes errors
-to be written to the file ``/usr/adm/lpd-errs''
-instead of the console.
+to be written to the file ``/usr/log/lpd-la180''
+instead of the default error log, ``/usr/log/lpd-errs''.
 Most errors from \fIlpd\fP are logged using
 \fIsyslogd\fP\|(8) and will not be logged in the specified file.
 The filters should use \fIsyslogd\fP to report errors; only those that
@@ -176,7 +176,7 @@
 .DT
 va|varian|Benson-Varian:\e
 	:lp=/dev/va0:sd=/var/spool/output/vad:of=/usr/lib/vpf:\e
-	:if=/usr/lib/vpf:tf=/usr/lib/rvcat:af=/usr/adm/vaacct:\e
+	:if=/usr/lib/vpf:tf=/usr/lib/rvcat:af=/usr/account/va_lpr:\e
 	:mx#2000:pl#58:px=2112:py=1700:tr=\ef:
 .DE
 .NH 2
Index: usr.sbin/lpr/SMM.doc/Makefile
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/SMM.doc/Makefile,v
retrieving revision 1.5
diff -u -r1.5 Makefile
--- usr.sbin/lpr/SMM.doc/Makefile	10 Jul 2003 10:34:48 -0000	1.5
+++ usr.sbin/lpr/SMM.doc/Makefile	31 May 2007 19:01:05 -0000
@@ -8,5 +8,7 @@
 paper.ps: ${SRCS}
 	${TOOL_SOELIM} -I${.CURDIR} ${.ALLSRC} | ${TOOL_TBL} | \
 	    ${TOOL_ROFF_PS} ${MACROS} > ${.TARGET}
+CLEANFILES +=	paper.ps
+
 
 .include <bsd.doc.mk>
cvs diff: Diffing usr.sbin/lpr/common_source
Index: usr.sbin/lpr/common_source/common.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/common_source/common.c,v
retrieving revision 1.37
diff -u -r1.37 common.c
--- usr.sbin/lpr/common_source/common.c	25 May 2006 02:53:10 -0000	1.37
+++ usr.sbin/lpr/common_source/common.c	1 Jun 2007 17:23:25 -0000
@@ -72,6 +72,7 @@
 const char	*CF;		/* name of cifplot filter (per job) */
 const char	*DF;		/* name of tex filter (per job) */
 long		 DU;		/* daeomon user-id */
+long     EX;		/* use extended command interface for filters */
 long		 FC;		/* flags to clear if lp is a tty */
 const char	*FF;		/* form feed string */
 long		 FS;		/* flags to set if lp is a tty */
@@ -92,7 +93,7 @@
 long		 PX;		/* page width in pixels */
 long		 PY;		/* page length in pixels */
 const char	*RF;		/* name of fortran text filter (per job) */
-const char	*RG;		/* resricted group */
+const char	*RG;		/* restricted group */
 const char	*RM;		/* remote machine name */
 const char	*RP;		/* remote printer name */
 long		 RS;		/* restricted to those with local accounts */
@@ -112,8 +113,6 @@
 char	line[BUFSIZ];
 int	remote;		/* true if sending files to a remote host */
 
-extern uid_t	uid, euid;
-
 static int compar(const void *, const void *);
 
 const char *
@@ -137,7 +136,7 @@
 	int error;
 	int refuse, trial;
 	char hbuf[NI_MAXSERV], *ptr;
-	const char *port = "printer";
+	const char *port = "printer";	/* XXX magic name! XXX */
 	const char *hostname = rhost;
 
 	/*
@@ -169,9 +168,7 @@
 	for (r = res; r; r = r->ai_next) {
 		trial++;
 retryport:
-		seteuid(euid);
 		s = rresvport_af(&lport, r->ai_family);
-		seteuid(uid);
 		if (s < 0)
 			return(-1);
 		if (connect(s, r->ai_addr, r->ai_addrlen) < 0) {
@@ -240,9 +237,7 @@
 	DIR *dirp;
 	u_int nitems = 0, arraysz;
 
-	seteuid(euid);
 	dirp = opendir(SD);
-	seteuid(uid);
 	if (dirp == NULL)
 		return(-1);
 	if (fstat(dirp->dd_fd, &stbuf) < 0)
@@ -260,12 +255,9 @@
 	while ((d = readdir(dirp)) != NULL) {
 		if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
 			continue;	/* daemon control files only */
-		seteuid(euid);
 		if (stat(d->d_name, &stbuf) < 0) {
-			seteuid(uid);
 			continue;	/* Doesn't exist */
 		}
-		seteuid(uid);
 		q = (struct queue *)malloc(sizeof(time_t)+strlen(d->d_name)+1);
 		if (q == NULL)
 			goto errdone;
@@ -437,11 +429,11 @@
 	int i;
 
 	if ((i = cgetent(&bp, printcapdb, pr)) == -2)
-		fatal("can't open printer description file");
+		fatal("can't open printer description file: %s: %s", *printcapdb, strerror(errno));
 	else if (i == -1)
 		fatal("unknown printer: %s", pr);
 	else if (i == -3)
-		fatal("potential reference loop detected in printcap file");
+		fatal("potential reference loop detected in printcap file: %s", *printcapdb);
 
 	LP = cgetstr(bp, DEFLP, &cp) == -1 ? _PATH_DEFDEVLP : cp;
 	RP = cgetstr(bp, "rp", &cp) == -1 ? DEFLP : cp;
@@ -451,7 +443,7 @@
 	RM = cgetstr(bp, "rm", &cp) == -1 ? NULL : cp;
 	if ((dp = checkremote()) != NULL)
 		printf("Warning: %s\n", dp);
-	LF = cgetstr(bp, "lf", &cp) == -1 ? _PATH_CONSOLE : cp;
+	LF = cgetstr(bp, "lf", &cp) == -1 ? _PATH_DEFERRLOG : cp;
 }
 
 /*
Index: usr.sbin/lpr/common_source/common_vars.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/common_source/common_vars.c,v
retrieving revision 1.4
diff -u -r1.4 common_vars.c
--- usr.sbin/lpr/common_source/common_vars.c	28 Nov 2005 03:26:06 -0000	1.4
+++ usr.sbin/lpr/common_source/common_vars.c	31 May 2007 19:01:05 -0000
@@ -46,5 +46,5 @@
 char	*printer;		/* printer name */
 char	host[MAXHOSTNAMELEN+1];	/* host machine name */
 char	*from = host;		/* client's machine name */
-const char *printcapdb[2] = { _PATH_PRINTCAP, 0 };
+const char	*printcapdb[2] = { _PATH_PRINTCAP, 0 };
 char	*bp;			/* pointer into printcap buffer. */
Index: usr.sbin/lpr/common_source/displayq.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/common_source/displayq.c,v
retrieving revision 1.32
diff -u -r1.32 displayq.c
--- usr.sbin/lpr/common_source/displayq.c	21 Mar 2006 22:47:26 -0000	1.32
+++ usr.sbin/lpr/common_source/displayq.c	31 May 2007 19:31:19 -0000
@@ -69,8 +69,6 @@
 extern char    *user[];	        /* users to process */
 extern int	users;		/* # of users in user array */
 
-extern uid_t	uid, euid;
-
 static int	col;		/* column on screen */
 static char	current[MAXPATHLEN]; /* current file being printed */
 static char	fname[MAXPATHLEN]; /* print file name */
@@ -109,23 +107,17 @@
 	 * Print out local queue
 	 * Find all the control files in the spooling directory
 	 */
-	seteuid(euid);
 	if (chdir(SD) < 0)
 		fatal("cannot chdir to spooling directory");
-	seteuid(uid);
 	if ((nitems = getq(&queue)) < 0)
 		fatal("cannot examine spooling area\n");
-	seteuid(euid);
 	ret = stat(LO, &statb);
-	seteuid(uid);
 	if (ret >= 0) {
 		if (statb.st_mode & S_IXUSR) {
 			if (remote)
 				printf("%s: ", host);
 			printf("Warning: %s is down: ", printer);
-			seteuid(euid);
 			fd = open(ST, O_RDONLY);
-			seteuid(uid);
 			if (fd >= 0) {
 				(void)flock(fd, LOCK_SH);
 				while ((i = read(fd, line, sizeof(line))) > 0)
@@ -142,9 +134,7 @@
 	}
 
 	if (nitems) {
-		seteuid(euid);
 		fp = fopen(LO, "r");
-		seteuid(uid);
 		if (fp == NULL)
 			nodaemon();
 		else {
@@ -160,9 +150,7 @@
 			if (i <= 0) {
 				ret = -1;
 			} else {
-				seteuid(euid);
 				ret = kill(i, 0);
-				seteuid(uid);
 			}
 			if (ret < 0) {
 				nodaemon();
@@ -180,9 +168,7 @@
 				 */
 				if (remote)
 					printf("%s: ", host);
-				seteuid(euid);
 				fd = open(ST, O_RDONLY);
-				seteuid(uid);
 				if (fd >= 0) {
 					(void)flock(fd, LOCK_SH);
 					while ((i = read(fd, line, sizeof(line))) > 0)
@@ -302,10 +288,8 @@
 	 * There's a chance the control file has gone away
 	 * in the meantime; if this is the case just keep going
 	 */
-	seteuid(euid);
 	if ((cfp = fopen(cf, "r")) == NULL)
 		return;
-	seteuid(uid);
 
 	if (rank < 0)
 		rank = 0;
@@ -430,10 +414,8 @@
 		printf("%s", nfile);
 		col += n+fill;
 	}
-	seteuid(euid);
 	if (*file && !stat(file, &lbuf))
 		totsize += copies * (long)lbuf.st_size;
-	seteuid(uid);
 }
 
 /*
Index: usr.sbin/lpr/common_source/lp.h
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/common_source/lp.h,v
retrieving revision 1.22
diff -u -r1.22 lp.h
--- usr.sbin/lpr/common_source/lp.h	21 Mar 2006 22:47:26 -0000	1.22
+++ usr.sbin/lpr/common_source/lp.h	31 May 2007 19:32:44 -0000
@@ -36,11 +36,14 @@
  * Global definitions for the line printer system.
  */
 
+#define LPR_OFILTER_STOP_SEQ	"\031\1" /* XXX magic "stop yourself" sequence for output filters */
+
 extern const char	*AF;	/* accounting file */
 extern long		 BR;	/* baud rate if lp is a tty */
 extern const char	*CF;	/* name of cifplot filter (per job) */
 extern const char	*DF;	/* name of tex filter (per job) */
 extern long		 DU;	/* daemon user-id */
+extern long		 EX;	/* use extended command interface for filters */
 extern long		 FC;	/* flags to clear if lp is a tty */
 extern const char	*FF;	/* form feed string */
 extern long		 FS;	/* flags to set if lp is a tty */
Index: usr.sbin/lpr/common_source/pathnames.h
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/common_source/pathnames.h,v
retrieving revision 1.8
diff -u -r1.8 pathnames.h
--- usr.sbin/lpr/common_source/pathnames.h	19 Oct 2003 05:51:44 -0000	1.8
+++ usr.sbin/lpr/common_source/pathnames.h	31 May 2007 19:01:05 -0000
@@ -34,7 +34,9 @@
 #include <paths.h>
 
 #define	_PATH_DEFDEVLP		"/dev/lp"
+#define _PATH_DEFFILTERDIR	"/usr/libexec/lpr"
 #define	_PATH_DEFSPOOL		"/var/spool/output/lpd"
+#define	_PATH_DEFERRLOG		"/var/log/lpd-errs"
 #define	_PATH_HOSTSEQUIV	"/etc/hosts.equiv"
 #define	_PATH_HOSTSLPD		"/etc/hosts.lpd"
 #define	_PATH_MASTERLOCK	"/var/run/lpd.pid"
Index: usr.sbin/lpr/common_source/rmjob.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/common_source/rmjob.c,v
retrieving revision 1.23
diff -u -r1.23 rmjob.c
--- usr.sbin/lpr/common_source/rmjob.c	20 Jan 2006 17:30:00 -0000	1.23
+++ usr.sbin/lpr/common_source/rmjob.c	31 May 2007 19:33:06 -0000
@@ -70,8 +70,6 @@
 static int	cur_daemon;		/* daemon's pid */
 static char	current[40];		/* active control file name */
 
-extern uid_t	uid, euid;		/* real and effective user id's */
-
 static	void	do_unlink(const char *);
 static	void	alarmer(int);
 
@@ -103,12 +101,10 @@
 		person = root;
 	}
 
-	seteuid(euid);
 	if (chdir(SD) < 0)
 		fatal("cannot chdir to spool directory");
 	if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
 		fatal("cannot access spool directory");
-	seteuid(uid);
 
 	if (nitems) {
 		/*
@@ -117,9 +113,7 @@
 		 *  (after which we have to restart the daemon).
 		 */
 		if (lockchk(LO) && chk(current)) {
-			seteuid(euid);
 			assasinated = kill(cur_daemon, SIGINT) == 0;
-			seteuid(uid);
 			if (!assasinated)
 				fatal("cannot kill printer daemon");
 		}
@@ -149,14 +143,12 @@
 	FILE *fp;
 	int i, n;
 
-	seteuid(euid);
 	if ((fp = fopen(s, "r")) == NULL) {
 		if (errno == EACCES)
 			fatal("can't access lock file");
 		else
 			return(0);
 	}
-	seteuid(uid);
 	if (!getline(fp)) {
 		(void)fclose(fp);
 		return(0);		/* no daemon present */
@@ -188,10 +180,8 @@
 
 	if (!chk(file))
 		return;
-	seteuid(euid);
 	if ((cfp = fopen(file, "r")) == NULL)
 		fatal("cannot open %s", file);
-	seteuid(uid);
 	while (getline(cfp)) {
 		switch (line[0]) {
 		case 'U':  /* unlink associated files */
@@ -211,9 +201,7 @@
 
 	if (from != host)
 		printf("%s: ", host);
-	seteuid(euid);
 	ret = unlink(file);
-	seteuid(uid);
 	printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
 }
 
@@ -240,10 +228,8 @@
 	/*
 	 * get the owner's name from the control file.
 	 */
-	seteuid(euid);
 	if ((cfp = fopen(file, "r")) == NULL)
 		return(0);
-	seteuid(uid);
 	while (getline(cfp)) {
 		if (line[0] == 'P')
 			break;
Index: usr.sbin/lpr/common_source/startdaemon.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/common_source/startdaemon.c,v
retrieving revision 1.15
diff -u -r1.15 startdaemon.c
--- usr.sbin/lpr/common_source/startdaemon.c	16 May 2007 20:45:45 -0000	1.15
+++ usr.sbin/lpr/common_source/startdaemon.c	31 May 2007 19:33:29 -0000
@@ -52,8 +52,6 @@
 #include "lp.h"
 #include "pathnames.h"
 
-extern uid_t	uid, euid;
-
 /*
  * Tell the printer daemon that there are new files in the spool directory.
  */
@@ -77,15 +75,12 @@
 #ifndef SUN_LEN
 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
 #endif
-	seteuid(euid);
 	if (connect(s, (const struct sockaddr *)&un,
 	    (int)SUN_LEN(&un)) < 0) {
-		seteuid(uid);
 		warn("connect");
 		(void)close(s);
 		return(0);
 	}
-	seteuid(uid);
 	n = snprintf(buf, sizeof(buf), "\1%s\n", pname);
 	if (write(s, buf, n) != n) {
 		warn("write");
cvs diff: Diffing usr.sbin/lpr/filters
Index: usr.sbin/lpr/filters/lpf.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/filters/lpf.c,v
retrieving revision 1.11
diff -u -r1.11 lpf.c
--- usr.sbin/lpr/filters/lpf.c	7 Aug 2003 11:25:26 -0000	1.11
+++ usr.sbin/lpr/filters/lpf.c	31 May 2007 19:01:05 -0000
@@ -40,6 +40,8 @@
 #endif /* not lint */
 
 /*
+ *	for use as the nroff data filter ("nf=" in the printcap)
+ * 
  * 	filter which reads the output of nroff and converts lines
  *	with ^H's to overwritten lines.  Thus this works like 'ul'
  *	but is much better: it can handle more than 2 overwrites
@@ -157,9 +159,11 @@
 
 			case '\031':
 				/*
-				 * lpd needs to use a different filter to
-				 * print data so stop what we are doing and
-				 * wait for lpd to restart us.
+				 * when lpd needs to use an input filter to
+				 * print data it sends us the magic
+				 * LPR_OFILTER_STOP_SEQ character sequence to
+				 * tell us to stop what we are doing and wait
+				 * for lpd to restart us.
 				 */
 				if ((ch = getchar()) == '\1') {
 					fflush(stdout);
cvs diff: Diffing usr.sbin/lpr/lp
cvs diff: Diffing usr.sbin/lpr/lpc
Index: usr.sbin/lpr/lpc/cmds.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpc/cmds.c,v
retrieving revision 1.19
diff -u -r1.19 cmds.c
--- usr.sbin/lpr/lpc/cmds.c	21 Mar 2006 22:49:43 -0000	1.19
+++ usr.sbin/lpr/lpc/cmds.c	1 Jun 2007 17:25:24 -0000
@@ -64,8 +64,6 @@
 #include "extern.h"
 #include "pathnames.h"
 
-extern uid_t	uid, euid;
-
 static void	abortpr(int);
 static void	cleanpr(void);
 static void	disablepr(void);
@@ -132,7 +130,6 @@
 	 * Turn on the owner execute bit of the lock file to disable printing.
 	 */
 	if (dis) {
-		seteuid(euid);
 		if (stat(line, &stbuf) >= 0) {
 			if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0)
 				printf("\tcannot disable printing\n");
@@ -173,10 +170,12 @@
 			printf("\tno daemon to abort\n");
 		else
 			printf("\tWarning: daemon (pid %d) not killed\n", pid);
-	} else
+	} else {
 		printf("\tdaemon (pid %d) killed\n", pid);
-out:
-	seteuid(uid);
+	}
+
+  out:
+	return;
 }
 
 /*
@@ -298,9 +297,7 @@
 		;
 	lp[-1] = '/';
 
-	seteuid(euid);
 	nitems = scandir(SD, &queue, doselect, sortq);
-	seteuid(uid);
 	if (nitems < 0) {
 		printf("\tcannot examine spool directory\n");
 		return;
@@ -338,12 +335,10 @@
 static void
 unlinkf(const char *name)
 {
-	seteuid(euid);
 	if (unlink(name) < 0)
 		printf("\tcannot remove %s\n", name);
 	else
 		printf("\tremoved %s\n", name);
-	seteuid(uid);
 }
 
 /*
@@ -407,14 +402,12 @@
 	/*
 	 * Turn off the group execute bit of the lock file to enable queuing.
 	 */
-	seteuid(euid);
 	if (stat(line, &stbuf) >= 0) {
 		if (chmod(line, stbuf.st_mode & 0767) < 0)
 			printf("\tcannot enable queuing\n");
 		else
 			printf("\tqueuing enabled\n");
 	}
-	seteuid(uid);
 }
 
 /*
@@ -463,7 +456,6 @@
 	/*
 	 * Turn on the group execute bit of the lock file to disable queuing.
 	 */
-	seteuid(euid);
 	if (stat(line, &stbuf) >= 0) {
 		if (chmod(line, (stbuf.st_mode & 0777) | 010) < 0)
 			printf("\tcannot disable queuing\n");
@@ -478,7 +470,6 @@
 		}
 	} else
 		printf("\tcannot stat lock file\n");
-	seteuid(uid);
 }
 
 /*
@@ -538,7 +529,6 @@
 	 * Turn on the group execute bit of the lock file to disable queuing and
 	 * turn on the owner execute bit of the lock file to disable printing.
 	 */
-	seteuid(euid);
 	if (stat(line, &stbuf) >= 0) {
 		if (chmod(line, (stbuf.st_mode & 0777) | 0110) < 0)
 			printf("\tcannot disable queuing\n");
@@ -551,7 +541,6 @@
 			(void)close(fd);
 			printf("\tprinter and queuing disabled\n");
 		}
-		seteuid(uid);
 		return;
 	} else
 		printf("\tcannot stat lock file\n");
@@ -562,10 +551,8 @@
 	fd = open(line, O_WRONLY|O_CREAT, 0664);
 	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
 		printf("\tcannot create status file\n");
-		seteuid(uid);
 		return;
 	}
-	seteuid(uid);
 	(void)ftruncate(fd, 0);
 	if (argc <= 0) {
 		(void)write(fd, "\n", 1);
@@ -677,7 +664,6 @@
 	/*
 	 * Turn off the owner execute bit of the lock file to enable printing.
 	 */
-	seteuid(euid);
 	if (ena && stat(line, &stbuf) >= 0) {
 		if (chmod(line, stbuf.st_mode & (ena == 2 ? 0666 : 0677)) < 0)
 			printf("\tcannot enable printing\n");
@@ -688,7 +674,6 @@
 		printf("\tcouldn't start daemon\n");
 	else
 		printf("\tdaemon started\n");
-	seteuid(uid);
 }
 
 /*
@@ -830,7 +815,6 @@
 	/*
 	 * Turn on the owner execute bit of the lock file to disable printing.
 	 */
-	seteuid(euid);
 	if (stat(line, &stbuf) >= 0) {
 		if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0)
 			printf("\tcannot disable printing\n");
@@ -848,7 +832,6 @@
 		}
 	} else
 		printf("\tcannot stat lock file\n");
-	seteuid(uid);
 }
 
 struct	queue **queue;
@@ -878,12 +861,10 @@
 	getcaps();
 	printf("%s:\n", printer);
 
-	seteuid(euid);
 	if (chdir(SD) < 0) {
 		printf("\tcannot chdir to %s\n", SD);
-		goto out;
+		return;
 	}
-	seteuid(uid);
 	nitems = getq(&queue);
 	if (nitems == 0)
 		return;
@@ -907,12 +888,9 @@
 	 * Turn on the public execute bit of the lock file to
 	 * get lpd to rebuild the queue after the current job.
 	 */
-	seteuid(euid);
 	if (changed && stat(LO, &stbuf) >= 0)
 		(void)chmod(LO, (stbuf.st_mode & 0777) | 01);
 
-out:
-	seteuid(uid);
 } 
 
 /*
@@ -927,9 +905,7 @@
 
 	tvp[0].tv_sec = tvp[1].tv_sec = --mtime;
 	tvp[0].tv_usec = tvp[1].tv_usec = 0;
-	seteuid(euid);
 	ret = utimes(q->q_name, tvp);
-	seteuid(uid);
 	return (ret);
 }
 
@@ -987,9 +963,7 @@
 	 * Process item consisting of owner's name (example: henry).
 	 */
 	for (qq = queue + nitems; --qq >= queue; ) {
-		seteuid(euid);
 		fp = fopen((*qq)->q_name, "r");
-		seteuid(uid);
 		if (fp == NULL)
 			continue;
 		while (getline(fp) > 0)
Index: usr.sbin/lpr/lpc/lpc.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpc/lpc.c,v
retrieving revision 1.21
diff -u -r1.21 lpc.c
--- usr.sbin/lpr/lpc/lpc.c	25 May 2006 00:21:52 -0000	1.21
+++ usr.sbin/lpr/lpc/lpc.c	31 May 2007 19:34:47 -0000
@@ -74,7 +74,6 @@
 int	margc;
 char	*margv[MAX_MARGV];
 int	top;
-uid_t	uid, euid;
 
 jmp_buf	toplevel;
 
@@ -94,9 +93,6 @@
 int
 main(int argc, char *argv[])
 {
-	euid = geteuid();
-	uid = getuid();
-	seteuid(uid);
 	setprogname(argv[0]);
 	openlog("lpd", 0, LOG_LPR);
 
cvs diff: Diffing usr.sbin/lpr/lpd
Index: usr.sbin/lpr/lpd/lpd.8
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpd/lpd.8,v
retrieving revision 1.33
diff -u -r1.33 lpd.8
--- usr.sbin/lpr/lpd/lpd.8	22 Jan 2006 21:31:17 -0000	1.33
+++ usr.sbin/lpr/lpd/lpd.8	31 May 2007 19:36:40 -0000
@@ -250,17 +250,18 @@
 Troff File.
 The file contains
 .Xr troff 1
-output (cat phototypesetter commands).
+output.
 .It n
-Ditroff File.
-The file contains device independent troff
+Nroff File.
+The file contains
+.Xr nroff 1
 output.
 .It d
 DVI File.
 The file contains
 .Tn Tex l
 output
-DVI format from Stanford.
+DVI format.
 .It g
 Graph File.
 The file contains data produced by
@@ -277,17 +278,21 @@
 The file contains text data with
 FORTRAN carriage control characters.
 .It \&1
-Troff Font R.
-Name of the font file to use instead of the default.
+Troff Font
+.Em R .
+Name of the font file to use instead of the default for position 1.
 .It \&2
-Troff Font I.
-Name of the font file to use instead of the default.
+Troff Font
+.Em I .
+Name of the font file to use instead of the default for position 2.
 .It \&3
-Troff Font B.
-Name of the font file to use instead of the default.
+Troff Font
+.Em B .
+Name of the font file to use instead of the default for position 3.
 .It \&4
-Troff Font S.
-Name of the font file to use instead of the default.
+Troff Font
+.Em S .
+Name of the font file to use instead of the default for position 4.
 .It W
 Width.
 Changes the page width (in characters) used by
@@ -307,6 +312,23 @@
 is invoked in a pipeline).
 .El
 .Pp
+If the job is in
+.Sq Ar d ,
+.Sq Ar n ,
+or
+.Sq Ar t
+format then a
+.Pa .railmag
+file will be created in the spool directory and the names of the font
+files for the four positions will be written to it, in order, one per
+line.  The
+.Sy df ,
+.Sy nf ,
+or
+.Sy tf
+filter program will read this file and ensure the specified fonts are
+available on the printer.
+.Pp
 If a file cannot be opened, a message will be logged via
 .Xr syslog 3
 using the
@@ -319,11 +341,12 @@
 .Nm
 uses
 .Xr flock 2
-to provide exclusive access to the lock file and to prevent multiple
-daemons from becoming active simultaneously.
-If the daemon should be killed or die unexpectedly, the lock file
-need not be removed.
-The lock file is kept in a readable
+to provide exclusive access to the
+.Pa lock
+.Sy ( lo )
+file and to prevent multiple daemons from becoming active
+simultaneously.  If the daemon should be killed or die unexpectedly, the
+lock file need not be removed.  The lock file is kept in a readable
 .Tn ASCII
 form and contains two lines.
 The first is the process id of the daemon and the second is the control
@@ -335,26 +358,62 @@
 and
 .Xr lprm 1 .
 .Sh FILES
-.Bl -tag -width "/var/spool/output/*/minfree" -compact
+.Bl -tag -width "/var/spool/output/*/.railmag" -compact
 .It Pa /etc/printcap
-printer description file
-.It Pa /var/spool/output/*
-spool directories
-.It Pa /var/spool/output/*/minfree
-minimum free space to leave
-.It Pa /dev/lp*
-line printer devices
-.It Pa /var/run/printer
-socket for local requests
+printer queue description/capabilities configuration file
 .It Pa /etc/hosts.allow
 explicit remote host access list.
 .It Pa /etc/hosts.deny
 explicit remote host denial of service list.
 .It Pa /etc/hosts.equiv
-lists machine names allowed printer access
+lists machine names allowed printer access.
 .It Pa /etc/hosts.lpd
 lists machine names allowed printer access,
 but not under same administrative control.
+.It Pa /usr/libexec/lpr/
+Default location of filter programs.
+.It Pa /usr/libexec/lpr/lpf
+Line printer filter for
+.Xr nroff
+output.
+.It Pa /usr/libdata/vfont/*
+Default location of font files.
+.It Pa /var/spool/output/*
+Directories used for spooling (specified by
+.Sy sd ) .
+.It Pa /var/spool/output/lpd
+Default spool
+.Sy ( sd )
+directory.
+.It Pa /var/spool/output/*/.railmag
+Font specification file for
+.Sy df
+.Sy nf ,
+or
+.Sy tf
+filters.
+.It Pa /var/spool/output/*/cf*
+Daemon control files.
+.It Pa /var/spool/output/*/df*
+Data files specified in the ``cf'' files.
+.It Pa /var/spool/output/*/errs*
+Temporary filter error output files.
+.It Pa /var/spool/output/*/lock
+Default name of the spool lock
+.Sy ( sf )
+file.
+.It Pa /var/spool/output/*/minfree
+minimum free space to leave on device holding spool directory
+.It Pa /var/spool/output/*/tf*
+Temporary copies of the ``cf'' files.
+.It Pa /var/spool/output/*/status
+Default name of the status
+.Sy ( sf )
+file.
+.It Pa /dev/lp*
+line printer devices
+.It Pa /var/run/printer
+socket for local requests
 .El
 .Sh SEE ALSO
 .Xr lpq 1 ,
@@ -375,3 +434,15 @@
 An
 .Nm
 daemon appeared in Version 6 AT\*[Am]T UNIX.
+.Sh BUGS
+Use of the
+.Fl W
+flag, or the ability of any trusted remote client to use a reserved
+port, precludes any meaningful use the
+.Cm rs
+attribute in a printer description.
+.Pp
+.Nm
+does not pay attention to the
+.Cm rg
+attribute when authorizing remote users.
Index: usr.sbin/lpr/lpd/lpd.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpd/lpd.c,v
retrieving revision 1.54
diff -u -r1.54 lpd.c
--- usr.sbin/lpr/lpd/lpd.c	18 Jan 2006 23:17:38 -0000	1.54
+++ usr.sbin/lpr/lpd/lpd.c	1 Jun 2007 17:57:02 -0000
@@ -134,7 +134,6 @@
 static void		usage(void);
 static struct pollfd	*socksetup(int, int, const char *, int *);
 
-uid_t	uid, euid;
 int child_count;
 
 #define LPD_NOPORTCHK	0001		/* skip reserved-port check */
@@ -153,14 +152,12 @@
 	const char *port = "printer";
 	char **newblist;
 
-	euid = geteuid();	/* these shouldn't be different */
-	uid = getuid();
 	gethostname(host, sizeof(host));
 	host[sizeof(host) - 1] = '\0';
 	setprogname(*argv);
 
 	errs = 0;
-	while ((i = getopt(argc, argv, "b:dln:srw:W")) != -1)
+	while ((i = getopt(argc, argv, "b:cdln:srw:W")) != -1)
 		switch (i) {
 		case 'b':
 			if (blist_addrs >= blist_size) {
@@ -409,11 +406,17 @@
 	for (;;) {
 		cp = cbuf;
 		do {
-			if (cp >= &cbuf[sizeof(cbuf) - 1])
+			if (cp >= &cbuf[sizeof(cbuf) - 1]) {
+				if (lflag)
+					syslog(LOG_ERR, "command line too long from %s", from);
 				fatal("Command line too long");
+			}
 			if ((n = read(STDOUT_FILENO, cp, 1)) != 1) {
-				if (n < 0)
-					fatal("Lost connection");
+				if (n < 0) {
+					if (lflag)
+						syslog(LOG_ERR, "lost connection from %s: %m", from);
+					return;
+				}
 				return;
 			}
 		} while (*cp++ != '\n');
@@ -427,25 +430,28 @@
 				    cmdnames[(int)*cp], cp+1);
 			}
 			else
-				syslog(LOG_INFO, "bad request (%d) from %s",
+				syslog(LOG_INFO, "bad request code (%d) from %s",
 					*cp, from);
 		}
+		setproctitle("serving %s: %s %s", from, cmdnames[(int)*cp], cp+1);
 		switch (*cp++) {
 		case '\1':	/* check the queue and print any jobs there */
 			printer = cp;
 			if (*printer == '\0')
 				printer = DEFLP;
 			printjob();
+			/* NOTREACHED */
 			break;
 		case '\2':	/* receive files to be queued */
 			if (!from_remote) {
-				syslog(LOG_INFO, "illegal request (%d)", *cp);
+				syslog(LOG_INFO, "invalid receive request code (%d) from %s", *cp, from);
 				exit(1);
 			}
 			printer = cp;
 			if (*printer == '\0')
 				printer = DEFLP;
 			recvjob();
+			/* NOTREACHED */
 			break;
 		case '\3':	/* display the queue (short form) */
 		case '\4':	/* display the queue (long form) */
@@ -463,20 +469,27 @@
 				if (*cp == '\0')
 					break;
 				if (isdigit((unsigned char)*cp)) {
-					if (requests >= MAXREQUESTS)
+					if (requests >= MAXREQUESTS) {
+						if (lflag)
+							syslog(LOG_ERR, "too many requests to add from %s: %d", from, requests);
 						fatal("Too many requests");
+					}
 					requ[requests++] = atoi(cp);
 				} else {
-					if (users >= MAXUSERS)
+					if (users >= MAXUSERS) {
+						if (lflag)
+							syslog(LOG_ERR, "too many users to add from %s: %d", from, users);
 						fatal("Too many users");
+					}
 					user[users++] = cp;
 				}
 			}
 			displayq(cbuf[0] - '\3');
 			exit(0);
+			/* NOTREACHED */
 		case '\5':	/* remove a job from the queue */
 			if (!from_remote) {
-				syslog(LOG_INFO, "illegal request (%d)", *cp);
+				syslog(LOG_INFO, "invalid remove request code (%d) from %s", *cp, from);
 				exit(1);
 			}
 			printer = cp;
@@ -499,19 +512,28 @@
 				if (*cp == '\0')
 					break;
 				if (isdigit((unsigned char)*cp)) {
-					if (requests >= MAXREQUESTS)
+					if (requests >= MAXREQUESTS) {
+						if (lflag)
+							syslog(LOG_ERR, "too many requests to remove from %s: %d", from, requests);
 						fatal("Too many requests");
+					}
 					requ[requests++] = atoi(cp);
 				} else {
-					if (users >= MAXUSERS)
+					if (users >= MAXUSERS) {
+						if (lflag)
+							syslog(LOG_ERR, "too many users to remove from %s: %d", from, users);
 						fatal("Too many users");
+					}
 					user[users++] = cp;
 				}
 			}
 			rmjob();
+			/* NOTREACHED */
 			break;
 		}
-		fatal("Illegal service request");
+		if (lflag)
+			syslog(LOG_ERR, "invalid service request code (%d) from %s", (int) *--cp, from);
+		fatal("Invalid service request");
 	}
 }
 
@@ -539,7 +561,7 @@
 				break;
 			}
 		if (lflag)
-			syslog(LOG_INFO, "work for %s", buf);
+			syslog(LOG_INFO, "found existing jobs for %s", buf);
 		switch (fork()) {
 		case -1:
 			syslog(LOG_WARNING, "startup: cannot fork");
@@ -577,23 +599,35 @@
 
 	error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
 			    NI_NUMERICSERV);
-	if (error)
-		fatal("Malformed from address: %s", gai_strerror(error));
+	if (error) {
+		if (lflag)
+			syslog(LOG_ERR, "error resolving source port: %s", gai_strerror(error));
+		fatal("Malformed source address or port: %s", gai_strerror(error));
+	}
 
-         if (!(check_opts & LPD_NOPORTCHK) &&
-	       atoi(serv) >= IPPORT_RESERVED)
+	if (!(check_opts & LPD_NOPORTCHK) &&
+	    atoi(serv) >= IPPORT_RESERVED) {
+		if (lflag)
+			syslog(LOG_ERR, "Connect from invalid source port: %s", serv);
 		fatal("Connect from invalid port (%s)", serv);
+	}
 
 	/* Need real hostname for temporary filenames */
 	error = getnameinfo(f, f->sa_len, hst, sizeof(hst), NULL, 0,
 			    NI_NAMEREQD);
 	if (error) {
-		error = getnameinfo(f, f->sa_len, hst, sizeof(hst), NULL, 0,
-				    NI_NUMERICHOST);
-		if (error)
+		int nerror = getnameinfo(f, f->sa_len, hst, sizeof(hst), NULL, 0,
+					 NI_NUMERICHOST);
+		if (nerror) {
+			/* this should be almost impossible.... */
+			if (lflag)
+				syslog(LOG_ERR, "error resolving source address: %s", gai_strerror(nerror));
 			fatal("Host name for your address unknown");
-		else
+		} else {
+			if (lflag)
+				syslog(LOG_ERR, "error resolving source address for %s: %s", host, gai_strerror(error));
 			fatal("Host name for your address (%s) unknown", hst);
+		}
 	}
 
 	(void)strlcpy(fromb, hst, sizeof(fromb));
@@ -602,8 +636,12 @@
 	/* need address in stringform for comparison (no DNS lookup here) */
 	error = getnameinfo(f, f->sa_len, hst, sizeof(hst), NULL, 0,
 			    NI_NUMERICHOST);
-	if (error)
-		fatal("Cannot print address");
+	if (error) {
+		/* this should be almost impossible.... */
+		if (lflag)
+			syslog(LOG_ERR, "error converting source address %s: %s", from, gai_strerror(error));
+		fatal("Cannot format printable address for %s: %s", from, gai_strerror(error));
+	}
 
 	/* Check for spoof, ala rlogind */
 	memset(&hints, 0, sizeof(hints));
@@ -611,6 +649,8 @@
 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
 	error = getaddrinfo(fromb, NULL, &hints, &res);
 	if (error) {
+		if (lflag)
+			syslog(LOG_ERR, "hostname for address (%s) unknown: %s", from, gai_strerror(error));
 		fatal("hostname for your address (%s) unknown: %s", hst,
 		    gai_strerror(error));
 	}
@@ -623,9 +663,11 @@
 	}
 	if (res)
 		freeaddrinfo(res);
-	if (good == 0)
+	if (good == 0) {
+		if (lflag)
+			syslog(LOG_ERR, "address for hostname (%s) not matched", host);
 		fatal("address for your hostname (%s) not matched", hst);
-
+	}
 	setproctitle("serving %s", from);
 
 #ifdef LIBWRAP
@@ -655,6 +697,8 @@
 #ifdef LIBWRAP
   denied:
 #endif
+	if (lflag)
+		syslog(LOG_ERR, "rejected access from %s", host);
 	fatal("Your host does not have line printer access");
 	/*NOTREACHED*/
 }
@@ -692,7 +736,7 @@
 
 	s = socket(AF_LOCAL, SOCK_STREAM, 0);
 	if (s < 0) {
-		syslog(LOG_ERR, "socket(): %m");
+		syslog(LOG_ERR, "socket(AF_LOCAL, SOCK_STREAM, 0): %m");
 		exit(1);
 	}
 	memset(&un, 0, sizeof(un));
@@ -702,7 +746,7 @@
 	(void)umask(07);
 	(void)unlink(_PATH_SOCKETNAME);
 	if (bind(s, (struct sockaddr *)&un, un.sun_len) < 0) {
-		syslog(LOG_ERR, "bind(): %m");
+		syslog(LOG_ERR, "bind(AF_LOCAL, %s): %m", _PATH_SOCKETNAME);
 		exit(1);
 	}
 	(void)umask(0);
@@ -720,13 +764,13 @@
 		hints.ai_family = af;
 		hints.ai_socktype = SOCK_STREAM;
 		error = getaddrinfo((blist_addrs == 0) ? NULL : blist[blidx],
-		    port ? port : "printer", &hints, &res);
+		    port ? port : "printer", &hints, &res); /* XXX magic name */
 		if (error) {
 			if (blist_addrs)
-				syslog(LOG_ERR, "%s: %s", blist[blidx],
+				syslog(LOG_ERR, "getaddrinfo(%s): %s", blist[blidx],
 				    gai_strerror(error));
 			else
-				syslog(LOG_ERR, "%s", gai_strerror(error));
+				syslog(LOG_ERR, "getaddrinfo(*): %s", gai_strerror(error));
 			mcleanup(0);
 		}
 
@@ -744,33 +788,33 @@
 			s = socket(r->ai_family, r->ai_socktype,
 			    r->ai_protocol);
 			if (s < 0) {
-				syslog(LOG_DEBUG, "socket(): %m");
+				syslog(LOG_DEBUG, "socket(): %m"); /* XXX this _really_ needs more info!!! */
 				continue;
 			}
 			if (options & SO_DEBUG)
 				if (setsockopt(s, SOL_SOCKET, SO_DEBUG,
 					       &on, sizeof(on)) < 0) {
 					syslog(LOG_ERR,
-					       "setsockopt (SO_DEBUG): %m");
+					       "setsockopt(SO_DEBUG): %m"); /* XXX this _really_ needs more info!!! */
 					close(s);
 					continue;
 				}
 			if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &on,
 			    sizeof(on)) < 0) {
 				syslog(LOG_ERR,
-				    "setsockopt (SO_REUSEPORT): %m");
+				    "setsockopt(SO_REUSEPORT): %m"); /* XXX this _really_ needs more info!!! */
 				close(s);
 				continue;
 			}
 			if (r->ai_family == AF_INET6 && setsockopt(s,
 			    IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
 				syslog(LOG_ERR,
-				    "setsockopt (IPV6_V6ONLY): %m");
+				    "setsockopt(IPV6_V6ONLY): %m"); /* XXX this _really_ needs more info!!! */
 				close(s);
 				continue;
 			}
 			if (bind(s, r->ai_addr, r->ai_addrlen) < 0) {
-				syslog(LOG_DEBUG, "bind(): %m");
+				syslog(LOG_DEBUG, "bind(): %m"); /* XXX this _really_ needs more info!!! */
 				close(s);
 				continue;
 			}
Index: usr.sbin/lpr/lpd/printjob.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpd/printjob.c,v
retrieving revision 1.50
diff -u -r1.50 printjob.c
--- usr.sbin/lpr/lpd/printjob.c	11 May 2006 00:22:53 -0000	1.50
+++ usr.sbin/lpr/lpd/printjob.c	1 Jun 2007 17:54:57 -0000
@@ -55,6 +55,7 @@
 
 #include <sys/param.h>
 #include <sys/wait.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/file.h>
@@ -68,6 +69,7 @@
 #include <fcntl.h>
 #include <dirent.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -181,20 +183,22 @@
 	 */
 	if (chdir(SD) < 0) {
 		syslog(LOG_ERR, "%s: %m", SD);
-		exit(1);
+		return;
+	}
+	if (stat(LO, &stb) == 0 && (stb.st_mode & S_IXUSR)) {
+		syslog(LOG_DEBUG,  "%s: printing is disabled (no S_IXUSR on %s)", printer, LO);
+		return;		/* printing disabled */
 	}
-	if (stat(LO, &stb) == 0 && (stb.st_mode & S_IXUSR))
-		exit(0);		/* printing disabled */
 	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
 	if (lfd < 0) {
 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
-		exit(1);
+		return;
 	}
 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
 		if (errno == EWOULDBLOCK)	/* active daemon present */
 			exit(0);
 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
-		exit(1);
+		return;
 	}
 	ftruncate(lfd, 0);
 	/*
@@ -203,17 +207,19 @@
 	pidoff = i = snprintf(line, sizeof(line), "%u\n", pid);
 	if (write(lfd, line, i) != i) {
 		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
-		exit(1);
+		return;
 	}
 	/*
 	 * search the spool directory for work and sort by queue order.
 	 */
 	if ((nitems = getq(&queue)) < 0) {
 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
-		exit(1);
+		return;
+	}
+	if (nitems == 0) {		/* no work to do */
+		syslog(LOG_DEBUG, "%s: no work found in %s", printer, SD);
+		return;
 	}
-	if (nitems == 0)		/* no work to do */
-		exit(0);
 	if (stb.st_mode & S_IXOTH) {	/* reset queue flag */
 		stb.st_mode &= ~S_IXOTH;
 		if (fchmod(lfd, stb.st_mode & 0777) < 0)
@@ -291,9 +297,12 @@
 	 */
 	if ((nitems = getq(&queue)) < 0) {
 		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
-		exit(1);
+		return;
 	}
 	if (nitems == 0) {		/* no more work to do */
+		fd_set input;
+		struct timeval timeout;
+
 	done:
 		if (count > 0) {	/* Files actually printed */
 			if (!SF && !tof)
@@ -303,15 +312,39 @@
 		}
 		(void)unlink(tempfile);
 		(void)unlink(tempremote);
-		exit(0);
+
+		/* ideas from PR#28593 */
+		shutdown(pfd, SHUT_WR);	/* no more writes to the printer */
+
+		timeout.tv_sec  = 90;
+		timeout.tv_usec = 0;
+		FD_ZERO(&input);
+		FD_SET(pfd, &input);
+		if (select(pfd + 1, &input, (fd_set *) NULL, (fd_set *) NULL, &timeout) > 0) {
+			char buf[BUFSIZ];
+			int rlen;
+
+			if ((rlen = recv(pfd, buf, sizeof(buf), 0)) > 0) {
+				if (rlen >= sizeof(buf))
+					rlen = sizeof(buf) - 1;
+				buf[rlen] = '\0';
+				/*
+				 * XXX this should (also) be mailed back to the user....
+				 */
+				syslog(LOG_WARNING, "Printer reply: '%s'\n", buf);
+			}
+		}
+		(void) close(pfd);
+
+		return;
 	}
 	goto again;
 }
 
-#define FONTLEN	50
+#define FONTLEN	PATH_MAX
 char	fonts[4][FONTLEN];	/* fonts for troff */
 
-char ifonts[4][40] = {
+char ifonts[4][FONTLEN] = {
 	_PATH_VFONTR,
 	_PATH_VFONTI,
 	_PATH_VFONTB,
@@ -346,41 +379,42 @@
 	indent[3] = '\0';
 
 	/*
-	 *      read the control file for work to do
+	 *	read the control file for work to do
 	 *
-	 *      file format -- first character in the line is a command
-	 *      rest of the line is the argument.
-	 *      valid commands are:
+	 *	file format -- first character in the line is a command
+	 *	rest of the line is the argument.
+	 *	valid commands are:
 	 *
 	 *		S -- "stat info" for symbolic link protection
 	 *		J -- "job name" on banner page
 	 *		C -- "class name" on banner page
-	 *              L -- "literal" user's name to print on banner
+	 *		L -- "literal" user's name to print on banner
 	 *		T -- "title" for pr
 	 *		H -- "host name" of machine where lpr was done
-	 *              P -- "person" user's login name
-	 *              I -- "indent" amount to indent output
+	 *		P -- "person" user's login name
+	 *		I -- "indent" amount to indent output
 	 *		R -- laser dpi "resolution"
-	 *              f -- "file name" name of text file to print
+	 *		f -- "file name" name of text file to print
 	 *		l -- "file name" text file with control chars
+	 *		o -- "file name" PostScript file to print
 	 *		p -- "file name" text file to print with pr(1)
-	 *		t -- "file name" troff(1) file to print
-	 *		n -- "file name" ditroff(1) file to print
-	 *		d -- "file name" dvi file to print
-	 *		g -- "file name" plot(1G) file to print
-	 *		v -- "file name" plain raster file to print
-	 *		c -- "file name" cifplot file to print
-	 *		o -- "file name" postscript file to print
-	 *		1 -- "R font file" for troff
-	 *		2 -- "I font file" for troff
-	 *		3 -- "B font file" for troff
-	 *		4 -- "S font file" for troff
+	 *		t -- "file name" troff(1) file to print (tf)
+	 *		n -- "file name" nroff(1) file to print (nf)
+	 *		d -- "file name" DVI (TeX) file to print (df)
+	 *		g -- "file name" plot(1G) file to print (gf)
+	 *		v -- "file name" plain raster file to print (vf)
+	 *		c -- "file name" cifplot file to print (cf)
+	 *		r -- "file name" FORTRAN file to print (rf)
+	 *		1 -- "R font file" for troff/nroff/tex
+	 *		2 -- "I font file" for troff/nroff/tex
+	 *		3 -- "B font file" for troff/nroff/tex
+	 *		4 -- "S font file" for troff/nroff/tex
 	 *		N -- "name" of file (used by lpq)
-	 *              U -- "unlink" name of file to remove
-	 *                    (after we print it. (Pass 2 only)).
+	 *		U -- "unlink" name of file to remove
+	 *		     (after we print it. (Pass 2 only)).
 	 *		M -- "mail" to user when done printing
 	 *
-	 *      getline reads a line and expands tabs to blanks
+	 *	getline reads a line and expands tabs to blanks
 	 */
 
 	/* pass 1 */
@@ -517,7 +551,7 @@
 
 /*
  * Print a file.
- * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
+ * Set up the chain [ PR | PF [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
  * Return -1 if a non-recoverable error occurred,
  * 2 if the filter detected some errors (but printed the job anyway),
  * 1 if we should try to reprint this job and
@@ -531,7 +565,8 @@
 	FILE *fp;
 	int status;
 	struct stat stb;
-	const char *prog, *av[15];
+	char *prog;
+	const char *av[21];
 	char buf[BUFSIZ];
 	int n, fi, fo, child_pid, p[2], stopped = 0, nofile;
 
@@ -560,9 +595,9 @@
 		return(OK);
 	}
 	switch (format) {
-	case 'p':	/* print file using 'pr' */
+	case 'p':	/* print file using 'pr' XXX this must go away! XXX */
 		if (IF == NULL) {	/* use output filter */
-			prog = _PATH_PR;
+			prog = strdup(_PATH_PR);
 			av[0] = "pr";
 			av[1] = width;
 			av[2] = length;
@@ -574,6 +609,8 @@
 		}
 		pipe(p);
 		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
+			int oerrno;
+
 			dup2(fi, 0);		/* file is stdin */
 			dup2(p[1], 1);		/* pipe is stdout */
 			closelog();
@@ -582,8 +619,10 @@
 				(void)close(n);
 			execl(_PATH_PR, "pr", width, length,
 			    "-h", *title ? title : " ", NULL);
-			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
-			exit(2);
+			oerrno = errno;
+			openlog("lpd", LOG_PID, LOG_LPR);
+			syslog(LOG_ERR, "cannot execl %s: %s", _PATH_PR, strerror(oerrno));
+			exit(2);	/* XXX return code is ignored */
 		}
 		(void)close(p[1]);		/* close output side */
 		(void)close(fi);
@@ -594,7 +633,7 @@
 		}
 		fi = p[0];			/* use pipe for input */
 	case 'f':	/* print plain text file */
-		prog = IF;
+		prog = strdup(IF);
 		av[1] = width;
 		av[2] = length;
 		av[3] = indent;
@@ -603,7 +642,7 @@
 	case 'o':	/* print a postscript file */
 		if (PF == NULL) {
 			/* if PF is not set, handle it like an 'l' */
-			prog = IF;
+			prog = strdup(IF);
 			av[1] = "-c";
 			av[2] = width;
 			av[3] = length;
@@ -611,14 +650,14 @@
 			n = 5;
 			break;
 		} else {
-			prog = PF;
+			prog = strdup(PF);
 			av[1] = pxwidth;
 			av[2] = pxlength;
 			n = 3;
 			break;
 		}
 	case 'l':	/* like 'f' but pass control characters */
-		prog = IF;
+		prog = strdup(IF);
 		av[1] = "-c";
 		av[2] = width;
 		av[3] = length;
@@ -626,13 +665,13 @@
 		n = 5;
 		break;
 	case 'r':	/* print a fortran text file */
-		prog = RF;
+		prog = strdup(RF);
 		av[1] = width;
 		av[2] = length;
 		n = 3;
 		break;
 	case 't':	/* print troff output */
-	case 'n':	/* print ditroff output */
+	case 'n':	/* print nroff output */
 	case 'd':	/* print tex output */
 		(void)unlink(".railmag");
 		if ((fo = creat(".railmag", FILMOD)) < 0) {
@@ -648,25 +687,25 @@
 			}
 			(void)close(fo);
 		}
-		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
+		prog = strdup((format == 't') ? TF : (format == 'n') ? NF : DF);
 		av[1] = pxwidth;
 		av[2] = pxlength;
 		n = 3;
 		break;
 	case 'c':	/* print cifplot output */
-		prog = CF;
+		prog = strdup(CF);
 		av[1] = pxwidth;
 		av[2] = pxlength;
 		n = 3;
 		break;
 	case 'g':	/* print plot(1G) output */
-		prog = GF;
+		prog = strdup(GF);
 		av[1] = pxwidth;
 		av[2] = pxlength;
 		n = 3;
 		break;
 	case 'v':	/* print raster output */
-		prog = VF;
+		prog = strdup(VF);
 		av[1] = pxwidth;
 		av[2] = pxlength;
 		n = 3;
@@ -688,6 +727,20 @@
 		av[0]++;
 	else
 		av[0] = prog;
+	if (*prog != '/') {
+		if (asprintf(&prog, "%s/%s", _PATH_DEFFILTERDIR, prog) == -1) {
+			syslog(LOG_ERR, "%s: asprintf(): %m", printer);
+			return (REPRINT);
+		}
+	}
+	if (EX) {
+		av[n++] = "-j";
+		av[n++] = jobname;
+		av[n++] = "-p";
+		av[n++] = printer;
+		av[n++] = "-s";
+		av[n++] = ST;
+	}
 	av[n++] = "-n";
 	av[n++] = logname;
 	av[n++] = "-h";
@@ -696,15 +749,15 @@
 	av[n] = 0;
 	fo = pfd;
 	if (ofilter > 0) {		/* stop output filter */
-		write(ofd, "\031\1", 2);
+		write(ofd, LPR_OFILTER_STOP_SEQ, 2);
 		while ((child_pid =
 		    wait3(&status, WUNTRACED, 0)) > 0 && child_pid != ofilter)
 			;
 		if (WIFSTOPPED(status) == 0) {
 			(void)close(fi);
 			syslog(LOG_WARNING,
-			    "%s: output filter died (retcode=%d termsig=%d)",
-				printer, WEXITSTATUS(status), WTERMSIG(status));
+			    "%s: output filter died (retcode=%d, pid=%d, termsig=%d)",
+				printer, WEXITSTATUS(status), (int) pid, WTERMSIG(status));
 			return(REPRINT);
 		}
 		stopped++;
@@ -722,6 +775,7 @@
 		for (n = 3; n < nofile; n++)
 			(void)close(n);
 		execv(prog, __UNCONST(av));
+		openlog("lpd", LOG_PID, LOG_LPR);
 		syslog(LOG_ERR, "cannot execv %s", prog);
 		exit(2);
 	}
@@ -737,10 +791,10 @@
 		;
 	child = 0;
 	prchild = 0;
-	if (stopped) {		/* restart output filter */
+	if (stopped) {		/* restart the output filter */
 		if (kill(ofilter, SIGCONT) < 0) {
 			syslog(LOG_ERR, "cannot restart output filter");
-			exit(1);
+			return(FILTERERR);
 		}
 	}
 	tof = 0;
@@ -766,11 +820,12 @@
 		return(REPRINT);
 	case 2:
 		return(ERROR);
-	default:
-		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
-			printer, format, WEXITSTATUS(status));
-		return(FILTERERR);
 	}
+
+	syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
+		printer, format, WEXITSTATUS(status));
+
+	return(FILTERERR);
 }
 
 /*
@@ -1114,6 +1169,8 @@
 		return;
 	pipe(p);
 	if ((s = dofork(DORETURN)) == 0) {		/* child */
+		int oerrno;
+
 		dup2(p[0], 0);
 		closelog();
 		nofile = sysconf(_SC_OPEN_MAX);
@@ -1124,6 +1181,9 @@
 		else
 			cp = _PATH_SENDMAIL;
 		execl(_PATH_SENDMAIL, cp, "-t", NULL);
+		oerrno = errno;
+		openlog("lpd", LOG_PID, LOG_LPR);                                                                    
+		syslog(LOG_ERR, "cannot execl %s: %s", _PATH_SENDMAIL, strerror(oerrno));
 		_exit(0);
 	} else if (s > 0) {				/* parent */
 		dup2(p[1], 1);
@@ -1142,14 +1202,14 @@
 		default:
 		case FATALERR:
 			printf("\ncould not be printed\n");
-			cp = "FATALERR";
+			cp = "FATAL ERROR";
 			break;
 		case NOACCT:
 			printf("\ncould not be printed without an account on %s\n", host);
-			cp = "NOACCT";
+			cp = "RESTRICTED(rg|rs) - NO ACCOUNT";
 			break;
 		case FILTERERR:
-			cp = "FILTERERR";
+			cp = "FILTER ERROR";
 			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
 			    (fp = fopen(tempfile, "r")) == NULL) {
 				printf("\nhad some errors and may not have printed\n");
@@ -1162,7 +1222,7 @@
 			break;
 		case ACCESS:
 			printf("\nwas not printed because it was not linked to the original file\n");
-			cp = "ACCESS";
+			cp = "ACCESS DENIED - Symlink problem";
 		}
 		fflush(stdout);
 		(void)close(1);
@@ -1199,7 +1259,7 @@
 		if (child_pid == 0) {
 			pw = getpwuid(DU);
 			if (pw == 0) {
-				syslog(LOG_ERR, "uid %ld not in password file",
+				syslog(LOG_ERR, "default UID %ld not in password file",
 				    DU);
 				break;
 			}
@@ -1283,6 +1343,7 @@
 	SB = (cgetcap(bp, "sb", ':') != NULL);
 	HL = (cgetcap(bp, "hl", ':') != NULL);
 	RW = (cgetcap(bp, "rw", ':') != NULL);
+	EX = (cgetcap(bp, "ex", ':') != NULL);
 
 	cgetnum(bp, "br", &BR);
 	if (cgetnum(bp, "fc", &FC) < 0)
@@ -1314,6 +1375,8 @@
 
 		pipe(p);
 		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
+			int oerrno;
+
 			dup2(p[0], 0);		/* pipe is std in */
 			dup2(pfd, 1);		/* printer is std out */
 			closelog();
@@ -1325,7 +1388,9 @@
 			else
 				cp++;
 			execl(OF, cp, width, length, NULL);
-			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
+			oerrno = errno;
+			openlog("lpd", LOG_PID, LOG_LPR);
+			syslog(LOG_ERR, "%s: %s: %s", printer, OF, strerror(oerrno));
 			exit(1);
 		}
 		(void)close(p[0]);		/* close input side */
@@ -1345,7 +1410,7 @@
 	int i;
 
 	if (ofilter) {
-		kill(ofilter, SIGCONT);	/* to be sure */
+		kill(ofilter, SIGCONT);	/* just to be sure */
 		(void)close(ofd);
 		ofd = pfd;
 		while ((i = wait(NULL)) > 0 && i != ofilter)
Index: usr.sbin/lpr/lpd/recvjob.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpd/recvjob.c,v
retrieving revision 1.20
diff -u -r1.20 recvjob.c
--- usr.sbin/lpr/lpd/recvjob.c	28 Nov 2005 03:26:06 -0000	1.20
+++ usr.sbin/lpr/lpd/recvjob.c	1 Jun 2007 17:17:51 -0000
@@ -99,10 +99,10 @@
 		(void) dup2(fd, STDERR_FILENO);
 		(void) close(fd);
 	} else
-		(void) close(STDERR_FILENO);
+		(void) close(STDERR_FILENO); /* XXX this doesn't seem right... */
 
 	if (chdir(SD) < 0)
-		frecverr("%s: %s: %m", printer, SD);
+		frecverr("%s: chdir(%s): %m", printer, SD);
 	if (stat(LO, &stb) == 0) {
 		if (stb.st_mode & 010) {
 			/* queue is disabled */
@@ -110,13 +110,13 @@
 			exit(1);
 		}
 	} else if (stat(SD, &stb) < 0)
-		frecverr("%s: %s: %m", printer, SD);
+		frecverr("%s: stat(%s): %m", printer, SD);
 	minfree = 2 * read_number("minfree");	/* scale KB to 512 blocks */
 	signal(SIGTERM, rcleanup);
 	signal(SIGPIPE, rcleanup);
 
 	if (readjob())
-		printjob();
+		printjob();		/* XXX error handling! */
 }
 
 /*
@@ -139,13 +139,12 @@
 		do {
 			if ((size = read(STDOUT_FILENO, cp, 1)) != 1) {
 				if (size < 0)
-					frecverr("%s: Lost connection",
-					    printer);
+					frecverr("readjob: %s: Lost connection: %m", printer);
 				return(nfiles);
 			}
 		} while (*cp++ != '\n' && (cp - line + 1) < sizeof(line));
 		if (cp - line + 1 >= sizeof(line))
-			frecverr("readjob overflow");
+			frecverr("readjob: overflow");
 		*--cp = '\0';
 		cp = line;
 		switch (*cp++) {
@@ -168,7 +167,7 @@
  			(void)strlcpy(cp + 6, from,
 			    sizeof(line) + line - cp - 6);
 			if (strchr(cp, '/'))
-				frecverr("readjob: %s: illegal path name", cp);
+				frecverr("readjob: %s: invalid path name", cp);
 			(void)strlcpy(tfname, cp, sizeof(tfname));
 			tfname[0] = 't';
 			if (!chksize(size)) {
@@ -180,7 +179,7 @@
 				continue;
 			}
 			if (link(tfname, cp) < 0)
-				frecverr("%s: %m", tfname);
+				frecverr("readjob: %s: link() to %s: %m", tfname, cp);
 			(void)unlink(tfname);
 			tfname[0] = '\0';
 			nfiles++;
@@ -197,12 +196,12 @@
 				continue;
 			}
 			if (strchr(cp, '/'))
-				frecverr("readjob: %s: illegal path name", cp);
+				frecverr("readjob: %s: invalid path name", cp);
 			(void)strlcpy(dfname, cp, sizeof(dfname));
 			(void)readfile(dfname, size);
 			continue;
 		}
-		frecverr("protocol screwup: %s", line);
+		frecverr("readjob: protocol screwup: %s", line);
 	}
 }
 
@@ -219,7 +218,7 @@
 
 	fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
 	if (fd < 0)
-		frecverr("readfile: %s: illegal path name: %m", file);
+		frecverr("readfile: %s: invalid job data file: %m", file);
 	ack();
 	err = 0;
 	for (i = 0; i < size; i += BUFSIZ) {
@@ -230,7 +229,7 @@
 		do {
 			j = read(STDOUT_FILENO, cp, amt);
 			if (j <= 0)
-				frecverr("Lost connection");
+				frecverr("readfile: %s[%d]: Lost connection: %s", file, fd, strerror(errno));
 			amt -= j;
 			cp += j;
 		} while (amt > 0);
@@ -244,7 +243,7 @@
 	}
 	(void)close(fd);
 	if (err)
-		frecverr("%s: write error", file);
+		frecverr("readfile: %s: write error: %m", file);
 	if (noresponse()) {		/* file sent had bad data in it */
 		(void)unlink(file);
 		return(0);
@@ -259,7 +258,7 @@
 	char resp;
 
 	if (read(STDOUT_FILENO, &resp, 1) != 1)
-		frecverr("Lost connection");
+		frecverr("noresponse: Lost connection: %m");
 	if (resp == '\0')
 		return(0);
 	return(1);
@@ -327,13 +326,37 @@
 frecverr(const char *msg, ...)
 {
 	extern char fromb[];
+	int oerrno = errno;
 	va_list ap;
 
 	va_start(ap, msg);
-	rcleanup(0);
-	syslog(LOG_ERR, "%s", fromb);
+	/*
+	 * XXX this most ugly hack is just to get fromb (the client hostname)
+	 * logged, but it results in two log records.  If vasprintf() handled
+	 * '%m' then we could use it to build a one line message.  We could use
+	 * the hack that syslog(3) itself implements, but that would be even
+	 * uglier code and perhaps harder to maintain.
+	 */
+	syslog(LOG_ERR, "Error receiveing job from %s: ...", fromb);
+	errno = oerrno;
 	vsyslog(LOG_ERR, msg, ap);
 	va_end(ap);
+	/*
+	 * rcleanup() must not be called until AFTER logging the error message,
+	 * because it will zap some variables which may have been supplied as
+	 * parameters for that msg...  (Fix from FreeBSD)
+	 */
+	rcleanup(0);
+	/* 
+	 * Add a minimal delay before returning the final error code to the
+	 * sending host.  This is just in case that machine responds this error
+	 * by INSTANTLY retrying (and instantly re-failing...).  It would be
+	 * stupid of the sending host to do that, but if there was a broken
+	 * implementation which did it, the issue might be obscured by
+	 * performance problems and a flood of syslog messages on the receiving
+	 * host.  (also from FreeBSD)
+	 */ 
+	sleep(2);		/* a paranoid throttling measure */
 	putchar('\1');		/* return error code */
 	exit(1);
 }
cvs diff: Diffing usr.sbin/lpr/lpq
Index: usr.sbin/lpr/lpq/lpq.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpq/lpq.c,v
retrieving revision 1.16
diff -u -r1.16 lpq.c
--- usr.sbin/lpr/lpq/lpq.c	28 Nov 2005 03:26:06 -0000	1.16
+++ usr.sbin/lpr/lpq/lpq.c	31 May 2007 20:42:06 -0000
@@ -69,7 +69,6 @@
 int	 requests;		/* # of spool requests */
 char	*user[MAXUSERS];	/* users to process */
 int	 users;			/* # of users in user array */
-uid_t	uid, euid;
 
 static void usage(void) __attribute__((__noreturn__));
 
@@ -80,11 +79,8 @@
 	char	*buf, *cp;
 
 	setprogname(*argv);
-	euid = geteuid();
-	uid = getuid();
-	seteuid(uid);
 	if (gethostname(host, sizeof(host)))
-		err(1, "lpq: gethostname");
+		err(1, "gethostname() failed");
 	host[sizeof(host) - 1] = '\0';
 	openlog("lpd", 0, LOG_LPR);
 
cvs diff: Diffing usr.sbin/lpr/lpr
Index: usr.sbin/lpr/lpr/lpr.1
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpr/lpr.1,v
retrieving revision 1.18
diff -u -r1.18 lpr.1
--- usr.sbin/lpr/lpr/lpr.1	21 Jan 2006 23:34:45 -0000	1.18
+++ usr.sbin/lpr/lpr/lpr.1	31 May 2007 21:00:16 -0000
@@ -37,7 +37,7 @@
 .Nd off line print
 .Sh SYNOPSIS
 .Nm
-.Op Fl cdfghlmnopqRrstv
+.Op Fl cdfghlmnpqRrstv
 .Bk -words
 .Op Fl 1234 Ar font
 .Ek
@@ -101,8 +101,7 @@
 page breaks.
 .It Fl n
 The files are assumed to contain data from
-.Em ditroff
-(device independent troff).
+.Xr nroff 1 .
 .It Fl o
 The files are assumed to be in postscript format.
 .It Fl p
@@ -112,8 +111,7 @@
 .Ic print ) .
 .It Fl t
 The files are assumed to contain data from
-.Xr troff 1
-(cat phototypesetter commands).
+.Xr troff 1 .
 .It Fl v
 The files are assumed to contain a raster image for devices like the
 Benson Varian.
@@ -134,11 +132,21 @@
 is used.
 .It Fl q
 Queue the print job but do not start the spooling daemon.
+.It Fl R
+Normally
+.Nm
+works silently except for diagnostic messages.
+This option caused it to writes a message to standard output containing
+the unique identifier which is used to refer to the newly created spool
+job.
+This identifier can be used to cancel the job (see
+.Xr lprm 1 ) ;
+or to find the job's status (see
+.Xr lpq 1 ) .
 .It Fl r
-Remove the file upon completion of spooling or upon completion of
-printing (with the
-.Fl s
-option).
+.Em NOTE :
+File removal is no longer supported by
+.Nm
 .It Fl s
 Use symbolic links.
 Usually files are copied to the spool directory.
@@ -169,36 +177,61 @@
 .Pp
 The remaining options apply to copies, the page display, and headers:
 .Bl -tag -width indent
-.It Fl \&# Ns Ar num
+.It Fl \&# Ar num
 The quantity
 .Ar num
 is the number of copies desired of each file named.
 For example,
 .Bd -literal -offset indent
-lpr \-#3 foo.c bar.c more.c
+lpr \-# 3 foo.c bar.c more.c
 .Ed
+.Pp
 would result in 3 copies of the file foo.c, followed by 3 copies
 of the file bar.c, etc.
 On the other hand,
 .Bd -literal -offset indent
-cat foo.c bar.c more.c \&| lpr \-#3
+cat foo.c bar.c more.c \&| lpr \-# 3
 .Ed
 .Pp
 will give three copies of the concatenation of the files.
 Often a site will disable this feature to encourage use of a photocopier
 instead.
 .It Xo
-.Fl Ns Oo Cm 1234 Oc Ar font
+.Fl 1 Ar font ,
+.Fl 2 Ar font ,
+.Fl 3 Ar font ,
+.Fl 4 Ar font
 .Xc
 Specifies a
 .Ar font
-to be mounted on font position
-.Ar i  .
-The daemon
-will construct a
-.Li .railmag
-file referencing
-the font pathname.
+to be ``mounted on'' the specified font ``position.''
+The spooler daemon
+will construct, in the spool directory, a file called
+.Pa .railmag
+containing the specified font name[s].
+The filter program is expected to look for this file and to do the
+appropriate things to load the specified font files to the device.
+Fonts may only be specified along with
+.Fl d ,
+.Fl n ,
+or
+.Fl t .
+If the font name does not begin with a slash
+.Pq Sq /
+then it will be prefixed by the pathname
+.Pa /usr/libdata/vfont/ .
+The default font names are:
+.Nl
+.Bl -enum -offset indent -compact
+.It
+.Pa /usr/libdata/vfont/R
+.It
+.Pa /usr/libdata/vfont/I
+.It
+.Pa /usr/libdata/vfont/B
+.It
+.Pa /usr/libdata/vfont/S
+.El
 .It Fl C Ar class
 Job classification
 to use on the burst page.
@@ -212,7 +245,7 @@
 to be replaced on the burst page by
 .Tn EECS ,
 and the file foo.c to be printed.
-.It Fl i numcols
+.It Fl i Ar numcols
 The output is indented by
 .Pq Ar numcols .
 .It Fl J Ar job
@@ -228,7 +261,7 @@
 This option is only honored if the real user-id is daemon
 (or that specified in the printcap file instead of daemon),
 and is intended for those instances where print filters wish to requeue jobs.
-.It Fl w Ns Ar num
+.It Fl w Ar num
 Uses
 .Ar num
 as the page width for
@@ -247,21 +280,17 @@
 Personal identification.
 .It Pa /etc/printcap
 Printer capabilities data base.
-.It Pa /usr/sbin/lpd
-Line printer daemons.
-.It Pa /var/spool/output/*
-Directories used for spooling.
-.It Pa /var/spool/output/*/cf*
-Daemon control files.
-.It Pa /var/spool/output/*/df*
-Data files specified in "cf" files.
-.It Pa /var/spool/output/*/tf*
-Temporary copies of "cf" files.
+.It Pa /usr/libexec/lpr/
+Default location of filter programs.
+.It Pa /usr/libexec/lpr/lpf
+Line printer filter for
+.Nm nroff
+output.
 .El
 .Sh DIAGNOSTICS
 If you try to spool too large a file, it will be truncated.
 .Nm
-will object to printing binary files.
+may object to printing binary files.
 If a user other than root prints a file and spooling is disabled,
 .Nm
 will print a message saying so and will not put jobs in the queue.
@@ -270,8 +299,8 @@
 on the local machine cannot be made,
 .Nm
 will say that the daemon cannot be started.
-Diagnostics may be printed in the daemon's log file
-regarding missing spool files by
+Diagnostics may be printed in the daemon's log file regarding missing
+spool files by
 .Xr lpd 8 .
 .Sh SEE ALSO
 .Xr lpq 1 ,
@@ -290,6 +319,16 @@
 Fonts for
 .Xr troff 1
 and
+.Xr tex 1
+must reside on the host with the printer.
+It is currently not possible to use local font libraries with remote printers.
+.Pp
+Only four font additional fonts can be specified although
+.Ic troff
+now supports many simultaneous fonts and
 .Ic tex
-reside on the host with the printer.
-It is currently not possible to use local font libraries.
+always has.
+.Pp
+It is generally easier and far more flexible to ensure that output is
+in a form suitable for direct printing on the printer, particularly with
+something like an HP-PCL\(tm or PostScript\(tm printer.
Index: usr.sbin/lpr/lpr/lpr.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lpr/lpr.c,v
retrieving revision 1.35
diff -u -r1.35 lpr.c
--- usr.sbin/lpr/lpr/lpr.c	29 Jan 2006 18:55:46 -0000	1.35
+++ usr.sbin/lpr/lpr/lpr.c	1 Jun 2007 17:51:59 -0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: lpr.c,v 1.35 2006/01/29 18:55:46 christos Exp $	*/
+/*	$NetBSD: lpr.c,v 1.20 2001/06/25 16:40:50 mrg Exp $ */
 
 /*
  * Copyright (c) 1983, 1989, 1993
@@ -116,7 +116,302 @@
 static int	 test(const char *);
 static void	 usage(void) __attribute__((__noreturn__));
 
-uid_t	uid, euid;
+#ifndef HAVE_FOPEN_AS_USER
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+static ssize_t read_fd (int, void *, size_t, int *);
+
+# ifdef __IN_LIB_C__
+#  include "../stdio/local.h"
+# else
+extern int	__sflags (const char *, int *);
+# endif
+
+#ifndef CMSG_SPACE
+# define CMSG_SPACE(size)	(sizeof(struct cmsghdr) + (size))
+#endif
+
+/*
+ * file descriptor passing helper function
+ *
+ * More or less copied from W. Richard Stevens' "UNIX Network Programming",
+ * Vol. 1 (2nd ed), p. 387 (obsolete code for old recvmsg() deleted, and
+ * converted to use malloc() because CMSG_SPACE() is not a compile-time
+ * constant on NetBSD)
+ */
+static ssize_t
+read_fd(fd, ptr, nbytes, precvfd)
+	int fd;				/* AF_LOCAL socket */
+	void *ptr;			/* for ancilliary data */
+	size_t nbytes;			/* size of ptr storage */
+	int *precvfd;			/* pointer for storing descriptor */
+{
+	struct msghdr	msg;
+	struct iovec	iov;
+	ssize_t		n;
+	char           *control;
+	struct cmsghdr *cmptr;
+	int             oerrno;
+
+	*precvfd = -1;			/* assume the worst.... */
+
+	if (!(control = malloc(CMSG_SPACE(sizeof(*precvfd)))))
+		return -1;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_control = control;
+	msg.msg_controllen = (socklen_t) CMSG_SPACE(sizeof(*precvfd)); /* CMSG_LEN()??? */
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_flags = 0;
+
+	iov.iov_base = ptr;
+	iov.iov_len = nbytes;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	n = recvmsg(fd, &msg, 0);
+	oerrno = errno;
+	if (n <= 0) {
+		free(control);
+		errno = oerrno;
+		return -1;
+	}
+
+	cmptr = CMSG_FIRSTHDR(&msg);
+	if (!cmptr || cmptr->cmsg_len != CMSG_LEN(sizeof(*precvfd))) {
+		free(control);
+		errno = EBADF;		/* descriptor was not passed */
+		return -1;
+	}
+	if (cmptr->cmsg_level != SOL_SOCKET) {
+		free(control);
+		errno = EBADF;
+		return -1;
+	}
+	if (cmptr->cmsg_type != SCM_RIGHTS) {
+		free(control);
+		errno = EBADF;
+		return -1;
+	}
+	*precvfd = *((int *) ((void *) CMSG_DATA(cmptr)));
+
+	free(control);
+	errno = oerrno;
+
+	return (n);
+}
+
+static ssize_t write_fd (int, void *, size_t, int);
+
+/*
+ * file descriptor passing helper function
+ *
+ * More or less copied from W. Richard Stevens' "UNIX Network Programming",
+ * Vol. 1 (2nd ed), p. 389 (obsolete code for old recvmsg() deleted, and
+ * converted to use malloc() because CMSG_SPACE() is not a compile-time
+ * constant on NetBSD)
+ */
+static ssize_t
+write_fd(fd, ptr, nbytes, sendfd)
+	int fd;				/* AF_LOCAL socket */
+	void *ptr;			/* for ancilliary data */
+	size_t nbytes;			/* size of ancilliary data */
+	int sendfd;			/* descriptor to send */
+{
+	struct msghdr	msg;
+	struct iovec	iov;
+	char           *control;
+	struct cmsghdr *cmptr;
+	int             oerrno;
+	int             rv;
+
+	if (!(control = malloc(CMSG_SPACE(sizeof(sendfd)))))
+		return -1;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_control = control;
+	msg.msg_controllen = (socklen_t) CMSG_LEN(sizeof(sendfd));
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_flags = 0;
+
+	cmptr = CMSG_FIRSTHDR(&msg);
+	cmptr->cmsg_len = CMSG_LEN(sizeof(sendfd));
+	cmptr->cmsg_level = SOL_SOCKET;
+	cmptr->cmsg_type = SCM_RIGHTS;
+	*((int *) ((void *) CMSG_DATA(cmptr))) = sendfd;
+
+	iov.iov_base = ptr;
+	iov.iov_len = nbytes;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	rv = sendmsg(fd, &msg, 0);
+	oerrno = errno;
+
+	free(control);
+	errno = oerrno;
+
+	return rv;
+}
+
+static FILE *fopen_as_user(char *, const char *, uid_t, gid_t);
+
+/*
+ *	fopen_as_user() - open a file using uid:gid privileges only.
+ *
+ * This is the magic, forking, file-descriptor passing, version!
+ *
+ * More or less copied from W. Richard Stevens' "UNIX Network Programming",
+ * Vol. 1 (2nd ed), p. 385
+ *
+ * XXX WARNING: This is an incomplete implementation! (e.g. no initgroups())
+ */
+static FILE *
+fopen_as_user(path, mode, uid, gid)
+	char *path;
+	const char *mode;
+	uid_t uid;
+	gid_t gid;
+{
+	int fd;
+	int pid;
+	int sockfd[2];
+	int status;
+	FILE *fp = NULL;
+	char one_byte[1] = { '\0' };
+
+	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0)
+		return NULL;
+
+	switch ((pid = fork())) {
+	case -1: {
+		int oerrno = errno;
+
+		/* fork() error */
+		close(sockfd[0]);
+		close(sockfd[1]);
+		errno = oerrno;
+		return NULL;
+		/* NOTREACHED */
+	}
+	case 0: {
+		int cfd;
+		int oflags;
+
+# if (ELAST > 254)
+#  include "ERORR:  This code returns errno via _exit() and ELAST > 254!"
+# endif
+		/* in the child process */
+
+		/*
+		 * Take care to use _exit() so as to avoid any side-effects of
+		 * exit(), such as atexit()s registered by the parent process.
+		 *
+		 * Note this means we have to be very careful to always close
+		 * what we open!
+		 */
+		close(sockfd[0]);
+
+		if (setgid(gid) != 0) {
+			int oerrno = errno;
+
+			close(sockfd[1]);
+			_exit(oerrno + 1);
+		}
+		if (setuid(uid) != 0) {
+			int oerrno = errno;
+
+			close(sockfd[1]);
+			_exit(oerrno + 1);
+		}
+		if (__sflags(mode, &oflags) == 0) { /* magic *BSD stdio internals */
+			int oerrno = errno;
+
+			close(sockfd[1]);
+			_exit(oerrno + 1);
+		}
+		if ((cfd = open(path, oflags, DEFFILEMODE)) < 0) {
+			int oerrno = errno;
+
+			close(sockfd[1]);
+			_exit(oerrno + 1);
+		}
+		/*
+		 * When sending a descriptor across a stream pipe we always
+		 * send at least one byte of data, even if the receiver does
+		 * nothing with the data, otherwise the receiver cannot tell
+		 * whether a return value of 0 from read_fd() means "no data
+		 * (but possibly a descriptor)", or just "end of file".
+		 */
+		if (write_fd(sockfd[1], one_byte, sizeof(one_byte), cfd) < 0) {
+			int oerrno = errno;
+
+			close(sockfd[1]);
+			close(cfd);
+			_exit(oerrno + 1);
+		}
+		close(cfd);
+		close(sockfd[1]);
+		_exit(0);
+		/* NOTREACHED */
+	}
+	default:
+		/* in the parent process */
+		close(sockfd[1]);
+		if (waitpid(pid, &status, 0) < 0) { /* WUNTRACED??? */
+			int oerrno = errno;
+
+			close(sockfd[0]);
+			errno = oerrno;
+			return NULL;
+		}
+		if (WIFEXITED(status)) {
+			char ch;
+
+			if (WEXITSTATUS(status)) {
+				close(sockfd[0]);
+				errno = WEXITSTATUS(status) - 1;
+				return NULL;
+			}
+			if (read_fd(sockfd[0], &ch, 1, &fd) <= 0) {
+				int oerrno = errno;
+
+				close(sockfd[0]);
+				errno = oerrno;
+				return NULL;
+			}
+			if (fd < 0) {
+				int oerrno = errno;
+
+				close(sockfd[0]);
+				errno = oerrno;
+				return NULL;
+			}
+			close(sockfd[0]);
+		} else {
+			close(sockfd[0]);
+			errno = EINTR;
+			return NULL;
+		}
+	}
+	if (!(fp = fdopen(fd, mode))) {
+		int oerrno = errno;
+
+		close(fd);
+		errno = oerrno;
+		return NULL;
+	}
+
+	return fp;
+}
+#endif /* HAVE_FOPEN_AS_USER */
 
 int
 main(int argc, char *argv[])
@@ -126,13 +421,8 @@
 	char *arg;
 	const char *cp;
 	char buf[MAXPATHLEN];
-	int i, f, errs, c;
+	int i, errs, c;
 	struct stat stb;
-	int oerrno;
-
-	euid = geteuid();
-	uid = getuid();
-	seteuid(uid);
 
 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
 		signal(SIGHUP, cleanup);
@@ -230,8 +520,8 @@
 			qflag++;
 			break;
 
-		case 'r':		/* remove file when done */
-			rflag++;
+		case 'r':		/* DEPRECATED: remove file when done */
+			warnx("File removal no longer supported!  Feed it to stdin and remove it yourself!");
 			break;
 
 		case 's':		/* try to link files */
@@ -260,19 +550,21 @@
 		usage();
 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
 		printer = DEFLP;
+
 	chkprinter(printer);
+
 	if (SC && ncopies > 1)
-		fatal2("multiple copies are not allowed");
+		errx(1, "%s: multiple copies are not allowed", printer);
 	if (MC > 0 && ncopies > MC)
-		fatal2("only %ld copies are allowed", MC);
+		errx(1, "%s: only %ld copies are allowed", printer, MC);
 	/*
 	 * Get the identity of the person doing the lpr using the same
 	 * algorithm as lprm. 
 	 */
 	userid = getuid();
-	if (userid != DU || person == 0) {
+	if (userid != DU || person == NULL) {
 		if ((pw = getpwuid(userid)) == NULL)
-			fatal2("Who are you?");
+			errx(1, "Who are you?");
 		person = pw->pw_name;
 	}
 	/*
@@ -280,7 +572,7 @@
 	 */
 	if (RG != NULL && userid != DU) {
 		if ((gptr = getgrnam(RG)) == NULL)
-			fatal2("Restricted group specified incorrectly");
+			errx(1, "%s: Restricted group '%s' specified incorrectly", printer, RG);
 		if (gptr->gr_gid != getgid()) {
 			while (*gptr->gr_mem != NULL) {
 				if ((strcmp(person, *gptr->gr_mem)) == 0)
@@ -302,9 +594,7 @@
 	 */
 	mktemps();
 	tfd = nfile(tfname);
-	seteuid(euid);
 	(void)fchown(tfd, DU, -1);	/* owned by daemon for protection */
-	seteuid(uid);
 	card('H', host);
 	card('P', person);
 	if (hdr) {
@@ -336,13 +626,16 @@
 	if (argc == 0)
 		copy(0, " ");
 	else while (argc--) {
+		FILE *fp;
+		int fd;
+
 		if (argv[0][0] == '-' && argv[0][1] == '\0') {
 			/* use stdin */
 			copy(0, " ");
 			argv++;
 			continue;
 		}
-		if ((f = test(arg = *argv++)) < 0)
+		if (test(arg = *argv++) < 0)
 			continue;	/* file unreasonable */
 
 		if (sflag && (cp = linked(arg)) != NULL) {
@@ -355,8 +648,10 @@
 			for (i = 0; i < ncopies; i++)
 				card(format, &dfname[inchar-2]);
 			card('U', &dfname[inchar-2]);
+#if 0
 			if (f)
 				card('U', cp);
+#endif
 			card('N', arg);
 			dfname[inchar]++;
 			nact++;
@@ -364,20 +659,17 @@
 		}
 		if (sflag)
 			warnx("%s: not linked, copying instead", arg);
-		seteuid(uid);
-		if ((i = open(arg, O_RDONLY)) < 0) {
-			oerrno = errno;
-			seteuid(uid);
-			errno = oerrno;
-			warn("cannot open %s", arg);
+		if (! (fp = fopen_as_user(arg, "r", userid, getgid()))) {
+			warn("%s: cannot open", arg);
 			continue;
-		} else {
-			copy(i, arg);
-			(void)close(i);
-			if (f && unlink(arg) < 0)
-				warn("%s: not removed", arg);
 		}
-		seteuid(uid);
+		fd = fileno(fp);
+		copy(fd, arg);		/* XXX error check! */
+		(void) fclose(fp);	/* XXX error check! */
+#if 0
+		if (f && unlink(arg) < 0)
+			warn("%s: not removed", arg);
+#endif
 	}
 
 	if (nact) {
@@ -386,7 +678,6 @@
 		/*
 		 * Touch the control file to fix position in the queue.
 		 */
-		seteuid(euid);
 		if ((tfd = open(tfname, O_RDWR)) >= 0) {
 			char ch;
 
@@ -405,7 +696,6 @@
 			cleanup(0);
 		}
 		unlink(tfname);
-		seteuid(uid);
 		if (Rflag)
 			printf("request id is %d\n", reqid);
 		if (qflag)		/* just queue things up */
@@ -422,7 +712,8 @@
 }
 
 /*
- * Create the file n and copy from file descriptor f.
+ * create the data file named in dfname and copy from file descriptor f to the
+ * data file.
  */
 static void
 copy(int f, const char *n)
@@ -496,9 +787,7 @@
 		strlcat(buf, file, sizeof(buf));
 		file = buf;
 	}
-	seteuid(euid);
 	ret = symlink(file, dfname);
-	seteuid(uid);
 	return(ret ? NULL : file);
 }
 
@@ -535,7 +824,6 @@
 	int oldumask = umask(0);		/* should block signals */
 	int oerrno;
 
-	seteuid(euid);
 	f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
 	oerrno = errno;
 	(void)umask(oldumask);
@@ -548,7 +836,6 @@
 		warn("cannot chown %s", n);
 		cleanup(0);	/* cleanup does exit */
 	}
-	seteuid(uid);
 	if (++n[inchar] > 'z') {
 		if (++n[inchar-2] == 't') {
 			printf("too many files - break up the job\n");
@@ -573,7 +860,6 @@
 	signal(SIGQUIT, SIG_IGN);
 	signal(SIGTERM, SIG_IGN);
 	i = inchar;
-	seteuid(euid);
 	if (tfname)
 		do
 			unlink(tfname);
@@ -595,7 +881,7 @@
 /*
  * Test to see if this is a printable file.
  * Return -1 if it is not, 0 if its printable, and 1 if
- * we should remove it after printing.
+ * we can and should remove it after printing.
  */
 static int
 test(const char *file)
@@ -603,7 +889,6 @@
 	int fd;
 	char *cp;
 
-	seteuid(uid);
 	if (access(file, 4) < 0) {
 		warn("cannot access %s", file);
 		goto bad;
@@ -644,7 +929,6 @@
 	}
 	return(0);
 bad:
-	seteuid(uid);
 	return(-1);
 }
 
@@ -694,12 +978,10 @@
 	char buf[MAXPATHLEN];
 
 	(void)snprintf(buf, sizeof(buf), "%s/.seq", SD);
-	seteuid(euid);
 	if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0)
 		err(1, "cannot create %s\n", buf);
 	if (flock(fd, LOCK_EX))
 		err(1, "cannot lock %s", buf);
-	seteuid(uid);
 	n = 0;
 	if ((len = read(fd, buf, sizeof(buf))) > 0) {
 		for (cp = buf; len--; ) {
cvs diff: Diffing usr.sbin/lpr/lprm
Index: usr.sbin/lpr/lprm/lprm.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/lprm/lprm.c,v
retrieving revision 1.17
diff -u -r1.17 lprm.c
--- usr.sbin/lpr/lprm/lprm.c	28 Nov 2005 03:26:07 -0000	1.17
+++ usr.sbin/lpr/lprm/lprm.c	1 Jun 2007 17:56:00 -0000
@@ -75,7 +75,6 @@
 int	 requests;		/* # of spool requests */
 char	*user[MAXUSERS];	/* users to process */
 int	 users;			/* # of users in user array */
-uid_t	 uid, euid;		/* real and effective user id's */
 
 static char	luser[16];	/* buffer for person */
 
@@ -87,9 +86,6 @@
 	char *arg;
 	struct passwd *p;
 
-	uid = getuid();
-	euid = geteuid();
-	seteuid(uid);	/* be safe */
 	setprogname(*argv);
 	gethostname(host, sizeof(host));
 	host[sizeof(host) - 1] = '\0';
cvs diff: Diffing usr.sbin/lpr/lptest
cvs diff: Diffing usr.sbin/lpr/pac
Index: usr.sbin/lpr/pac/pac.8
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/pac/pac.8,v
retrieving revision 1.11
diff -u -r1.11 pac.8
--- usr.sbin/lpr/pac/pac.8	7 Aug 2003 11:25:32 -0000	1.11
+++ usr.sbin/lpr/pac/pac.8	31 May 2007 19:01:06 -0000
@@ -29,7 +29,7 @@
 .\"
 .\"     @(#)pac.8	8.1 (Berkeley) 6/6/93
 .\"
-.Dd June 6, 1993
+.Dd October 17, 2003
 .Dt PAC 8
 .Os
 .Sh NAME
@@ -42,6 +42,9 @@
 .Op Fl P Ar printer
 .Ek
 .Bk -words
+.Op Fl f Ar acctfile
+.Ek
+.Bk -words
 .Op Fl p Ar price
 .Ek
 .Op Ar name ...
@@ -54,7 +57,7 @@
 .Pp
 Options and operands available:
 .Bl -tag -width PPprinter
-.It Fl P Ns Ar printer
+.It Fl P Ar printer
 Accounting is done for the named printer.
 Normally, accounting is done for the default printer (site dependent) or
 the value of the environment variable
@@ -63,15 +66,20 @@
 .It Fl c
 flag causes the output to be sorted by cost; usually the
 output is sorted alphabetically by name.
+.It Fl f Ar acctfile
+The file
+.Ar acctfile
+is used instead of the accounting file specified for the printer in
+.Pa /etc/printcap .
 .It Fl m
 flag causes the host name to be ignored in the accounting file.  This
 allows for a user on multiple machines to have all of his printing
 charges grouped together.
-.It Fl p Ns Ar price
+.It Fl p Ar price
 The value
 .Ar price
 is used for the cost in dollars instead of the default value of 0.02
-or the price specified in
+or the price specified for the printer in
 .Pa /etc/printcap .
 .It Fl r
 Reverse the sorting order.
@@ -99,11 +107,18 @@
 Note that
 .Nm
 on other system might print the price as price per copy.
+.Sh ENVIRONMENT
+If the following environment variable exists, it is used by
+.Nm :
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+Specifies an alternative default printer.
+.El
 .Sh FILES
-.Bl -tag -width /var/account/?_sum -compact
-.It Pa /var/account/?acct
+.Bl -tag -width /var/account/*_lpr_sum -compact
+.It Pa /var/account/*_lpr
 raw accounting files
-.It Pa /var/account/?_sum
+.It Pa /var/account/*_lpr_sum
 summary accounting files
 .It Pa /etc/printcap
 printer capability data base
Index: usr.sbin/lpr/pac/pac.c
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/usr.sbin/lpr/pac/pac.c,v
retrieving revision 1.21
diff -u -r1.21 pac.c
--- usr.sbin/lpr/pac/pac.c	24 Nov 2006 19:47:00 -0000	1.21
+++ usr.sbin/lpr/pac/pac.c	31 May 2007 19:01:06 -0000
@@ -44,7 +44,7 @@
 /*
  * Do Printer accounting summary.
  * Currently, usage is
- *	pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
+ *	pac [-P printer] [-p price] [-s] [-r] [-c] [-m] [user ...]
  * to print the usage information for the named people.
  */
 
@@ -60,7 +60,7 @@
 #include "lp.h"
 #include "lp.local.h"
 
-static char	*acctfile;	/* accounting file (input data) */
+static char	*acctfile = NULL;/* accounting file (input data) */
 static int	 allflag = 1;	/* Get stats on everybody */
 static int	 errs;
 static int	 hcount;	/* Count of hash entries */
@@ -107,7 +107,9 @@
 	FILE *acf;
 	int opt;
 
-	while ((opt = getopt(argc, argv, "P:p:scmr")) != -1) {
+	setprogname(argv[0]);
+	
+	while ((opt = getopt(argc, argv, "P:f:p:scmr")) != -1) {
 		switch(opt) {
 		case 'P':
 			/*
@@ -138,6 +140,13 @@
 			sort++;
 			continue;
 
+		case 'f':
+			/*
+			 * use specified acctfile
+			 */
+			acctfile = optarg;
+			continue;
+
 		case 'm':
 			/*
 			 * disregard machine names for each user
@@ -253,6 +262,7 @@
 	struct hent *hp, **ap;
 	int hno, c, runs;
 	float feet;
+	float dollars;
 
 	hp = hashtab[0];
 	hno = 1;
@@ -266,24 +276,25 @@
 		hp = hp->h_link;
 	}
 	qsort(base, hcount, sizeof hp, qucmp);
-	printf(" pages/feet runs price    %s\n",
+	printf("  pages/feet  runs     price  %s\n",
 		(mflag ? "login" : "host name and login"));
-	printf(" ---------- ---- -------- ----------------------\n");
-	feet = 0.0;
+	printf("  ----------  ----  --------  ----------------------\n");
 	runs = 0;
+	feet = 0.0;
+	dollars = 0.0;
 	for (ap = base, c = hcount; c--; ap++) {
 		hp = *ap;
 		runs += hp->h_count;
 		feet += hp->h_feetpages;
-		printf("    %7.2f %4d $%7.2f %s\n",
+		dollars += hp->h_feetpages * price * hp->h_count;
+		printf("     %7.2f  %4d  $%7.2f  %s\n",
 			hp->h_feetpages, hp->h_count, 
 			hp->h_feetpages * price * hp->h_count,
 			hp->h_name);
 	}
 	if (allflag) {
-		printf(" ---------- ---- -------- ----------------------\n");
-		printf("Sum:%7.2f %4d $%7.2f\n", feet, runs, 
-			feet * price * runs);
+		printf("  ----------  ----  --------  ----------------------\n");
+		printf("Sum: %7.2f  %4d  $%7.2f\n", feet, runs, dollars);
 	}
 }
 
@@ -414,18 +425,19 @@
 chkprinter(const char *s)
 {
 	int stat;
+	char *realacctfile;
 
 	if ((stat = cgetent(&bp, printcapdb, s)) == -2) {
-		printf("pac: can't open printer description file\n");
-		exit(3);
+		err(3, "can't open printer description file");
 	} else if (stat == -1)
 		return(0);
 	else if (stat == -3)
 		fatal("potential reference loop detected in printcap file");
 
-	if (cgetstr(bp, "af", &acctfile) == -1) {
-		printf("accounting not enabled for printer %s\n", printer);
-		exit(2);
+	if (!acctfile) {
+		if (cgetstr(bp, "af", &realacctfile) == -1)
+			errx(2, "accounting not enabled for printer %s", printer);
+		acctfile = realacctfile;
 	}
 	if (!pflag && (cgetnum(bp, "pc", &price100) == 0))
 		price = price100/10000.0;
@@ -439,6 +451,6 @@
 usage(void)
 {
 	fprintf(stderr,
-	  "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
+	  "usage: pac [-P printer] [-p price] [-f acctfile] [-s] [-c] [-r] [-m] [user ...]\n");
 	exit(1);
 }
Index: share/man/man5/printcap.5
===================================================================
RCS file: /cvs/master/m-NetBSD/main/src/share/man/man5/printcap.5,v
retrieving revision 1.12
diff -u -r1.12 printcap.5
--- share/man/man5/printcap.5	11 Sep 2001 01:01:58 -0000	1.12
+++ share/man/man5/printcap.5	13 Feb 2005 03:40:23 -0000
@@ -33,7 +33,7 @@
 .\"
 .\"     @(#)printcap.5	8.2 (Berkeley) 12/11/93
 .\"
-.Dd February 14, 2001
+.Dd February 12, 2005
 .Dt PRINTCAP 5
 .Os
 .Sh NAME
@@ -79,9 +79,10 @@
 .Pf ( Xr ioctl 2
 call)
 .It "cf	str" Ta Dv NULL Ta No "cifplot data filter"
-.It "df	str" Ta Dv NULL Ta No "tex data filter"
+.It "df	str" Ta Dv NULL Ta No "DVI (TeX) data filter"
 .Pf ( Tn DVI
 format)
+.It "ex	bool	false	use extended filter command interface"
 .It "fc	num	0	if lp is a tty, clear flag bits"
 .Pq Pa sgtty.h
 .It "ff	str" Ta So Li \ef Sc Ta No "string to send for a form feed"
@@ -93,7 +94,7 @@
 .It "hl	bool	false	print the burst header page last"
 .It "ic	bool	false	driver supports (non standard) ioctl to indent printout"
 .It "if	str" Ta Dv NULL Ta No "name of text filter which does accounting"
-.It "lf	str" Ta Pa /dev/console Ta No "error logging file name"
+.It "lf	str" Ta Pa /var/log/lpd-errs Ta No "error logging file name"
 .It "lo	str" Ta Pa lock Ta No "name of lock file"
 .It "lp	str" Ta Pa /dev/lp Ta No "device name to open for output to local printer, or port@host for remote printer/printer on print server"
 .It "ms	str" Ta Dv NULL Ta No "list of terminal modes to set or clear"
@@ -101,9 +102,9 @@
 .Dv BUFSIZ
 blocks), zero = unlimited
 .It "nd	str" Ta Dv NULL Ta No "next directory for list of queues (unimplemented)"
-.It "nf	str" Ta Dv NULL Ta No "ditroff data filter (device independent troff)"
+.It "nf	str" Ta Pa /usr/libexec/lpr/lpf Ta No "nroff data filter"
 .It "of	str" Ta Dv NULL Ta No "name of output filtering program"
-.It "pc	num	200	price per foot or page in hundredths of cents"
+.It "pc	num	200	price per foot of paper, or per page, in hundredths of cents"
 .It "pl	num	66	page length (in lines)"
 .It "pw	num	132	page width (in characters)"
 .It "px	num	0	page width in pixels (horizontal)"
@@ -111,18 +112,18 @@
 .It "rf	str" Ta Dv NULL Ta No "filter for printing"
 .Tn FORTRAN
 style text files
-.It "rg	str" Ta Dv NULL Ta No "restricted group. Only members of group allowed access"
+.It "rg	str" Ta Dv NULL Ta No "restricted group.  Only members of group allowed access to `lpr'"
 .It "rm	str" Ta Dv NULL Ta No "machine name for remote printer"
 .It "rp	str	``lp''	remote printer name argument"
 .It "rs	bool	false	restrict remote users to those with local accounts"
 .It "rw	bool	false	open the printer device for reading and writing"
 .It "sb	bool	false	short banner (one line only)"
 .It "sc	bool	false	suppress multiple copies"
-.It "sd	str" Ta Pa /var/spool/output/lpd Ta No "spool directory"
+.It "sd	str" Ta Pa /var/spool/output/lpd Ta No "spool directory (must be unique per entry)"
 .It "sf	bool	false	suppress form feeds"
 .It "sh	bool	false	suppress printing of burst page header"
 .It "st	str" Ta Pa status Ta No "status file name"
-.It "tf	str" Ta Dv NULL Ta No "troff data filter (cat phototypesetter)"
+.It "tf	str" Ta Dv NULL Ta No "troff data filter"
 .It "tr	str" Ta Dv NULL Ta No "trailer string to print when queue empties"
 .It "vf	str" Ta Dv NULL Ta No "raster image filter"
 .It "xc	num	0	if lp is a tty, clear local mode bits"
@@ -135,7 +136,7 @@
 .Sh FILTERS
 If a printer is specified via
 .Sy lp
-(either local or remote),
+(either on a local device or on a remote network device),
 the
 .Xr lpd 8
 daemon creates a pipeline of
@@ -145,16 +146,24 @@
 .Sy rm
 unless the local host is the same as the remote printer host
 given.
-The filters selected depend on the flags passed to
+.Pp
+The filter program may either be specified with a fully qualified
+pathname, or as the name of a program to be found in
+.Pa /usr/libexec/lpr .
+.Pp
+The filter(s) used depend on the flags passed to
 .Xr lpr 1 .
+.Pp
 The pipeline set up is:
 .Bd -literal -offset indent
-p	pr | if	regular text + pr(1)
-none	if	regular text
+\fIflag\fP	\fIfilter\fP	\fIdescription\fP
+none	if	regular text (or data suitable for printing)
+p	\fBpr\fP | if	regular text + pr(1), piped through any if
+l	if \fB-c\fP	raw data suitable for printing
 c	cf	cifplot
-d	df	DVI (tex)
+d	df	DVI (TeX)
 g	gf	plot(3)
-n	nf	ditroff
+n	nf	nroff
 f	rf	Fortran
 t	tf	troff
 v	vf	raster image
@@ -219,12 +228,22 @@
 is better suited to performing accounting.
 The
 .Cm of
-is only given the
-.Ar width
+program is only given the
+.Fl w Ns Ar width
 and
-.Ar length
+.Fl l Ns Ar length
 flags.
 .Pp
+Note that if both
+.Cm if
+and
+.Cm of
+are specified then the magic character sequence
+.Dq \e031\e001
+is sent between files to tell the output filter to send a
+.Dv SIGSTOP
+signal to itself and thus wait to be restarted by printjob daemon.
+.Pp
 All other filters are called as:
 .Bd -filled -offset indent
 .Nm filter
@@ -245,17 +264,29 @@
 .Cm py
 entries respectively.
 .Pp
+If the
+.Cm ex
+capability is specified then the following parameters are also passed to
+each filter:
+.Bd -filled -offset indent
+.Fl j Ar jobname
+.Fl p Ar printer
+.Fl s Ar status-file
+.Ed
+.Pp
 All filters take
 .Em stdin
 as the file,
 .Em stdout
-as the printer,
-may log either to
+as the printer device,
+may log error messages to either
 .Em stderr
 or using
-.Xr syslog 3 ,
-and must not ignore
+.Xr syslog 3 .
+.Pp
+Filters must not ignore
 .Dv SIGINT .
+.\"
 .Sh LOGGING
 Error messages generated by the line printer programs themselves
 (that is, the lp* programs) are logged by
@@ -281,6 +312,7 @@
 .Xr termcap 5 ,
 .Xr lpc 8 ,
 .Xr lpd 8 ,
+.\" XXX .Xr lpf 8 ,
 .Xr pac 8
 .Rs
 .%T "4.3 BSD Line Printer Spooler Manual"
@@ -290,3 +322,24 @@
 .Nm
 file format appeared in
 .Bx 4.2 .
+.Sh BUGS
+The
+.Cm rg
+attribute only controls local users invoking
+.Xr lpr 1
+while the
+.Cm rs
+attribute only controls remote users, and there is no interaction
+between them (i.e. remote users do not also have to be members of the
+restricted group).  Any remote user able to originate a connection from
+a reserved port (one less than 1024) (or if
+.Xr lpd 8
+was invoked with
+.Fl W )
+then it is trivial for a remote user to spoof his or her identity as
+that information is provided in-band.
+.Pp
+The whole idea of using filters to convert output from one format to
+another is rather silly and can result in far more confusion and wasted
+paper than simply requiring users to provide their output in the correct
+format for the desired printer.

>Unformatted: