diff options
Diffstat (limited to 'usr.sbin/lpr/lpd')
| -rw-r--r-- | usr.sbin/lpr/lpd/Makefile | 13 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/Makefile.depend | 17 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/extern.h | 45 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/hosts.lpd | 3 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/lpd.8 | 351 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/lpd.c | 934 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/lpdchar.c | 1062 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/modes.c | 225 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/printcap | 52 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/printjob.c | 2008 | ||||
| -rw-r--r-- | usr.sbin/lpr/lpd/recvjob.c | 393 | 
11 files changed, 5103 insertions, 0 deletions
| diff --git a/usr.sbin/lpr/lpd/Makefile b/usr.sbin/lpr/lpd/Makefile new file mode 100644 index 000000000000..199f03ed16a0 --- /dev/null +++ b/usr.sbin/lpr/lpd/Makefile @@ -0,0 +1,13 @@ +PACKAGE=lp +CONFS=	hosts.lpd printcap +PROG=	lpd +MAN=	lpd.8 +SRCS=	lpd.c printjob.c recvjob.c lpdchar.c modes.c + +CFLAGS+= -I${.CURDIR:H}/common_source + +WARNS?=	1 + +LIBADD=	lpr + +.include <bsd.prog.mk> diff --git a/usr.sbin/lpr/lpd/Makefile.depend b/usr.sbin/lpr/lpd/Makefile.depend new file mode 100644 index 000000000000..f72fd2df5270 --- /dev/null +++ b/usr.sbin/lpr/lpd/Makefile.depend @@ -0,0 +1,17 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ +	include \ +	include/arpa \ +	include/xlocale \ +	lib/${CSU_DIR} \ +	lib/libc \ +	lib/libcompiler_rt \ +	usr.sbin/lpr/common_source \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/lpr/lpd/extern.h b/usr.sbin/lpr/lpd/extern.h new file mode 100644 index 000000000000..71b02ea904f4 --- /dev/null +++ b/usr.sbin/lpr/lpd/extern.h @@ -0,0 +1,45 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1989, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */ + +extern char scnkey[][HEIGHT];	/* in lpdchar.c */ +extern int lflag;		/* in lpd.c */ + +struct	printer; +struct termios; + +__BEGIN_DECLS +void	 printjob(struct printer *_pp); +void	 startprinting(const char *_printer); +void	 recvjob(const char *_printer); +int	 msearch(char *_str, struct termios *_ip); +__END_DECLS diff --git a/usr.sbin/lpr/lpd/hosts.lpd b/usr.sbin/lpr/lpd/hosts.lpd new file mode 100644 index 000000000000..c440bdec992e --- /dev/null +++ b/usr.sbin/lpr/lpd/hosts.lpd @@ -0,0 +1,3 @@ +# +# See lpd(8) +#machine.domain diff --git a/usr.sbin/lpr/lpd/lpd.8 b/usr.sbin/lpr/lpd/lpd.8 new file mode 100644 index 000000000000..eed37ea1d6f4 --- /dev/null +++ b/usr.sbin/lpr/lpd/lpd.8 @@ -0,0 +1,351 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\"	The Regents of the University of California.  All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd April 15, 2021 +.Dt LPD 8 +.Os +.Sh NAME +.Nm lpd +.Nd line printer spooler daemon +.Sh SYNOPSIS +.Nm +.Op Fl cdlpsFW46 +.Op Ar port# +.Sh DESCRIPTION +The +.Nm +utility +is the line printer daemon (spool area handler) and is normally invoked +at boot time from the +.Xr rc 8 +file. +It makes a single pass through the +.Xr printcap 5 +file to find out about the existing printers and +prints any files left after a crash. +It then uses the system calls +.Xr listen 2 +and +.Xr accept 2 +to receive requests to print files in the queue, +transfer files to the spooling area, display the queue, +or remove jobs from the queue. +In each case, it forks a child to handle +the request so the parent can continue to listen for more requests. +.Pp +Available options: +.Bl -tag -width Ds +.It Fl c +By default, if some remote host has a connection error while trying to +send a print request to +.Nm +on a local host, +.Nm +will only send error message to that remote host. +The +.Fl c +flag causes +.Nm +to also log all of those connection errors via +.Xr syslog 3 . +.It Fl d +Turn on +.Dv SO_DEBUG +on the Internet listening socket (see +.Xr setsockopt 2 ) . +.It Fl l +The +.Fl l +flag causes +.Nm +to log valid requests received from the network. +This can be useful +for debugging purposes. +.It Fl p +The +.Fl p +flag is a synonym for the +.Fl s +flag. +It is being deprecated, and may be removed in a +future version of +.Nm . +.It Fl s +The +.Fl s +(secure) flag causes +.Nm +not to open an Internet listening socket. +This means that +.Nm +will not accept any connections from any remote +hosts, although it will still accept print requests +from all local users. +.It Fl F +By default, +.Nm +will daemonize into the background. +The +.Fl F +flag causes +.Nm +to remain in the foreground. +Logging is still performed with +.Xr syslog 3 . +.It Fl W +By default, the +.Nm +daemon will only accept connections which originate +from a reserved-port (<1024) on the remote host. +The +.Fl W +flag causes +.Nm +to accept connections coming from any port. +This is can be useful when you want to accept print jobs +from certain implementations of lpr written for Windows. +.It Fl 4 +Inet only. +.It Fl 6 +Inet6 only. +.It Fl 46 +Inet and inet6 (default). +.It Ar "port#" +The Internet port number used to rendezvous +with other processes is normally obtained with +.Xr getservbyname 3 +but can be changed with the +.Ar port# +argument. +.El +.Pp +Access control is provided by two means. +First, all requests must come from +one of the machines listed in the file +.Pa /etc/hosts.equiv +or +.Pa /etc/hosts.lpd . +Second, if the +.Li rs +capability is specified in the +.Xr printcap 5 +entry for the printer being accessed, +.Em lpr +requests will only be honored for those users with accounts on the +machine with the printer. +.Pp +The file +.Em minfree +in each spool directory contains the number of kilobytes to leave free +so that the line printer queue will not completely fill the disk. +The +.Em minfree +file can be edited with your favorite text editor. +.Pp +The daemon begins processing files +after it has successfully set the lock for exclusive +access (described a bit later), +and scans the spool directory +for files beginning with +.Em cf . +Lines in each +.Em cf +file specify files to be printed or non-printing actions to be +performed. +Each such line begins with a key character +to specify what to do with the remainder of the line. +.Bl -tag -width Ds +.It J +Job Name. +String to be used for the job name on the burst page. +.It C +Classification. +String to be used for the classification line +on the burst page. +.It L +Literal. +The line contains identification info from +the password file and causes the banner page to be printed. +.It T +Title. +String to be used as the title for +.Xr pr 1 . +.It H +Host Name. +Name of the machine where +.Xr lpr 1 +was invoked. +.It P +Person. +Login name of the person who invoked +.Xr lpr 1 . +This is used to verify ownership by +.Xr lprm 1 . +.It M +Send mail to the specified user when the current print job completes. +.It f +Formatted File. +Name of a file to print which is already formatted. +.It l +Like ``f'' but passes control characters and does not make page breaks. +.It p +Name of a file to print using +.Xr pr 1 +as a filter. +.It t +Troff File. +The file contains +.Xr troff 1 Pq Pa ports/textproc/groff +output (cat phototypesetter commands). +.It n +Ditroff File. +The file contains device independent troff +output. +.It r +DVI File. +The file contains +.Tn Tex l +output +DVI format from Stanford. +.It g +Graph File. +The file contains data produced by +.Xr plot 3 . +.It c +Cifplot File. +The file contains data produced by +.Em cifplot . +.It v +The file contains a raster image. +.It r +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. +.It \&2 +Troff Font I. +Name of the font file to use instead of the default. +.It \&3 +Troff Font B. +Name of the font file to use instead of the default. +.It \&4 +Troff Font S. +Name of the font file to use instead of the default. +.It W +Width. +Changes the page width (in characters) used by +.Xr pr 1 +and the text filters. +.It I +Indent. +The number of characters to indent the output by (in ASCII). +.It U +Unlink. +Name of file to remove upon completion of printing. +.It N +File name. +The name of the file which is being printed, or a blank +for the standard input (when +.Xr lpr 1 +is invoked in a pipeline). +.It Z +Locale. +String to be used as the locale for +.Xr pr 1 . +.El +.Pp +If a file cannot be opened, a message will be logged via +.Xr syslog 3 +using the +.Em LOG_LPR +facility. +The +.Nm +utility will try up to 20 times +to reopen a file it expects to be there, after which it will +skip the file to be printed. +.Pp +The +.Nm +utility 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 +.Tn ASCII +form +and contains two lines. +The first is the process id of the daemon and the second is the control +file name of the current job being printed. +The second line is updated to +reflect the current status of +.Nm +for the programs +.Xr lpq 1 +and +.Xr lprm 1 . +.Sh FILES +.Bl -tag -width "/var/spool/*/minfree" -compact +.It Pa /etc/printcap +printer description file +.It Pa /var/spool/* +spool directories +.It Pa /var/spool/*/minfree +minimum free space to leave +.It Pa /dev/lp* +line printer devices +.It Pa /var/run/printer +socket for local requests +.It Pa /etc/hosts.equiv +lists machine names allowed printer access +.It Pa /etc/hosts.lpd +lists machine names allowed printer access, +but not under same administrative control. +.El +.Sh SEE ALSO +.Xr lpq 1 , +.Xr lpr 1 , +.Xr lprm 1 , +.Xr setsockopt 2 , +.Xr syslog 3 , +.Xr hosts.lpd 5 , +.Xr printcap 5 , +.Xr chkprintcap 8 , +.Xr lpc 8 , +.Xr pac 8 +.Rs +.\" 4.4BSD SMM:7 +.%A Ralph Campbell +.%T "4.2 BSD Line Printer Spooler Manual" +.Re +.Sh HISTORY +An +.Nm +daemon appeared in Version 6 AT&T UNIX. diff --git a/usr.sbin/lpr/lpd/lpd.c b/usr.sbin/lpr/lpd/lpd.c new file mode 100644 index 000000000000..089b8fedc2d5 --- /dev/null +++ b/usr.sbin/lpr/lpd/lpd.c @@ -0,0 +1,934 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1983, 1993, 1994 + *	The Regents of the University of California.  All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */ +/* + * lpd -- line printer daemon. + * + * Listen for a connection and perform the requested operation. + * Operations are: + *	\1printer\n + *		check the queue for jobs and print any found. + *	\2printer\n + *		receive a job from another machine and queue it. + *	\3printer [users ...] [jobs ...]\n + *		return the current state of the queue (short form). + *	\4printer [users ...] [jobs ...]\n + *		return the current state of the queue (long form). + *	\5printer person [users ...] [jobs ...]\n + *		remove jobs from the queue. + * + * 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 the spooling area. + *	3. Files in spooling area are owned by root, group spooling + *	   group, with mode 660. + *	4. lpd, lpq and lprm run 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. + */ + +#include <sys/param.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <netdb.h> +#include <unistd.h> +#include <syslog.h> +#include <signal.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <ctype.h> +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" +#include "extern.h" + +int	lflag;				/* log requests flag */ +int	sflag;				/* no incoming port flag */ +int	Fflag;				/* run in foreground flag */ +int	from_remote;			/* from remote socket */ + +int		 main(int argc, char **_argv); +static void	 reapchild(int _signo); +static void	 mcleanup(int _signo); +static void	 doit(void); +static void	 startup(void); +static void	 chkhost(struct sockaddr *_f, int _ch_opts); +static int	 ckqueue(struct printer *_pp); +static void	 fhosterr(int _ch_opts, char *_sysmsg, char *_usermsg); +static int	*socksetup(int _af, int _debuglvl); +static void	 usage(void); + +/* XXX from libc/net/rcmd.c */ +extern int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t, +				const char *, const char *); + +uid_t	uid, euid; + +#define LPD_NOPORTCHK	0001		/* skip reserved-port check */ +#define LPD_LOGCONNERR	0002		/* (sys)log connection errors */ +#define LPD_ADDFROMLINE	0004		/* just used for fhosterr() */ + +int +main(int argc, char **argv) +{ +	int ch_options, errs, f, funix, *finet, i, lfd, socket_debug; +	fd_set defreadfds; +	struct sockaddr_un un, fromunix; +	struct sockaddr_storage frominet; +	socklen_t fromlen; +	sigset_t omask, nmask; +	struct servent *sp, serv; +	int inet_flag = 0, inet6_flag = 0; + +	euid = geteuid();	/* these shouldn't be different */ +	uid = getuid(); + +	ch_options = 0; +	socket_debug = 0; +	gethostname(local_host, sizeof(local_host)); + +	progname = "lpd"; + +	if (euid != 0) +		errx(EX_NOPERM,"must run as root"); + +	errs = 0; +	while ((i = getopt(argc, argv, "cdlpswFW46")) != -1) +		switch (i) { +		case 'c': +			/* log all kinds of connection-errors to syslog */ +			ch_options |= LPD_LOGCONNERR; +			break; +		case 'd': +			socket_debug++; +			break; +		case 'l': +			lflag++; +			break; +		case 'p':		/* letter initially used for -s */ +			/* +			 * This will probably be removed with 5.0-release. +			 */ +			/* FALLTHROUGH */ +		case 's':		/* secure (no inet) */ +			sflag++; +			break; +		case 'w':		/* netbsd uses -w for maxwait */ +			/* +			 * This will be removed after the release of 4.4, as +			 * it conflicts with -w in netbsd's lpd.  For now it +			 * is just a warning, so we won't suddenly break lpd +			 * for anyone who is currently using the option. +			 */ +			syslog(LOG_WARNING, +			    "NOTE: the -w option has been renamed -W"); +			syslog(LOG_WARNING, +			    "NOTE: please change your lpd config to use -W"); +			/* FALLTHROUGH */ +		case 'F': +			Fflag++; +			break; +		case 'W': +			/* allow connections coming from a non-reserved port */ +			/* (done by some lpr-implementations for MS-Windows) */  +			ch_options |= LPD_NOPORTCHK; +			break; +		case '4': +			family = PF_INET; +			inet_flag++; +			break; +		case '6': +#ifdef INET6 +			family = PF_INET6; +			inet6_flag++; +#else +			errx(EX_USAGE, "lpd compiled sans INET6 (IPv6 support)"); +#endif +			break; +		/* +		 * The following options are not in FreeBSD (yet?), but are +		 * listed here to "reserve" them, because the option-letters +		 * are used by either NetBSD or OpenBSD (as of July 2001). +		 */  +		case 'b':		/* set bind-addr */ +		case 'n':		/* set max num of children */ +		case 'r':		/* allow 'of' for remote ptrs */ +					/* ...[not needed in freebsd] */ +			/* FALLTHROUGH */ +		default: +			errs++; +		} +	if (inet_flag && inet6_flag) +		family = PF_UNSPEC; +	argc -= optind; +	argv += optind; +	if (errs) +		usage(); + +	if (argc == 1) { +		if ((i = atoi(argv[0])) == 0) +			usage(); +		if (i < 0 || i > USHRT_MAX) +			errx(EX_USAGE, "port # %d is invalid", i); + +		serv.s_port = htons(i); +		sp = &serv; +		argc--; +	} else { +		sp = getservbyname("printer", "tcp"); +		if (sp == NULL) +			errx(EX_OSFILE, "printer/tcp: unknown service"); +	} + +	if (argc != 0) +		usage(); + +	/* +	 * We run chkprintcap right away to catch any errors and blat them +	 * to stderr while we still have it open, rather than sending them +	 * to syslog and leaving the user wondering why lpd started and +	 * then stopped.  There should probably be a command-line flag to +	 * ignore errors from chkprintcap. +	 */ +	{ +		pid_t pid; +		int status; +		pid = fork(); +		if (pid < 0) { +			err(EX_OSERR, "cannot fork"); +		} else if (pid == 0) {	/* child */ +			execl(_PATH_CHKPRINTCAP, _PATH_CHKPRINTCAP, (char *)0); +			err(EX_OSERR, "cannot execute %s", _PATH_CHKPRINTCAP); +		} +		if (waitpid(pid, &status, 0) < 0) { +			err(EX_OSERR, "cannot wait"); +		} +		if (WIFEXITED(status) && WEXITSTATUS(status) != 0) +			errx(EX_OSFILE, "%d errors in printcap file, exiting", +			     WEXITSTATUS(status)); +	} + +#ifdef DEBUG +	Fflag++; +#endif +	/* +	 * Set up standard environment by detaching from the parent +	 * if -F not specified +	 */ +	if (Fflag == 0) { +		daemon(0, 0); +	} + +	openlog("lpd", LOG_PID, LOG_LPR); +	syslog(LOG_INFO, "lpd startup: logging=%d%s%s", lflag, +	    socket_debug ? " dbg" : "", sflag ? " net-secure" : ""); +	(void) umask(0); +	/* +	 * NB: This depends on O_NONBLOCK semantics doing the right thing; +	 * i.e., applying only to the O_EXLOCK and not to the rest of the +	 * open/creation.  As of 1997-12-02, this is the case for commonly- +	 * used filesystems.  There are other places in this code which +	 * make the same assumption. +	 */ +	lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, +		   LOCK_FILE_MODE); +	if (lfd < 0) { +		if (errno == EWOULDBLOCK)	/* active daemon present */ +			exit(0); +		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); +		exit(1); +	} +	fcntl(lfd, F_SETFL, 0);	/* turn off non-blocking mode */ +	ftruncate(lfd, 0); +	/* +	 * write process id for others to know +	 */ +	sprintf(line, "%u\n", getpid()); +	f = strlen(line); +	if (write(lfd, line, f) != f) { +		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); +		exit(1); +	} +	signal(SIGCHLD, reapchild); +	/* +	 * Restart all the printers. +	 */ +	startup(); +	(void) unlink(_PATH_SOCKETNAME); +	funix = socket(AF_UNIX, SOCK_STREAM, 0); +	if (funix < 0) { +		syslog(LOG_ERR, "socket: %m"); +		exit(1); +	} + +	sigemptyset(&nmask); +	sigaddset(&nmask, SIGHUP); +	sigaddset(&nmask, SIGINT); +	sigaddset(&nmask, SIGQUIT); +	sigaddset(&nmask, SIGTERM); +	sigprocmask(SIG_BLOCK, &nmask, &omask); + +	(void) umask(077); +	signal(SIGHUP, mcleanup); +	signal(SIGINT, mcleanup); +	signal(SIGQUIT, mcleanup); +	signal(SIGTERM, mcleanup); +	memset(&un, 0, sizeof(un)); +	un.sun_family = AF_UNIX; +	strcpy(un.sun_path, _PATH_SOCKETNAME); +#ifndef SUN_LEN +#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) +#endif +	if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) { +		syslog(LOG_ERR, "ubind: %m"); +		exit(1); +	} +	(void) umask(0); +	sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); +	FD_ZERO(&defreadfds); +	FD_SET(funix, &defreadfds); +	listen(funix, 5); +	if (sflag == 0) { +		finet = socksetup(family, socket_debug); +	} else +		finet = NULL;	/* pretend we couldn't open TCP socket. */ +	if (finet) { +		for (i = 1; i <= *finet; i++) { +			FD_SET(finet[i], &defreadfds); +			listen(finet[i], 5); +		} +	} +	/* +	 * Main loop: accept, do a request, continue. +	 */ +	memset(&frominet, 0, sizeof(frominet)); +	memset(&fromunix, 0, sizeof(fromunix)); +	if (lflag) +		syslog(LOG_INFO, "lpd startup: ready to accept requests"); +	/* +	 * XXX - should be redone for multi-protocol +	 */ +	for (;;) { +		int domain, nfds, s; +		fd_set readfds; + +		FD_COPY(&defreadfds, &readfds); +		nfds = select(20, &readfds, 0, 0, 0); +		if (nfds <= 0) { +			if (nfds < 0 && errno != EINTR) +				syslog(LOG_WARNING, "select: %m"); +			continue; +		} +		domain = -1;		    /* avoid compile-time warning */ +		s = -1;			    /* avoid compile-time warning */ +		if (FD_ISSET(funix, &readfds)) { +			domain = AF_UNIX, fromlen = sizeof(fromunix); +			s = accept(funix, +			    (struct sockaddr *)&fromunix, &fromlen); + 		} else { +                        for (i = 1; i <= *finet; i++)  +				if (FD_ISSET(finet[i], &readfds)) { +					domain = AF_INET; +					fromlen = sizeof(frominet); +					s = accept(finet[i], +					    (struct sockaddr *)&frominet, +					    &fromlen); +				} +		} +		if (s < 0) { +			if (errno != EINTR) +				syslog(LOG_WARNING, "accept: %m"); +			continue; +		} +		if (fork() == 0) { +			/* +			 * Note that printjob() also plays around with +			 * signal-handling routines, and may need to be +			 * changed when making changes to signal-handling. +			 */ +			signal(SIGCHLD, SIG_DFL); +			signal(SIGHUP, SIG_IGN); +			signal(SIGINT, SIG_IGN); +			signal(SIGQUIT, SIG_IGN); +			signal(SIGTERM, SIG_IGN); +			(void) close(funix); +			if (sflag == 0 && finet) { +                        	for (i = 1; i <= *finet; i++)  +					(void)close(finet[i]); +			} +			dup2(s, STDOUT_FILENO); +			(void) close(s); +			if (domain == AF_INET) { +				/* for both AF_INET and AF_INET6 */ +				from_remote = 1; + 				chkhost((struct sockaddr *)&frominet, +				    ch_options); +			} else +				from_remote = 0; +			doit(); +			exit(0); +		} +		(void) close(s); +	} +} + +static void +reapchild(int signo __unused) +{ +	int status; + +	while (wait3(&status, WNOHANG, 0) > 0) +		; +} + +static void +mcleanup(int signo) +{ +	/* +	 * XXX syslog(3) is not signal-safe. +	 */ +	if (lflag) { +		if (signo) +			syslog(LOG_INFO, "exiting on signal %d", signo); +		else +			syslog(LOG_INFO, "exiting"); +	} +	unlink(_PATH_SOCKETNAME); +	exit(0); +} + +/* + * Stuff for handling job specifications + */ +char	*user[MAXUSERS];	/* users to process */ +int	users;			/* # of users in user array */ +int	requ[MAXREQUESTS];	/* job number of spool entries */ +int	requests;		/* # of spool requests */ +char	*person;		/* name of person doing lprm */ + +		 /* buffer to hold the client's machine-name */ +static char	 frombuf[MAXHOSTNAMELEN]; +char	cbuf[BUFSIZ];		/* command line buffer */ +const char	*cmdnames[] = { +	"null", +	"printjob", +	"recvjob", +	"displayq short", +	"displayq long", +	"rmjob" +}; + +static void +doit(void) +{ +	char *cp, *printer; +	int n; +	int status; +	struct printer myprinter, *pp = &myprinter; + +	init_printer(&myprinter); + +	for (;;) { +		cp = cbuf; +		do { +			if (cp >= &cbuf[sizeof(cbuf) - 1]) +				fatal(0, "Command line too long"); +			if ((n = read(STDOUT_FILENO, cp, 1)) != 1) { +				if (n < 0) +					fatal(0, "Lost connection"); +				return; +			} +		} while (*cp++ != '\n'); +		*--cp = '\0'; +		cp = cbuf; +		if (lflag) { +			if (*cp >= '\1' && *cp <= '\5') +				syslog(LOG_INFO, "%s requests %s %s", +					from_host, cmdnames[(u_char)*cp], cp+1); +			else +				syslog(LOG_INFO, "bad request (%d) from %s", +					*cp, from_host); +		} +		switch (*cp++) { +		case CMD_CHECK_QUE: /* check the queue, print any jobs there */ +			startprinting(cp); +			break; +		case CMD_TAKE_THIS: /* receive files to be queued */ +			if (!from_remote) { +				syslog(LOG_INFO, "illegal request (%d)", *cp); +				exit(1); +			} +			recvjob(cp); +			break; +		case CMD_SHOWQ_SHORT: /* display the queue (short form) */ +		case CMD_SHOWQ_LONG: /* display the queue (long form) */ +			/* XXX - this all needs to be redone. */ +			printer = cp; +			while (*cp) { +				if (*cp != ' ') { +					cp++; +					continue; +				} +				*cp++ = '\0'; +				while (isspace(*cp)) +					cp++; +				if (*cp == '\0') +					break; +				if (isdigit(*cp)) { +					if (requests >= MAXREQUESTS) +						fatal(0, "Too many requests"); +					requ[requests++] = atoi(cp); +				} else { +					if (users >= MAXUSERS) +						fatal(0, "Too many users"); +					user[users++] = cp; +				} +			} +			status = getprintcap(printer, pp); +			if (status < 0) +				fatal(pp, "%s", pcaperr(status)); +			displayq(pp, cbuf[0] == CMD_SHOWQ_LONG); +			exit(0); +		case CMD_RMJOB:	/* remove a job from the queue */ +			if (!from_remote) { +				syslog(LOG_INFO, "illegal request (%d)", *cp); +				exit(1); +			} +			printer = cp; +			while (*cp && *cp != ' ') +				cp++; +			if (!*cp) +				break; +			*cp++ = '\0'; +			person = cp; +			while (*cp) { +				if (*cp != ' ') { +					cp++; +					continue; +				} +				*cp++ = '\0'; +				while (isspace(*cp)) +					cp++; +				if (*cp == '\0') +					break; +				if (isdigit(*cp)) { +					if (requests >= MAXREQUESTS) +						fatal(0, "Too many requests"); +					requ[requests++] = atoi(cp); +				} else { +					if (users >= MAXUSERS) +						fatal(0, "Too many users"); +					user[users++] = cp; +				} +			} +			rmjob(printer); +			break; +		} +		fatal(0, "Illegal service request"); +	} +} + +/* + * Make a pass through the printcap database and start printing any + * files left from the last time the machine went down. + */ +static void +startup(void) +{ +	int pid, status, more; +	struct printer myprinter, *pp = &myprinter; + +	more = firstprinter(pp, &status); +	if (status) +		goto errloop; +	while (more) { +		if (ckqueue(pp) <= 0) { +			goto next; +		} +		if (lflag) +			syslog(LOG_INFO, "lpd startup: work for %s", +			    pp->printer); +		if ((pid = fork()) < 0) { +			syslog(LOG_WARNING, "lpd startup: cannot fork for %s", +			    pp->printer); +			mcleanup(0); +		} +		if (pid == 0) { +			lastprinter(); +			printjob(pp); +			/* NOTREACHED */ +		} +		do { +next: +			more = nextprinter(pp, &status); +errloop: +			if (status) +				syslog(LOG_WARNING,  +				    "lpd startup: printcap entry for %s has errors, skipping", +				    pp->printer ? pp->printer : "<noname?>"); +		} while (more && status); +	} +} + +/* + * Make sure there's some work to do before forking off a child + */ +static int +ckqueue(struct printer *pp) +{ +	register struct dirent *d; +	DIR *dirp; +	char *spooldir; + +	spooldir = pp->spool_dir; +	if ((dirp = opendir(spooldir)) == NULL) +		return (-1); +	while ((d = readdir(dirp)) != NULL) { +		if (d->d_name[0] != 'c' || d->d_name[1] != 'f') +			continue;	/* daemon control files only */ +		closedir(dirp); +		return (1);		/* found something */ +	} +	closedir(dirp); +	return (0); +} + +#define DUMMY ":nobody::" + +/* + * Check to see if the host connecting to this host has access to any + * lpd services on this host. + */ +static void +chkhost(struct sockaddr *f, int ch_opts) +{ +	struct addrinfo hints, *res, *r; +	register FILE *hostf; +	char hostbuf[NI_MAXHOST], ip[NI_MAXHOST]; +	char serv[NI_MAXSERV]; +	char *syserr, *usererr; +	int error, errsav, fpass, good; + +	from_host = ".na."; + +	/* Need real hostname for temporary filenames */ +	error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, +	    NI_NAMEREQD); +	if (error) { +		errsav = error; +		error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), +		    NULL, 0, NI_NUMERICHOST); +		if (error) { +			asprintf(&syserr, +			    "can not determine hostname for remote host (%d,%d)", +			    errsav, error); +			asprintf(&usererr, +			    "Host name for your address is not known"); +			fhosterr(ch_opts, syserr, usererr); +			/* NOTREACHED */ +		} +		asprintf(&syserr, +		    "Host name for remote host (%s) not known (%d)", +		    hostbuf, errsav); +		asprintf(&usererr, +		    "Host name for your address (%s) is not known", +		    hostbuf); +		fhosterr(ch_opts, syserr, usererr); +		/* NOTREACHED */ +	} + +	strlcpy(frombuf, hostbuf, sizeof(frombuf)); +	from_host = frombuf; +	ch_opts |= LPD_ADDFROMLINE; + +	/* Need address in stringform for comparison (no DNS lookup here) */ +	error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, +	    NI_NUMERICHOST); +	if (error) { +		asprintf(&syserr, "Cannot print IP address (error %d)", +		    error); +		asprintf(&usererr, "Cannot print IP address for your host"); +		fhosterr(ch_opts, syserr, usererr); +		/* NOTREACHED */ +	} +	from_ip = strdup(hostbuf); + +	/* Reject numeric addresses */ +	memset(&hints, 0, sizeof(hints)); +	hints.ai_family = family; +	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/ +	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; +	if (getaddrinfo(from_host, NULL, &hints, &res) == 0) { +		freeaddrinfo(res); +		/* This syslog message already includes from_host */ +		ch_opts &= ~LPD_ADDFROMLINE; +		asprintf(&syserr, "reverse lookup results in non-FQDN %s", +		    from_host); +		/* same message to both syslog and remote user */ +		fhosterr(ch_opts, syserr, syserr); +		/* NOTREACHED */ +	} + +	/* Check for spoof, ala rlogind */ +	memset(&hints, 0, sizeof(hints)); +	hints.ai_family = family; +	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/ +	error = getaddrinfo(from_host, NULL, &hints, &res); +	if (error) { +		asprintf(&syserr, "dns lookup for address %s failed: %s", +		    from_ip, gai_strerror(error)); +		asprintf(&usererr, "hostname for your address (%s) unknown: %s", +		    from_ip, gai_strerror(error)); +		fhosterr(ch_opts, syserr, usererr); +		/* NOTREACHED */ +	} +	good = 0; +	for (r = res; good == 0 && r; r = r->ai_next) { +		error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip), +		    NULL, 0, NI_NUMERICHOST); +		if (!error && !strcmp(from_ip, ip)) +			good = 1; +	} +	if (res) +		freeaddrinfo(res); +	if (good == 0) { +		asprintf(&syserr, "address for remote host (%s) not matched", +		    from_ip); +		asprintf(&usererr, +		    "address for your hostname (%s) not matched", from_ip); +		fhosterr(ch_opts, syserr, usererr); +		/* NOTREACHED */ +	} + +	fpass = 1; +	hostf = fopen(_PATH_HOSTSEQUIV, "r"); +again: +	if (hostf) { +		if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) { +			(void) fclose(hostf); +			goto foundhost; +		} +		(void) fclose(hostf); +	} +	if (fpass == 1) { +		fpass = 2; +		hostf = fopen(_PATH_HOSTSLPD, "r"); +		goto again; +	} +	/* This syslog message already includes from_host */ +	ch_opts &= ~LPD_ADDFROMLINE; +	asprintf(&syserr, "refused connection from %s, sip=%s", from_host, +	    from_ip); +	asprintf(&usererr, +	    "Print-services are not available to your host (%s).", from_host); +	fhosterr(ch_opts, syserr, usererr); +	/* NOTREACHED */ + +foundhost: +	if (ch_opts & LPD_NOPORTCHK) +		return;			/* skip the reserved-port check */ + +	error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv), +	    NI_NUMERICSERV); +	if (error) { +		/* same message to both syslog and remote user */ +		asprintf(&syserr, "malformed from-address (%d)", error); +		fhosterr(ch_opts, syserr, syserr); +		/* NOTREACHED */ +	} + +	if (atoi(serv) >= IPPORT_RESERVED) { +		/* same message to both syslog and remote user */ +		asprintf(&syserr, "connected from invalid port (%s)", serv); +		fhosterr(ch_opts, syserr, syserr); +		/* NOTREACHED */ +	} +} + +/* + * Handle fatal errors in chkhost.  The first message will optionally be + * sent to syslog, the second one is sent to the connecting host. + * + * The idea is that the syslog message is meant for an administrator of a + * print server (the host receiving connections), while the usermsg is meant + * for a remote user who may or may not be clueful, and may or may not be + * doing something nefarious.  Some remote users (eg, MS-Windows...) may not + * even see whatever message is sent, which is why there's the option to + * start 'lpd' with the connection-errors also sent to syslog. + * + * Given that hostnames can theoretically be fairly long (well, over 250 + * bytes), it would probably be helpful to have the 'from_host' field at + * the end of any error messages which include that info. + * + * These are Fatal host-connection errors, so this routine does not return. + */ +static void +fhosterr(int ch_opts, char *sysmsg, char *usermsg) +{ + +	/* +	 * If lpd was started up to print connection errors, then write +	 * the syslog message before the user message. +	 * And for many of the syslog messages, it is helpful to first +	 * write the from_host (if it is known) as a separate syslog +	 * message, since the hostname may be so long. +	 */ +	if (ch_opts & LPD_LOGCONNERR) { +		if (ch_opts & LPD_ADDFROMLINE) { +		    syslog(LOG_WARNING, "for connection from %s:", from_host); +		} +		syslog(LOG_WARNING, "%s", sysmsg); +	} + +	/* +	 * Now send the error message to the remote host which is trying +	 * to make the connection. +	 */ +	printf("%s [@%s]: %s\n", progname, local_host, usermsg); +	fflush(stdout); + +	/*  +	 * Add a minimal delay before exiting (and disconnecting from the +	 * sending-host).  This is just in case that machine responds by +	 * INSTANTLY retrying (and instantly re-failing...).  This may also +	 * give the other side more time to read the error message. +	 */ +	sleep(2);			/* a paranoid throttling measure */ +	exit(1); +} + +/* setup server socket for specified address family */ +/* if af is PF_UNSPEC more than one socket may be returned */ +/* the returned list is dynamically allocated, so caller needs to free it */ +static int * +socksetup(int af, int debuglvl) +{ +	struct addrinfo hints, *res, *r; +	int error, maxs, *s, *socks; +	const int on = 1; + +	memset(&hints, 0, sizeof(hints)); +	hints.ai_flags = AI_PASSIVE; +	hints.ai_family = af; +	hints.ai_socktype = SOCK_STREAM; +	error = getaddrinfo(NULL, "printer", &hints, &res); +	if (error) { +		syslog(LOG_ERR, "%s", gai_strerror(error)); +		mcleanup(0); +	} + +	/* Count max number of sockets we may open */ +	for (maxs = 0, r = res; r; r = r->ai_next, maxs++) +		; +	socks = malloc((maxs + 1) * sizeof(int)); +	if (!socks) { +		syslog(LOG_ERR, "couldn't allocate memory for sockets"); +		mcleanup(0); +	} + +	*socks = 0;   /* num of sockets counter at start of array */ +	s = socks + 1; +	for (r = res; r; r = r->ai_next) { +		*s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); +		if (*s < 0) { +			syslog(LOG_DEBUG, "socket(): %m"); +			continue; +		} +		if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) +		    < 0) { +			syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m"); +			close(*s); +			continue; +		} +		if (debuglvl) +			if (setsockopt(*s, SOL_SOCKET, SO_DEBUG, &debuglvl, +			    sizeof(debuglvl)) < 0) { +				syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); +				close(*s); +				continue; +			} +		if (r->ai_family == AF_INET6) { +			if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, +				       &on, sizeof(on)) < 0) { +				syslog(LOG_ERR, +				       "setsockopt (IPV6_V6ONLY): %m"); +				close(*s); +				continue; +			} +		} +		if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { +			syslog(LOG_DEBUG, "bind(): %m"); +			close(*s); +			continue; +		} +		(*socks)++; +		s++; +	} + +	if (res) +		freeaddrinfo(res); + +	if (*socks == 0) { +		syslog(LOG_ERR, "Couldn't bind to any socket"); +		free(socks); +		mcleanup(0); +	} +	return(socks); +} + +static void +usage(void) +{ +#ifdef INET6 +	fprintf(stderr, "usage: lpd [-cdlsFW46] [port#]\n"); +#else +	fprintf(stderr, "usage: lpd [-cdlsFW] [port#]\n"); +#endif +	exit(EX_USAGE); +} diff --git a/usr.sbin/lpr/lpd/lpdchar.c b/usr.sbin/lpr/lpd/lpdchar.c new file mode 100644 index 000000000000..a9d06fe7f600 --- /dev/null +++ b/usr.sbin/lpr/lpd/lpdchar.c @@ -0,0 +1,1062 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1983, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */ +/* + *	Character set for line printer daemon + */ +#include "lp.local.h" +#include "extern.h" + +#define c_______ 0 +#define c______1 01 +#define c_____1_ 02 +#define c____1__ 04 +#define c____11_ 06 +#define c___1___ 010 +#define c___1__1 011 +#define c___1_1_ 012 +#define c___11__ 014 +#define c__1____ 020 +#define c__1__1_ 022 +#define c__1_1__ 024 +#define c__11___ 030 +#define c__111__ 034 +#define c__111_1 035 +#define c__1111_ 036 +#define c__11111 037 +#define c_1_____ 040 +#define c_1____1 041 +#define c_1___1_ 042 +#define c_1__1__ 044 +#define c_1_1___ 050 +#define c_1_1__1 051 +#define c_1_1_1_ 052 +#define c_11____ 060 +#define c_11_11_ 066 +#define c_111___ 070 +#define c_111__1 071 +#define c_111_1_ 072 +#define c_1111__ 074 +#define c_1111_1 075 +#define c_11111_ 076 +#define c_111111 077 +#define c1______ 0100 +#define c1_____1 0101 +#define c1____1_ 0102 +#define c1____11 0103 +#define c1___1__ 0104 +#define c1___1_1 0105 +#define c1___11_ 0106 +#define c1__1___ 0110 +#define c1__1__1 0111 +#define c1__11_1 0115 +#define c1__1111 0117 +#define c1_1____ 0120 +#define c1_1___1 0121 +#define c1_1_1_1 0125 +#define c1_1_11_ 0126 +#define c1_111__ 0134 +#define c1_1111_ 0136 +#define c11____1 0141 +#define c11___1_ 0142 +#define c11___11 0143 +#define c11_1___ 0150 +#define c11_1__1 0151 +#define c111_11_ 0166 +#define c1111___ 0170 +#define c11111__ 0174 +#define c111111_ 0176 +#define c1111111 0177 + +char scnkey[][HEIGHT] =	/* this is relatively easy to modify */ +			/* just look: */ +{ +	{ c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______ },			/*   */ + +	{ c__11___, +	  c__11___, +	  c__11___, +	  c__11___, +	  c__11___, +	  c_______, +	  c_______, +	  c__11___, +	  c__11___ },			/* ! */ + +	{ c_1__1__, +	  c_1__1__, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______ },			/* " */ + +	{ c_______, +	  c__1_1__, +	  c__1_1__, +	  c1111111, +	  c__1_1__, +	  c1111111, +	  c__1_1__, +	  c__1_1__, +	  c_______ },			/* # */ + +	{ c___1___, +	  c_11111_, +	  c1__1__1, +	  c1__1___, +	  c_11111_, +	  c___1__1, +	  c1__1__1, +	  c_11111_, +	  c___1___ },			/* $ */ + + 	{ c_1_____, + 	  c1_1___1, + 	  c_1___1_, + 	  c____1__, + 	  c___1___, + 	  c__1____, + 	  c_1___1_, + 	  c1___1_1, + 	  c_____1_ },			/* % */ + + 	{ c_11____, + 	  c1__1___, + 	  c1___1__, + 	  c_1_1___, + 	  c__1____, + 	  c_1_1__1, + 	  c1___11_, + 	  c1___11_, + 	  c_111__1 },			/* & */ + + 	{ c___11__, + 	  c___11__, + 	  c___1___, + 	  c__1____, + 	  c_______, + 	  c_______, + 	  c_______, + 	  c_______, + 	  c_______ },			/* ' */ + + 	{ c____1__, + 	  c___1___, + 	  c__1____, + 	  c__1____, + 	  c__1____, + 	  c__1____, + 	  c__1____, + 	  c___1___, + 	  c____1__ },			/* ( */ + + 	{ c__1____, + 	  c___1___, + 	  c____1__, + 	  c____1__, + 	  c____1__, + 	  c____1__, + 	  c____1__, + 	  c___1___, + 	  c__1____ },			/* ) */ + + 	{ c_______, + 	  c___1___, + 	  c1__1__1, + 	  c_1_1_1_, + 	  c__111__, + 	  c_1_1_1_, + 	  c1__1__1, + 	  c___1___, + 	  c_______ },			/* * */ + + 	{ c_______, + 	  c___1___, + 	  c___1___, + 	  c___1___, + 	  c1111111, + 	  c___1___, + 	  c___1___, + 	  c___1___, + 	  c_______ },			/* + */ + + 	{ c_______, + 	  c_______, + 	  c_______, + 	  c_______, + 	  c__11___, + 	  c__11___, + 	  c__1____, + 	  c_1_____, + 	  c_______ },			/* , */ + + 	{ c_______, + 	  c_______, + 	  c_______, + 	  c_______, + 	  c1111111, + 	  c_______, + 	  c_______, + 	  c_______, + 	  c_______ },			/* - */ + + 	{ c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c__11___, +	  c__11___ },			/* . */ + +	{ c_______, +	  c______1, +	  c_____1_, +	  c____1__, +	  c___1___, +	  c__1____, +	  c_1_____, +	  c1______, +	  c_______ },			/* / */ + +	{ c_11111_, +	  c1_____1, +	  c1____11, +	  c1___1_1, +	  c1__1__1, +	  c1_1___1, +	  c11____1, +	  c1_____1, +	  c_11111_ },			/* 0 */ + +	{ c___1___, +	  c__11___, +	  c_1_1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c_11111_ },			/* 1 */ + +	{ c_11111_, +	  c1_____1, +	  c______1, +	  c_____1_, +	  c__111__, +	  c_1_____, +	  c1______, +	  c1______, +	  c1111111 },			/* 2 */ + +	{ c_11111_, +	  c1_____1, +	  c______1, +	  c______1, +	  c__1111_, +	  c______1, +	  c______1, +	  c1_____1, +	  c_11111_ },			/* 3 */ + +	{ c_____1_, +	  c____11_, +	  c___1_1_, +	  c__1__1_, +	  c_1___1_, +	  c1____1_, +	  c1111111, +	  c_____1_, +	  c_____1_ },			/* 4 */ + +	{ c1111111, +	  c1______, +	  c1______, +	  c11111__, +	  c_____1_, +	  c______1, +	  c______1, +	  c1____1_, +	  c_1111__ },			/* 5 */ + +	{ c__1111_, +	  c_1_____, +	  c1______, +	  c1______, +	  c1_1111_, +	  c11____1, +	  c1_____1, +	  c1_____1, +	  c_11111_ },			/* 6 */ + +	{ c1111111, +	  c1_____1, +	  c_____1_, +	  c____1__, +	  c___1___, +	  c__1____, +	  c__1____, +	  c__1____, +	  c__1____ },			/* 7 */ + +	{ c_11111_, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c_11111_, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c_11111_ },			/* 8 */ + +	{ c_11111_, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c_111111, +	  c______1, +	  c______1, +	  c1_____1, +	  c_1111__ },			/* 9 */ + +	{ c_______, +	  c_______, +	  c_______, +	  c__11___, +	  c__11___, +	  c_______, +	  c_______, +	  c__11___, +	  c__11___ },			/* : */ + + +	{ c__11___, +	  c__11___, +	  c_______, +	  c_______, +	  c__11___, +	  c__11___, +	  c__1____, +	  c_1_____, +	  c_______ },			/* ; */ + +	{ c____1__, +	  c___1___, +	  c__1____, +	  c_1_____, +	  c1______, +	  c_1_____, +	  c__1____, +	  c___1___, +	  c____1__ },			/* < */ + +	{ c_______, +	  c_______, +	  c_______, +	  c1111111, +	  c_______, +	  c1111111, +	  c_______, +	  c_______, +	  c_______ },			/* = */ + +	{ c__1____, +	  c___1___, +	  c____1__, +	  c_____1_, +	  c______1, +	  c_____1_, +	  c____1__, +	  c___1___, +	  c__1____ },			/* > */ + +	{ c__1111_, +	  c_1____1, +	  c_1____1, +	  c______1, +	  c____11_, +	  c___1___, +	  c___1___, +	  c_______, +	  c___1___ },			/* ? */ + +	{ c__1111_, +	  c_1____1, +	  c1__11_1, +	  c1_1_1_1, +	  c1_1_1_1, +	  c1_1111_, +	  c1______, +	  c_1____1, +	  c__1111_ },			/* @ */ + +	{ c__111__, +	  c_1___1_, +	  c1_____1, +	  c1_____1, +	  c1111111, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1 },			/* A */ + +	{ c111111_, +	  c_1____1, +	  c_1____1, +	  c_1____1, +	  c_11111_, +	  c_1____1, +	  c_1____1, +	  c_1____1, +	  c111111_ },			/* B */ + +	{ c__1111_, +	  c_1____1, +	  c1______, +	  c1______, +	  c1______, +	  c1______, +	  c1______, +	  c_1____1, +	  c__1111_ },			/* C */ + +	{ c11111__, +	  c_1___1_, +	  c_1____1, +	  c_1____1, +	  c_1____1, +	  c_1____1, +	  c_1____1, +	  c_1___1_, +	  c11111__ },			/* D */ + +	{ c1111111, +	  c1______, +	  c1______, +	  c1______, +	  c111111_, +	  c1______, +	  c1______, +	  c1______, +	  c1111111 },			/* E */ + +	{ c1111111, +	  c1______, +	  c1______, +	  c1______, +	  c111111_, +	  c1______, +	  c1______, +	  c1______, +	  c1______ },			/* F */ + +	{ c__1111_, +	  c_1____1, +	  c1______, +	  c1______, +	  c1______, +	  c1__1111, +	  c1_____1, +	  c_1____1, +	  c__1111_ },			/* G */ + +	{ c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1111111, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1 },			/* H */ + +	{ c_11111_, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c_11111_ },			/* I */ + +	{ c__11111, +	  c____1__, +	  c____1__, +	  c____1__, +	  c____1__, +	  c____1__, +	  c____1__, +	  c1___1__, +	  c_111___ },			/* J */ + +	{ c1_____1, +	  c1____1_, +	  c1___1__, +	  c1__1___, +	  c1_1____, +	  c11_1___, +	  c1___1__, +	  c1____1_, +	  c1_____1 },			/* K */ + +	{ c1______, +	  c1______, +	  c1______, +	  c1______, +	  c1______, +	  c1______, +	  c1______, +	  c1______, +	  c1111111 },			/* L */ + +	{ c1_____1, +	  c11___11, +	  c1_1_1_1, +	  c1__1__1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1 },			/* M */ + +	{ c1_____1, +	  c11____1, +	  c1_1___1, +	  c1__1__1, +	  c1___1_1, +	  c1____11, +	  c1_____1, +	  c1_____1, +	  c1_____1 },			/* N */ + +	{ c__111__, +	  c_1___1_, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c_1___1_, +	  c__111__ },			/* O */ + +	{ c111111_, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c111111_, +	  c1______, +	  c1______, +	  c1______, +	  c1______ },			/* P */ + +	{ c__111__, +	  c_1___1_, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1__1__1, +	  c1___1_1, +	  c_1___1_, +	  c__111_1 },			/* Q */ + +	{ c111111_, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c111111_, +	  c1__1___, +	  c1___1__, +	  c1____1_, +	  c1_____1 },			/* R */ + +	{ c_11111_, +	  c1_____1, +	  c1______, +	  c1______, +	  c_11111_, +	  c______1, +	  c______1, +	  c1_____1, +	  c_11111_ },			/* S */ + +	{ c1111111, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___ },			/* T */ + +	{ c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c_11111_ },			/* U */ + +	{ c1_____1, +	  c1_____1, +	  c1_____1, +	  c_1___1_, +	  c_1___1_, +	  c__1_1__, +	  c__1_1__, +	  c___1___, +	  c___1___ },			/* V */ + +	{ c1_____1, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c1__1__1, +	  c1__1__1, +	  c1_1_1_1, +	  c11___11, +	  c1_____1 },			/* W */ + +	{ c1_____1, +	  c1_____1, +	  c_1___1_, +	  c__1_1__, +	  c___1___, +	  c__1_1__, +	  c_1___1_, +	  c1_____1, +	  c1_____1 },			/* X */ + +	{ c1_____1, +	  c1_____1, +	  c_1___1_, +	  c__1_1__, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___ },			/* Y */ + +	{ c1111111, +	  c______1, +	  c_____1_, +	  c____1__, +	  c___1___, +	  c__1____, +	  c_1_____, +	  c1______, +	  c1111111 },			/* Z */ + +	{ c_1111__, +	  c_1_____, +	  c_1_____, +	  c_1_____, +	  c_1_____, +	  c_1_____, +	  c_1_____, +	  c_1_____, +	  c_1111__ },			/* [ */ + +	{ c_______, +	  c1______, +	  c_1_____, +	  c__1____, +	  c___1___, +	  c____1__, +	  c_____1_, +	  c______1, +	  c_______ },			/* \ */ + +	{ c__1111_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c__1111_ },			/* ] */ + +	{ c___1___, +	  c__1_1__, +	  c_1___1_, +	  c1_____1, +	  c_______, +	  c_______, +	  c_______, +	  c_______ },			/* ^ */ + +	{ c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c1111111, +	  c_______ },			/* _ */ + +	{ c__11___, +	  c__11___, +	  c___1___, +	  c____1__, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______ },			/* ` */ + +	{ c_______, +	  c_______, +	  c_______, +	  c_1111__, +	  c_____1_, +	  c_11111_, +	  c1_____1, +	  c1____11, +	  c_1111_1 },			/* a */ + +	{ c1______, +	  c1______, +	  c1______, +	  c1_111__, +	  c11___1_, +	  c1_____1, +	  c1_____1, +	  c11___1_, +	  c1_111__ },			/* b */ + +	{ c_______, +	  c_______, +	  c_______, +	  c_1111__, +	  c1____1_, +	  c1______, +	  c1______, +	  c1____1_, +	  c_1111__ },			/* c */ + +	{ c_____1_, +	  c_____1_, +	  c_____1_, +	  c_111_1_, +	  c1___11_, +	  c1____1_, +	  c1____1_, +	  c1___11_, +	  c_111_1_ },			/* d */ + +	{ c_______, +	  c_______, +	  c_______, +	  c_1111__, +	  c1____1_, +	  c111111_, +	  c1______, +	  c1____1_, +	  c_1111__ },			/* e */ + +	{ c___11__, +	  c__1__1_, +	  c__1____, +	  c__1____, +	  c11111__, +	  c__1____, +	  c__1____, +	  c__1____, +	  c__1____ },			/* f */ + +	{ c_111_1_, +	  c1___11_, +	  c1____1_, +	  c1____1_, +	  c1___11_, +	  c_111_1_, +	  c_____1_, +	  c1____1_, +	  c_1111__ },			/* g */ + +	{ c1______, +	  c1______, +	  c1______, +	  c1_111__, +	  c11___1_, +	  c1____1_, +	  c1____1_, +	  c1____1_, +	  c1____1_ },			/* h */ + +	{ c_______, +	  c___1___, +	  c_______, +	  c__11___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c__111__ },			/* i */ + +	{ c____11_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c_____1_, +	  c_1___1_, +	  c__111__ },			/* j */ + +	{ c1______, +	  c1______, +	  c1______, +	  c1___1__, +	  c1__1___, +	  c1_1____, +	  c11_1___, +	  c1___1__, +	  c1____1_ },			/* k */ + +	{ c__11___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c__111__ },			/* l */ + +	{ c_______, +	  c_______, +	  c_______, +	  c1_1_11_, +	  c11_1__1, +	  c1__1__1, +	  c1__1__1, +	  c1__1__1, +	  c1__1__1 },			/* m */ + +	{ c_______, +	  c_______, +	  c_______, +	  c1_111__, +	  c11___1_, +	  c1____1_, +	  c1____1_, +	  c1____1_, +	  c1____1_ },			/* n */ + +	{ c_______, +	  c_______, +	  c_______, +	  c_1111__, +	  c1____1_, +	  c1____1_, +	  c1____1_, +	  c1____1_, +	  c_1111__ },			/* o */ + +	{ c1_111__, +	  c11___1_, +	  c1____1_, +	  c1____1_, +	  c11___1_, +	  c1_111__, +	  c1______, +	  c1______, +	  c1______ },			/* p */ + +	{ c_111_1_, +	  c1___11_, +	  c1____1_, +	  c1____1_, +	  c1___11_, +	  c_111_1_, +	  c_____1_, +	  c_____1_, +	  c_____1_ },			/* q */ + +	{ c_______, +	  c_______, +	  c_______, +	  c1_111__, +	  c11___1_, +	  c1______, +	  c1______, +	  c1______, +	  c1______ },			/* r */ + +	{ c_______, +	  c_______, +	  c_______, +	  c_1111__, +	  c1____1_, +	  c_11____, +	  c___11__, +	  c1____1_, +	  c_1111__ },			/* s */ + +	{ c_______, +	  c__1____, +	  c__1____, +	  c11111__, +	  c__1____, +	  c__1____, +	  c__1____, +	  c__1__1_, +	  c___11__ },			/* t */ + +	{ c_______, +	  c_______, +	  c_______, +	  c1____1_, +	  c1____1_, +	  c1____1_, +	  c1____1_, +	  c1___11_, +	  c_111_1_ },			/* u */ + +	{ c_______, +	  c_______, +	  c_______, +	  c1_____1, +	  c1_____1, +	  c1_____1, +	  c_1___1_, +	  c__1_1__, +	  c___1___ },			/* v */ + +	{ c_______, +	  c_______, +	  c_______, +	  c1_____1, +	  c1__1__1, +	  c1__1__1, +	  c1__1__1, +	  c1__1__1, +	  c_11_11_ },			/* w */ + +	{ c_______, +	  c_______, +	  c_______, +	  c1____1_, +	  c_1__1__, +	  c__11___, +	  c__11___, +	  c_1__1__, +	  c1____1_ },			/* x */ + +	{ c1____1_, +	  c1____1_, +	  c1____1_, +	  c1____1_, +	  c1___11_, +	  c_111_1_, +	  c_____1_, +	  c1____1_, +	  c_1111__ },			/* y */ + +	{ c_______, +	  c_______, +	  c_______, +	  c111111_, +	  c____1__, +	  c___1___, +	  c__1____, +	  c_1_____, +	  c111111_ },			/* z */ + +	{ c___11__, +	  c__1____, +	  c__1____, +	  c__1____, +	  c_1_____, +	  c__1____, +	  c__1____, +	  c__1____, +	  c___11__ },			/* } */ + +	{ c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___, +	  c___1___ },			/* | */ + +	{ c__11___, +	  c____1__, +	  c____1__, +	  c____1__, +	  c_____1_, +	  c____1__, +	  c____1__, +	  c____1__, +	  c__11___ },			/* } */ + +	{ c_11____, +	  c1__1__1, +	  c____11_, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______, +	  c_______ },			/* ~ */ + +	{ c_1__1__, +	  c1__1__1, +	  c__1__1_, +	  c_1__1__, +	  c1__1__1, +	  c__1__1_, +	  c_1__1__, +	  c1__1__1, +	  c__1__1_ }			/* rub-out */ +}; diff --git a/usr.sbin/lpr/lpd/modes.c b/usr.sbin/lpr/lpd/modes.c new file mode 100644 index 000000000000..c1f61d5be069 --- /dev/null +++ b/usr.sbin/lpr/lpd/modes.c @@ -0,0 +1,225 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993, 1994 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */ +#include <stddef.h> +#include <string.h> +#include <termios.h> +#include "lp.local.h" +#include "extern.h" + +struct modes { +	const char *name; +	const long  set; +	const long  unset; +}; + +/* + * The code in optlist() depends on minus options following regular + * options, i.e. "foo" must immediately precede "-foo". + */ +struct modes cmodes[] = { +	{ "cs5",	CS5, CSIZE }, +	{ "cs6",	CS6, CSIZE }, +	{ "cs7",	CS7, CSIZE }, +	{ "cs8",	CS8, CSIZE }, +	{ "cstopb",	CSTOPB, 0 }, +	{ "-cstopb",	0, CSTOPB }, +	{ "cread",	CREAD, 0 }, +	{ "-cread",	0, CREAD }, +	{ "parenb",	PARENB, 0 }, +	{ "-parenb",	0, PARENB }, +	{ "parodd",	PARODD, 0 }, +	{ "-parodd",	0, PARODD }, +	{ "parity",	PARENB | CS7, PARODD | CSIZE }, +	{ "-parity",	CS8, PARODD | PARENB | CSIZE }, +	{ "evenp",	PARENB | CS7, PARODD | CSIZE }, +	{ "-evenp",	CS8, PARODD | PARENB | CSIZE }, +	{ "oddp",	PARENB | CS7 | PARODD, CSIZE }, +	{ "-oddp",	CS8, PARODD | PARENB | CSIZE }, +	{ "pass8",	CS8, PARODD | PARENB | CSIZE }, +	{ "-pass8",	PARENB | CS7, PARODD | CSIZE }, +	{ "hupcl",	HUPCL, 0 }, +	{ "-hupcl",	0, HUPCL }, +	{ "hup",	HUPCL, 0 }, +	{ "-hup",	0, HUPCL }, +	{ "clocal",	CLOCAL, 0 }, +	{ "-clocal",	0, CLOCAL }, +	{ "crtscts",	CRTSCTS, 0 }, +	{ "-crtscts",	0, CRTSCTS }, +	{ "ctsflow",	CCTS_OFLOW, 0 }, +	{ "-ctsflow",	0, CCTS_OFLOW }, +	{ "dsrflow",	CDSR_OFLOW, 0 }, +	{ "-dsrflow",	0, CDSR_OFLOW }, +	{ "dtrflow",	CDTR_IFLOW, 0 }, +	{ "-dtrflow",	0, CDTR_IFLOW }, +	{ "rtsflow",	CRTS_IFLOW, 0 }, +	{ "-rtsflow",	0, CRTS_IFLOW }, +	{ "mdmbuf",	MDMBUF, 0 }, +	{ "-mdmbuf",	0, MDMBUF }, +	{ NULL, 0, 0}, +}; + +struct modes imodes[] = { +	{ "ignbrk",	IGNBRK, 0 }, +	{ "-ignbrk",	0, IGNBRK }, +	{ "brkint",	BRKINT, 0 }, +	{ "-brkint",	0, BRKINT }, +	{ "ignpar",	IGNPAR, 0 }, +	{ "-ignpar",	0, IGNPAR }, +	{ "parmrk",	PARMRK, 0 }, +	{ "-parmrk",	0, PARMRK }, +	{ "inpck",	INPCK, 0 }, +	{ "-inpck",	0, INPCK }, +	{ "istrip",	ISTRIP, 0 }, +	{ "-istrip",	0, ISTRIP }, +	{ "inlcr",	INLCR, 0 }, +	{ "-inlcr",	0, INLCR }, +	{ "igncr",	IGNCR, 0 }, +	{ "-igncr",	0, IGNCR }, +	{ "icrnl",	ICRNL, 0 }, +	{ "-icrnl",	0, ICRNL }, +	{ "ixon",	IXON, 0 }, +	{ "-ixon",	0, IXON }, +	{ "flow",	IXON, 0 }, +	{ "-flow",	0, IXON }, +	{ "ixoff",	IXOFF, 0 }, +	{ "-ixoff",	0, IXOFF }, +	{ "tandem",	IXOFF, 0 }, +	{ "-tandem",	0, IXOFF }, +	{ "ixany",	IXANY, 0 }, +	{ "-ixany",	0, IXANY }, +	{ "decctlq",	0, IXANY }, +	{ "-decctlq",	IXANY, 0 }, +	{ "imaxbel",	IMAXBEL, 0 }, +	{ "-imaxbel",	0, IMAXBEL }, +	{ NULL, 0, 0}, +}; + +struct modes lmodes[] = { +	{ "echo",	ECHO, 0 }, +	{ "-echo",	0, ECHO }, +	{ "echoe",	ECHOE, 0 }, +	{ "-echoe",	0, ECHOE }, +	{ "crterase",	ECHOE, 0 }, +	{ "-crterase",	0, ECHOE }, +	{ "crtbs",	ECHOE, 0 },	/* crtbs not supported, close enough */ +	{ "-crtbs",	0, ECHOE }, +	{ "echok",	ECHOK, 0 }, +	{ "-echok",	0, ECHOK }, +	{ "echoke",	ECHOKE, 0 }, +	{ "-echoke",	0, ECHOKE }, +	{ "crtkill",	ECHOKE, 0 }, +	{ "-crtkill",	0, ECHOKE }, +	{ "altwerase",	ALTWERASE, 0 }, +	{ "-altwerase",	0, ALTWERASE }, +	{ "iexten",	IEXTEN, 0 }, +	{ "-iexten",	0, IEXTEN }, +	{ "echonl",	ECHONL, 0 }, +	{ "-echonl",	0, ECHONL }, +	{ "echoctl",	ECHOCTL, 0 }, +	{ "-echoctl",	0, ECHOCTL }, +	{ "ctlecho",	ECHOCTL, 0 }, +	{ "-ctlecho",	0, ECHOCTL }, +	{ "echoprt",	ECHOPRT, 0 }, +	{ "-echoprt",	0, ECHOPRT }, +	{ "prterase",	ECHOPRT, 0 }, +	{ "-prterase",	0, ECHOPRT }, +	{ "isig",	ISIG, 0 }, +	{ "-isig",	0, ISIG }, +	{ "icanon",	ICANON, 0 }, +	{ "-icanon",	0, ICANON }, +	{ "noflsh",	NOFLSH, 0 }, +	{ "-noflsh",	0, NOFLSH }, +	{ "tostop",	TOSTOP, 0 }, +	{ "-tostop",	0, TOSTOP }, +	{ "flusho",	FLUSHO, 0 }, +	{ "-flusho",	0, FLUSHO }, +	{ "pendin",	PENDIN, 0 }, +	{ "-pendin",	0, PENDIN }, +	{ "crt",	ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT }, +	{ "-crt",	ECHOK, ECHOE|ECHOKE|ECHOCTL }, +	{ "newcrt",	ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT }, +	{ "-newcrt",	ECHOK, ECHOE|ECHOKE|ECHOCTL }, +	{ "nokerninfo",	NOKERNINFO, 0 }, +	{ "-nokerninfo",0, NOKERNINFO }, +	{ "kerninfo",	0, NOKERNINFO }, +	{ "-kerninfo",	NOKERNINFO, 0 }, +	{ NULL, 0, 0}, +}; + +struct modes omodes[] = { +	{ "opost",	OPOST, 0 }, +	{ "-opost",	0, OPOST }, +	{ "litout",	0, OPOST }, +	{ "-litout",	OPOST, 0 }, +	{ "onlcr",	ONLCR, 0 }, +	{ "-onlcr",	0, ONLCR }, +	{ "tabs",	0, OXTABS },		/* "preserve" tabs */ +	{ "-tabs",	OXTABS, 0 }, +	{ "oxtabs",	OXTABS, 0 }, +	{ "-oxtabs",	0, OXTABS }, +	{ NULL, 0, 0}, +}; + +#define	CHK(name, s)	(*name == s[0] && !strcmp(name, s)) + +int +msearch(char *str, struct termios *ip) +{ +	struct modes *mp; + +	for (mp = cmodes; mp->name; ++mp) +		if (CHK(str, mp->name)) { +			ip->c_cflag &= ~mp->unset; +			ip->c_cflag |= mp->set; +			return (1); +		} +	for (mp = imodes; mp->name; ++mp) +		if (CHK(str, mp->name)) { +			ip->c_iflag &= ~mp->unset; +			ip->c_iflag |= mp->set; +			return (1); +		} +	for (mp = lmodes; mp->name; ++mp) +		if (CHK(str, mp->name)) { +			ip->c_lflag &= ~mp->unset; +			ip->c_lflag |= mp->set; +			return (1); +		} +	for (mp = omodes; mp->name; ++mp) +		if (CHK(str, mp->name)) { +			ip->c_oflag &= ~mp->unset; +			ip->c_oflag |= mp->set; +			return (1); +		} +	return (0); +} diff --git a/usr.sbin/lpr/lpd/printcap b/usr.sbin/lpr/lpd/printcap new file mode 100644 index 000000000000..ace92b2bf955 --- /dev/null +++ b/usr.sbin/lpr/lpd/printcap @@ -0,0 +1,52 @@ + +# +# This enables a simple local "raw" printer, hooked up to the first +# parallel port.  No kind of filtering is done, so everything you pass +# to the "lpr" command will be printed unmodified. +# +# Remember, for further print queues you're going to add, you have +# to choose different spool directories (the "sd" capability below), +# otherwise you will greatly confuse lpd. +# +# For some advanced printing, have a look at the "apsfilter" package. +# It plugs into the lpd system, allowing you to print a variety of +# different file types by converting everything to PostScript(tm) +# format.  For more information about apsfilter visit +# +#	     http://www.apsfilter.org/ +# +# If you don't have a PostScript(tm) printer, don't panic, but do +# also install the latest "ghostscript" package for best printer support. +# +# Do also refer to the "printing" section of the handbook. +# +#	https://docs.freebsd.org/en/books/handbook/printing/ +# +# A local copy can be found under +# +#	/usr/share/doc/handbook/handbook.{html,latin1}. +# +# Banner pages are now suppressed by default.  Remove the :sh: capability +# to turn them back on. +# +#lp|local line printer:\ +#	:sh:\ +#	:lp=/dev/lpt0:sd=/var/spool/output/lpd:lf=/var/log/lpd-errs: +# +# Sample remote printer.  The physical printer is on machine "lphost". +# You can perform any kind of local filtering directly.  If you need +# local filters (e.g. LF -> CR-LF conversion for HP printers), create +# a filter script that sends the proper escape sequence to the printer +# and then concatenates stdin to stdout. +# +#remote|sample remote printer:\ +#	:sh:\ +#	:rm=lphost:sd=/var/spool/output/lphost:lf=/var/log/lpd-errs:\ +#	:if=/usr/local/libexec/if-script: +# +# Simple Russian printer with hardware CP866 character set, output filter +# used for KOI8-R -> CP866 conversion +# +#lp|Russian local line printer:\ +#	:sh:of=/usr/libexec/lpr/ru/koi2alt:\ +#	:lp=/dev/lpt0:sd=/var/spool/output/lpd:lf=/var/log/lpd-errs: diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c new file mode 100644 index 000000000000..1c6736097492 --- /dev/null +++ b/usr.sbin/lpr/lpd/printjob.c @@ -0,0 +1,2008 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1983, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */ +/* + * printjob -- print jobs in the queue. + * + *	NOTE: the lock file is used to pass information to lpq and lprm. + *	it does not need to be removed because file locks are dynamic. + */ + +#include <sys/param.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <pwd.h> +#include <unistd.h> +#include <signal.h> +#include <syslog.h> +#include <fcntl.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <termios.h> +#include <time.h> +#include "lp.h" +#include "lp.local.h" +#include "pathnames.h" +#include "extern.h" + +#define DORETURN	0	/* dofork should return "can't fork" error */ +#define DOABORT		1	/* dofork should just die if fork() fails */ + +/* + * The buffer size to use when reading/writing spool files. + */ +#define	SPL_BUFSIZ	BUFSIZ + +/* + * Error tokens + */ +#define REPRINT		-2 +#define ERROR		-1 +#define	OK		0 +#define	FATALERR	1 +#define	NOACCT		2 +#define	FILTERERR	3 +#define	ACCESS		4 + +static dev_t	 fdev;		/* device of file pointed to by symlink */ +static ino_t	 fino;		/* inode of file pointed to by symlink */ +static FILE	*cfp;		/* control file */ +static pid_t	 of_pid;	/* process id of output filter, if any */ +static int	 child;		/* id of any filters */ +static int	 job_dfcnt;	/* count of datafiles in current user job */ +static int	 lfd;		/* lock file descriptor */ +static int	 ofd;		/* output filter file descriptor */ +static int	 tfd = -1;	/* output filter temp file output */ +static int	 pfd;		/* prstatic inter file descriptor */ +static int	 prchild;	/* id of pr process */ +static char	 title[80];	/* ``pr'' title */ +static char      locale[80];    /* ``pr'' locale */ + +/* these two are set from pp->daemon_user, but only if they are needed */ +static char	*daemon_uname;	/* set from pwd->pw_name */ +static int	 daemon_defgid; + +static char	class[32];		/* classification field */ +static char	origin_host[MAXHOSTNAMELEN];	/* user's host machine */ +				/* indentation size in static characters */ +static char	indent[10] = "-i0"; +static char	jobname[100];		/* job or file name */ +static char	length[10] = "-l";	/* page length in lines */ +static char	logname[32];		/* user's login name */ +static char	pxlength[10] = "-y";	/* page length in pixels */ +static char	pxwidth[10] = "-x";	/* page width in pixels */ +/* tempstderr is the filename used to catch stderr from exec-ing filters */ +static char	tempstderr[] = "errs.XXXXXXX"; +static char	width[10] = "-w";	/* page width in static characters */ +#define TFILENAME "fltXXXXXX" +static char	tfile[] = TFILENAME;	/* file name for filter output */ + +static void	 abortpr(int _signo); +static void	 alarmhandler(int _signo); +static void	 banner(struct printer *_pp, char *_name1, char *_name2); +static int	 dofork(const struct printer *_pp, int _action); +static int	 dropit(int _c); +static int	 execfilter(struct printer *_pp, char *_f_cmd, char **_f_av, +		    int _infd, int _outfd); +static void	 init(struct printer *_pp); +static void	 openpr(const struct printer *_pp); +static void	 opennet(const struct printer *_pp); +static void	 opentty(const struct printer *_pp); +static void	 openrem(const struct printer *pp); +static int	 print(struct printer *_pp, int _format, char *_file); +static int	 printit(struct printer *_pp, char *_file); +static void	 pstatus(const struct printer *_pp, const char *_msg, ...) +		    __printflike(2, 3); +static char	 response(const struct printer *_pp); +static void	 scan_out(struct printer *_pp, int _scfd, char *_scsp,  +		    int _dlm); +static char	*scnline(int _key, char *_p, int _c); +static int	 sendfile(struct printer *_pp, int _type, char *_file,  +		    char _format, int _copyreq); +static int	 sendit(struct printer *_pp, char *_file); +static void	 sendmail(struct printer *_pp, char *_userid, int _bombed); +static void	 setty(const struct printer *_pp); +static void	 wait4data(struct printer *_pp, const char *_dfile); + +void +printjob(struct printer *pp) +{ +	struct stat stb; +	register struct jobqueue *q, **qp; +	struct jobqueue **queue; +	register int i, nitems; +	off_t pidoff; +	pid_t printpid; +	int errcnt, jobcount, statok, tempfd; + +	jobcount = 0; +	init(pp); /* set up capabilities */ +	(void) write(STDOUT_FILENO, "", 1);	/* ack that daemon is started */ +	(void) close(STDERR_FILENO);			/* set up log file */ +	if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) { +		syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, +		    pp->log_file); +		(void) open(_PATH_DEVNULL, O_WRONLY); +	} +	if(setgid(getegid()) != 0) err(1, "setgid() failed"); +	printpid = getpid();			/* for use with lprm */ +	setpgid((pid_t)0, printpid); + +	/* +	 * At initial lpd startup, printjob may be called with various +	 * signal handlers in effect.  After that initial startup, any +	 * calls to printjob will have a *different* set of signal-handlers +	 * in effect.  Make sure all handlers are the ones we want. +	 */ +	signal(SIGCHLD, SIG_DFL); +	signal(SIGHUP, abortpr); +	signal(SIGINT, abortpr); +	signal(SIGQUIT, abortpr); +	signal(SIGTERM, abortpr); + +	/* +	 * uses short form file names +	 */ +	if (chdir(pp->spool_dir) < 0) { +		syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer, +		    pp->spool_dir); +		exit(1); +	} +	statok = stat(pp->lock_file, &stb); +	if (statok == 0 && (stb.st_mode & LFM_PRINT_DIS)) +		exit(0);		/* printing disabled */ +	umask(S_IWOTH); +	lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,  +		   LOCK_FILE_MODE); +	if (lfd < 0) { +		if (errno == EWOULDBLOCK)	/* active daemon present */ +			exit(0); +		syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, +		    pp->lock_file); +		exit(1); +	} +	/* +	 * If the initial call to stat() failed, then lock_file will have +	 * been created by open().  Update &stb to match that new file. +	 */ +	if (statok != 0) +		statok = stat(pp->lock_file, &stb); +	/* turn off non-blocking mode (was turned on for lock effects only) */ +	if (fcntl(lfd, F_SETFL, 0) < 0) { +		syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer, +		    pp->lock_file); +		exit(1); +	} +	ftruncate(lfd, 0); +	/* +	 * write process id for others to know +	 */ +	sprintf(line, "%u\n", printpid); +	pidoff = i = strlen(line); +	if (write(lfd, line, i) != i) { +		syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, +		    pp->lock_file); +		exit(1); +	} +	/* +	 * search the spool directory for work and sort by queue order. +	 */ +	if ((nitems = getq(pp, &queue)) < 0) { +		syslog(LOG_ERR, "%s: can't scan %s", pp->printer,  +		    pp->spool_dir); +		exit(1); +	} +	if (nitems == 0)		/* no work to do */ +		exit(0); +	if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */ +		if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0) +			syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, +			    pp->lock_file); +	} + +	/* create a file which will be used to hold stderr from filters */ +	if ((tempfd = mkstemp(tempstderr)) == -1) { +		syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, +		    tempstderr); +		exit(1); +	} +	if ((i = fchmod(tempfd, 0664)) == -1) { +		syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer, +		    tempstderr); +		exit(1); +	} +	/* lpd doesn't need it to be open, it just needs it to exist */ +	close(tempfd); + +	openpr(pp);			/* open printer or remote */ +again: +	/* +	 * we found something to do now do it -- +	 *    write the name of the current control file into the lock file +	 *    so the spool queue program can tell what we're working on +	 */ +	for (qp = queue; nitems--; free((char *) q)) { +		q = *qp++; +		if (stat(q->job_cfname, &stb) < 0) +			continue; +		errcnt = 0; +	restart: +		(void) lseek(lfd, pidoff, 0); +		(void) snprintf(line, sizeof(line), "%s\n", q->job_cfname); +		i = strlen(line); +		if (write(lfd, line, i) != i) +			syslog(LOG_ERR, "%s: write(%s): %m", pp->printer, +			    pp->lock_file); +		if (!pp->remote) +			i = printit(pp, q->job_cfname); +		else +			i = sendit(pp, q->job_cfname); +		/* +		 * Check to see if we are supposed to stop printing or +		 * if we are to rebuild the queue. +		 */ +		if (fstat(lfd, &stb) == 0) { +			/* stop printing before starting next job? */ +			if (stb.st_mode & LFM_PRINT_DIS) +				goto done; +			/* rebuild queue (after lpc topq) */ +			if (stb.st_mode & LFM_RESET_QUE) { +				for (free(q); nitems--; free(q)) +					q = *qp++; +				if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) +				    < 0) +					syslog(LOG_WARNING, +					    "%s: fchmod(%s): %m", +					    pp->printer, pp->lock_file); +				break; +			} +		} +		if (i == OK)		/* all files of this job printed */ +			jobcount++; +		else if (i == REPRINT && ++errcnt < 5) { +			/* try reprinting the job */ +			syslog(LOG_INFO, "restarting %s", pp->printer); +			if (of_pid > 0) { +				kill(of_pid, SIGCONT); /* to be sure */ +				(void) close(ofd); +				while ((i = wait(NULL)) > 0 && i != of_pid) +					; +				if (i < 0) +					syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m", +					    pp->printer, of_pid); +				of_pid = 0; +			} +			(void) close(pfd);	/* close printer */ +			if (ftruncate(lfd, pidoff) < 0) +				syslog(LOG_WARNING, "%s: ftruncate(%s): %m",  +				    pp->printer, pp->lock_file); +			openpr(pp);		/* try to reopen printer */ +			goto restart; +		} else { +			syslog(LOG_WARNING, "%s: job could not be %s (%s)",  +			    pp->printer, +			    pp->remote ? "sent to remote host" : "printed", +			    q->job_cfname); +			if (i == REPRINT) { +				/* ensure we don't attempt this job again */ +				(void) unlink(q->job_cfname); +				q->job_cfname[0] = 'd'; +				(void) unlink(q->job_cfname); +				if (logname[0]) +					sendmail(pp, logname, FATALERR); +			} +		} +	} +	free(queue); +	/* +	 * search the spool directory for more work. +	 */ +	if ((nitems = getq(pp, &queue)) < 0) { +		syslog(LOG_ERR, "%s: can't scan %s", pp->printer, +		    pp->spool_dir); +		exit(1); +	} +	if (nitems == 0) {		/* no more work to do */ +	done: +		if (jobcount > 0) {	/* jobs actually printed */ +			if (!pp->no_formfeed && !pp->tof) +				(void) write(ofd, pp->form_feed, +					     strlen(pp->form_feed)); +			if (pp->trailer != NULL) /* output trailer */ +				(void) write(ofd, pp->trailer, +					     strlen(pp->trailer)); +		} +		(void) close(ofd); +		(void) wait(NULL); +		(void) unlink(tempstderr); +		exit(0); +	} +	goto again; +} + +char	fonts[4][50];	/* fonts for troff */ + +char ifonts[4][40] = { +	_PATH_VFONTR, +	_PATH_VFONTI, +	_PATH_VFONTB, +	_PATH_VFONTS, +}; + +/* + * The remaining part is the reading of the control file (cf) + * and performing the various actions. + */ +static int +printit(struct printer *pp, char *file) +{ +	register int i; +	char *cp; +	int bombed, didignorehdr; + +	bombed = OK; +	didignorehdr = 0; +	/* +	 * open control file; ignore if no longer there. +	 */ +	if ((cfp = fopen(file, "r")) == NULL) { +		syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file); +		return (OK); +	} +	/* +	 * Reset troff fonts. +	 */ +	for (i = 0; i < 4; i++) +		strcpy(fonts[i], ifonts[i]); +	sprintf(&width[2], "%ld", pp->page_width); +	strcpy(indent+2, "0"); + +	/* initialize job-specific count of datafiles processed */ +	job_dfcnt = 0; +	 +	/* +	 *      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: +	 * +	 *		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 +	 *		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 +	 *		R -- laser dpi "resolution" +	 *              f -- "file name" name of text file to print +	 *		l -- "file name" text file with control chars +	 *		o -- "file name" postscript file, according to +	 *		     the RFC.  Here it is treated like an 'f'. +	 *		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 +	 *		1 -- "R font file" for troff +	 *		2 -- "I font file" for troff +	 *		3 -- "B font file" for troff +	 *		4 -- "S font file" for troff +	 *		N -- "name" of file (used by lpq) +	 *              U -- "unlink" name of file to remove +	 *                    (after we print it. (Pass 2 only)). +	 *		M -- "mail" to user when done printing +	 *              Z -- "locale" for pr +	 * +	 *      get_line reads a line and expands tabs to blanks +	 */ + +	/* pass 1 */ + +	while (get_line(cfp)) +		switch (line[0]) { +		case 'H': +			strlcpy(origin_host, line + 1, sizeof(origin_host)); +			if (class[0] == '\0') { +				strlcpy(class, line+1, sizeof(class)); +			} +			continue; + +		case 'P': +			strlcpy(logname, line + 1, sizeof(logname)); +			if (pp->restricted) { /* restricted */ +				if (getpwnam(logname) == NULL) { +					bombed = NOACCT; +					sendmail(pp, line+1, bombed); +					goto pass2; +				} +			} +			continue; + +		case 'S': +			cp = line+1; +			i = 0; +			while (*cp >= '0' && *cp <= '9') +				i = i * 10 + (*cp++ - '0'); +			fdev = i; +			cp++; +			i = 0; +			while (*cp >= '0' && *cp <= '9') +				i = i * 10 + (*cp++ - '0'); +			fino = i; +			continue; + +		case 'J': +			if (line[1] != '\0') { +				strlcpy(jobname, line + 1, sizeof(jobname)); +			} else +				strcpy(jobname, " "); +			continue; + +		case 'C': +			if (line[1] != '\0') +				strlcpy(class, line + 1, sizeof(class)); +			else if (class[0] == '\0') { +				/* XXX - why call gethostname instead of +				 *       just strlcpy'ing local_host? */ +				gethostname(class, sizeof(class)); +				class[sizeof(class) - 1] = '\0'; +			} +			continue; + +		case 'T':	/* header title for pr */ +			strlcpy(title, line + 1, sizeof(title)); +			continue; + +		case 'L':	/* identification line */ +			if (!pp->no_header && !pp->header_last) +				banner(pp, line+1, jobname); +			continue; + +		case '1':	/* troff fonts */ +		case '2': +		case '3': +		case '4': +			if (line[1] != '\0') { +				strlcpy(fonts[line[0]-'1'], line + 1, +				    (size_t)50); +			} +			continue; + +		case 'W':	/* page width */ +			strlcpy(width+2, line + 1, sizeof(width) - 2); +			continue; + +		case 'I':	/* indent amount */ +			strlcpy(indent+2, line + 1, sizeof(indent) - 2); +			continue; + +		case 'Z':       /* locale for pr */ +			strlcpy(locale, line + 1, sizeof(locale)); +			continue; + +		default:	/* some file to print */ +			/* only lowercase cmd-codes include a file-to-print */ +			if ((line[0] < 'a') || (line[0] > 'z')) { +				/* ignore any other lines */ +				if (lflag <= 1) +					continue; +				if (!didignorehdr) { +					syslog(LOG_INFO, "%s: in %s :", +					    pp->printer, file); +					didignorehdr = 1; +				} +				syslog(LOG_INFO, "%s: ignoring line: '%c' %s", +				    pp->printer, line[0], &line[1]); +				continue; +			} +			i = print(pp, line[0], line+1); +			switch (i) { +			case ERROR: +				if (bombed == OK) +					bombed = FATALERR; +				break; +			case REPRINT: +				(void) fclose(cfp); +				return (REPRINT); +			case FILTERERR: +			case ACCESS: +				bombed = i; +				sendmail(pp, logname, bombed); +			} +			title[0] = '\0'; +			continue; + +		case 'N': +		case 'U': +		case 'M': +		case 'R': +			continue; +		} + +	/* pass 2 */ + +pass2: +	fseek(cfp, 0L, 0); +	while (get_line(cfp)) +		switch (line[0]) { +		case 'L':	/* identification line */ +			if (!pp->no_header && pp->header_last) +				banner(pp, line+1, jobname); +			continue; + +		case 'M': +			if (bombed < NOACCT)	/* already sent if >= NOACCT */ +				sendmail(pp, line+1, bombed); +			continue; + +		case 'U': +			if (strchr(line+1, '/')) +				continue; +			(void) unlink(line+1); +		} +	/* +	 * clean-up in case another control file exists +	 */ +	(void) fclose(cfp); +	(void) unlink(file); +	return (bombed == OK ? OK : ERROR); +} + +/* + * Print a file. + * Set up the chain [ PR [ | {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 + * 0 if all is well. + * Note: all filters take stdin as the file, stdout as the printer, + * stderr as the log file, and must not ignore SIGINT. + */ +static int +print(struct printer *pp, int format, char *file) +{ +	register int n, i; +	register char *prog; +	int fi, fo; +	FILE *fp; +	char *av[15], buf[SPL_BUFSIZ]; +	pid_t wpid; +	int p[2], retcode, stopped, wstatus, wstatus_set; +	struct stat stb; + +	/* Make sure the entire data file has arrived. */ +	wait4data(pp, file); + +	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) { +		syslog(LOG_INFO, "%s: unable to open %s ('%c' line)", +		    pp->printer, file, format); +		return (ERROR); +	} +	/* +	 * Check to see if data file is a symbolic link. If so, it should +	 * still point to the same file or someone is trying to print +	 * something he shouldn't. +	 */ +	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 && +	    (stb.st_dev != fdev || stb.st_ino != fino)) +		return (ACCESS); + +	job_dfcnt++;		/* increment datafile counter for this job */ +	stopped = 0;		/* output filter is not stopped */ + +	/* everything seems OK, start it up */ +	if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */ +		(void) write(ofd, pp->form_feed, strlen(pp->form_feed)); +		pp->tof = 1; +	} +	if (pp->filters[LPF_INPUT] == NULL +	    && (format == 'f' || format == 'l' || format == 'o')) { +		pp->tof = 0; +		while ((n = read(fi, buf, SPL_BUFSIZ)) > 0) +			if (write(ofd, buf, n) != n) { +				(void) close(fi); +				return (REPRINT); +			} +		(void) close(fi); +		return (OK); +	} +	switch (format) { +	case 'p':	/* print file using 'pr' */ +		if (pp->filters[LPF_INPUT] == NULL) {	/* use output filter */ +			prog = _PATH_PR; +			i = 0; +			av[i++] = "pr"; +			av[i++] = width; +			av[i++] = length; +			av[i++] = "-h"; +			av[i++] = *title ? title : " "; +			av[i++] = "-L"; +			av[i++] = *locale ? locale : "C"; +			av[i++] = "-F"; +			av[i] = NULL; +			fo = ofd; +			goto start; +		} +		pipe(p); +		if ((prchild = dofork(pp, DORETURN)) == 0) {	/* child */ +			dup2(fi, STDIN_FILENO);		/* file is stdin */ +			dup2(p[1], STDOUT_FILENO);	/* pipe is stdout */ +			closelog(); +			closeallfds(3); +			execl(_PATH_PR, "pr", width, length, +			    "-h", *title ? title : " ", +			    "-L", *locale ? locale : "C", +			    "-F", (char *)0); +			syslog(LOG_ERR, "cannot execl %s", _PATH_PR); +			exit(2); +		} +		(void) close(p[1]);		/* close output side */ +		(void) close(fi); +		if (prchild < 0) { +			prchild = 0; +			(void) close(p[0]); +			return (ERROR); +		} +		fi = p[0];			/* use pipe for input */ +	case 'f':	/* print plain text file */ +		prog = pp->filters[LPF_INPUT]; +		av[1] = width; +		av[2] = length; +		av[3] = indent; +		n = 4; +		break; +	case 'o':	/* print postscript file */ +		/* +		 * Treat this as a "plain file with control characters", and +		 * assume the standard LPF_INPUT filter will recognize that +		 * the data is postscript and know what to do with it.  These +		 * 'o'-file requests could come from MacOS 10.1 systems. +		 * (later versions of MacOS 10 will explicitly use 'l') +		 * A postscript file can contain binary data, which is why 'l' +		 * is somewhat more appropriate than 'f'. +		 */ +		/* FALLTHROUGH */ +	case 'l':	/* like 'f' but pass control characters */ +		prog = pp->filters[LPF_INPUT]; +		av[1] = "-c"; +		av[2] = width; +		av[3] = length; +		av[4] = indent; +		n = 5; +		break; +	case 'r':	/* print a fortran text file */ +		prog = pp->filters[LPF_FORTRAN]; +		av[1] = width; +		av[2] = length; +		n = 3; +		break; +	case 't':	/* print troff output */ +	case 'n':	/* print ditroff output */ +	case 'd':	/* print tex output */ +		(void) unlink(".railmag"); +		if ((fo = creat(".railmag", FILMOD)) < 0) { +			syslog(LOG_ERR, "%s: cannot create .railmag",  +			    pp->printer); +			(void) unlink(".railmag"); +		} else { +			for (n = 0; n < 4; n++) { +				if (fonts[n][0] != '/') +					(void) write(fo, _PATH_VFONT, +					    sizeof(_PATH_VFONT) - 1); +				(void) write(fo, fonts[n], strlen(fonts[n])); +				(void) write(fo, "\n", 1); +			} +			(void) close(fo); +		} +		prog = (format == 't') ? pp->filters[LPF_TROFF]  +			: ((format == 'n') ? pp->filters[LPF_DITROFF] +			   : pp->filters[LPF_DVI]); +		av[1] = pxwidth; +		av[2] = pxlength; +		n = 3; +		break; +	case 'c':	/* print cifplot output */ +		prog = pp->filters[LPF_CIFPLOT]; +		av[1] = pxwidth; +		av[2] = pxlength; +		n = 3; +		break; +	case 'g':	/* print plot(1G) output */ +		prog = pp->filters[LPF_GRAPH]; +		av[1] = pxwidth; +		av[2] = pxlength; +		n = 3; +		break; +	case 'v':	/* print raster output */ +		prog = pp->filters[LPF_RASTER]; +		av[1] = pxwidth; +		av[2] = pxlength; +		n = 3; +		break; +	default: +		(void) close(fi); +		syslog(LOG_ERR, "%s: illegal format character '%c'", +		    pp->printer, format); +		return (ERROR); +	} +	if (prog == NULL) { +		(void) close(fi); +		syslog(LOG_ERR, +		   "%s: no filter found in printcap for format character '%c'", +		   pp->printer, format); +		return (ERROR); +	} +	if ((av[0] = strrchr(prog, '/')) != NULL) +		av[0]++; +	else +		av[0] = prog; +	av[n++] = "-n"; +	av[n++] = logname; +	av[n++] = "-h"; +	av[n++] = origin_host; +	av[n++] = pp->acct_file; +	av[n] = NULL; +	fo = pfd; +	if (of_pid > 0) {		/* stop output filter */ +		write(ofd, "\031\1", 2); +		while ((wpid = +		    wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid) +			; +		if (wpid < 0) +			syslog(LOG_WARNING, +			    "%s: after stopping 'of', wait3() returned: %m", +			    pp->printer); +		else if (!WIFSTOPPED(wstatus)) { +			(void) close(fi); +			syslog(LOG_WARNING, "%s: output filter died " +			    "(pid=%d retcode=%d termsig=%d)", +			    pp->printer, of_pid, WEXITSTATUS(wstatus), +			    WTERMSIG(wstatus)); +			return (REPRINT); +		} +		stopped++; +	} +start: +	if ((child = dofork(pp, DORETURN)) == 0) { /* child */ +		dup2(fi, STDIN_FILENO); +		dup2(fo, STDOUT_FILENO); +		/* setup stderr for the filter (child process) +		 * so it goes to our temporary errors file */ +		n = open(tempstderr, O_WRONLY|O_TRUNC, 0664); +		if (n >= 0) +			dup2(n, STDERR_FILENO); +		closelog(); +		closeallfds(3); +		execv(prog, av); +		syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, +		    prog); +		exit(2); +	} +	(void) close(fi); +	wstatus_set = 0; +	if (child < 0) +		retcode = 100; +	else { +		while ((wpid = wait(&wstatus)) > 0 && wpid != child) +			; +		if (wpid < 0) { +			retcode = 100; +			syslog(LOG_WARNING, +			    "%s: after execv(%s), wait() returned: %m", +			    pp->printer, prog); +		} else { +			wstatus_set = 1; +			retcode = WEXITSTATUS(wstatus); +		} +	} +	child = 0; +	prchild = 0; +	if (stopped) {		/* restart output filter */ +		if (kill(of_pid, SIGCONT) < 0) { +			syslog(LOG_ERR, "cannot restart output filter"); +			exit(1); +		} +	} +	pp->tof = 0; + +	/* Copy the filter's output to "lf" logfile */ +	if ((fp = fopen(tempstderr, "r"))) { +		while (fgets(buf, sizeof(buf), fp)) +			fputs(buf, stderr); +		fclose(fp); +	} + +	if (wstatus_set && !WIFEXITED(wstatus)) { +		syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)", +		    pp->printer, format, WTERMSIG(wstatus)); +		return (ERROR); +	} +	switch (retcode) { +	case 0: +		pp->tof = 1; +		return (OK); +	case 1: +		return (REPRINT); +	case 2: +		return (ERROR); +	default: +		syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)", +		    pp->printer, format, retcode); +		return (FILTERERR); +	} +} + +/* + * Send the daemon control file (cf) and any data files. + * Return -1 if a non-recoverable error occurred, 1 if a recoverable error and + * 0 if all is well. + */ +static int +sendit(struct printer *pp, char *file) +{ +	int dfcopies, err, i; +	char *cp, last[sizeof(line)]; + +	/* +	 * open control file +	 */ +	if ((cfp = fopen(file, "r")) == NULL) +		return (OK); + +	/* initialize job-specific count of datafiles processed */ +	job_dfcnt = 0; + +	/* +	 *      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. +	 *      commands of interest are: +	 * +	 *            a-z -- "file name" name of file to print +	 *              U -- "unlink" name of file to remove +	 *                    (after we print it. (Pass 2 only)). +	 */ + +	/* +	 * pass 1 +	 */ +	err = OK; +	while (get_line(cfp)) { +	again: +		if (line[0] == 'S') { +			cp = line+1; +			i = 0; +			while (*cp >= '0' && *cp <= '9') +				i = i * 10 + (*cp++ - '0'); +			fdev = i; +			cp++; +			i = 0; +			while (*cp >= '0' && *cp <= '9') +				i = i * 10 + (*cp++ - '0'); +			fino = i; +		} else if (line[0] == 'H') { +			strlcpy(origin_host, line + 1, sizeof(origin_host)); +			if (class[0] == '\0') { +				strlcpy(class, line + 1, sizeof(class)); +			} +		} else if (line[0] == 'P') { +			strlcpy(logname, line + 1, sizeof(logname)); +			if (pp->restricted) { /* restricted */ +				if (getpwnam(logname) == NULL) { +					sendmail(pp, line+1, NOACCT); +					err = ERROR; +					break; +				} +			} +		} else if (line[0] == 'I') { +			strlcpy(indent+2, line + 1, sizeof(indent) - 2); +		} else if (line[0] >= 'a' && line[0] <= 'z') { +			dfcopies = 1; +			strcpy(last, line); +			while ((i = get_line(cfp)) != 0) { +				if (strcmp(last, line) != 0) +					break; +				dfcopies++; +			} +			switch (sendfile(pp, '\3', last+1, *last, dfcopies)) { +			case OK: +				if (i) +					goto again; +				break; +			case REPRINT: +				(void) fclose(cfp); +				return (REPRINT); +			case ACCESS: +				sendmail(pp, logname, ACCESS); +			case ERROR: +				err = ERROR; +			} +			break; +		} +	} +	if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) { +		(void) fclose(cfp); +		return (REPRINT); +	} +	/* +	 * pass 2 +	 */ +	fseek(cfp, 0L, 0); +	while (get_line(cfp)) +		if (line[0] == 'U' && !strchr(line+1, '/')) +			(void) unlink(line+1); +	/* +	 * clean-up in case another control file exists +	 */ +	(void) fclose(cfp); +	(void) unlink(file); +	return (err); +} + +/* + * Send a data file to the remote machine and spool it. + * Return positive if we should try resending. + */ +static int +sendfile(struct printer *pp, int type, char *file, char format, int copyreq) +{ +	int i, amt; +	struct stat stb; +	char *av[15], *filtcmd; +	char buf[SPL_BUFSIZ], opt_c[4], opt_h[4], opt_n[4]; +	int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc; + +	/* Make sure the entire data file has arrived. */ +	wait4data(pp, file); + +	statrc = lstat(file, &stb); +	if (statrc < 0) { +		syslog(LOG_ERR, "%s: error from lstat(%s): %m", +		    pp->printer, file); +		return (ERROR); +	} +	sfd = open(file, O_RDONLY); +	if (sfd < 0) { +		syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m", +		    pp->printer, file); +		return (ERROR); +	} +	/* +	 * Check to see if data file is a symbolic link. If so, it should +	 * still point to the same file or someone is trying to print something +	 * he shouldn't. +	 */ +	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 && +	    (stb.st_dev != fdev || stb.st_ino != fino)) { +		close(sfd); +		return (ACCESS); +	} + +	/* Everything seems OK for reading the file, now to send it */ +	filtcmd = NULL; +	sizerr = 0; +	tfd = -1; +	if (type == '\3') { +		/* +		 * Type == 3 means this is a datafile, not a control file. +		 * Increment the counter of data-files in this job, and +		 * then check for input or output filters (which are only +		 * applied to datafiles, not control files). +		 */ +		job_dfcnt++; + +		/* +		 * Note that here we are filtering datafiles, one at a time, +		 * as they are sent to the remote machine.  Here, the *only* +		 * difference between an input filter (`if=') and an output +		 * filter (`of=') is the argument list that the filter is +		 * started up with.  Here, the output filter is executed +		 * for each individual file as it is sent.  This is not the +		 * same as local print queues, where the output filter is +		 * started up once, and then all jobs are passed thru that +		 * single invocation of the output filter. +		 * +		 * Also note that a queue for a remote-machine can have an +		 * input filter or an output filter, but not both. +		 */ +		if (pp->filters[LPF_INPUT]) { +			filtcmd = pp->filters[LPF_INPUT]; +			av[0] = filtcmd; +			narg = 0; +			strcpy(opt_c, "-c"); +			strcpy(opt_h, "-h"); +			strcpy(opt_n, "-n"); +			if (format == 'l') +				av[++narg] = opt_c; +			av[++narg] = width; +			av[++narg] = length; +			av[++narg] = indent; +			av[++narg] = opt_n; +			av[++narg] = logname; +			av[++narg] = opt_h; +			av[++narg] = origin_host; +			av[++narg] = pp->acct_file; +			av[++narg] = NULL; +		} else if (pp->filters[LPF_OUTPUT]) { +			filtcmd = pp->filters[LPF_OUTPUT]; +			av[0] = filtcmd; +			narg = 0; +			av[++narg] = width; +			av[++narg] = length; +			av[++narg] = NULL; +		} +	} +	if (filtcmd) { +		/* +		 * If there is an input or output filter, we have to run +		 * the datafile thru that filter and store the result as +		 * a temporary spool file, because the protocol requires +		 * that we send the remote host the file-size before we +		 * start to send any of the data. +		 */ +		strcpy(tfile, TFILENAME); +		tfd = mkstemp(tfile); +		if (tfd == -1) { +			syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer, +			    TFILENAME); +			sfres = ERROR; +			goto return_sfres; +		} +		filtstat = execfilter(pp, filtcmd, av, sfd, tfd); + +		/* process the return-code from the filter */ +		switch (filtstat) { +		case 0: +			break; +		case 1: +			sfres = REPRINT; +			goto return_sfres; +		case 2: +			sfres = ERROR; +			goto return_sfres; +		default: +			syslog(LOG_WARNING, +			    "%s: filter '%c' exited (retcode=%d)", +			    pp->printer, format, filtstat); +			sfres = FILTERERR; +			goto return_sfres; +		} +		statrc = fstat(tfd, &stb);   /* to find size of tfile */ +		if (statrc < 0)	{ +			syslog(LOG_ERR, +			    "%s: error processing 'if', fstat(%s): %m", +			    pp->printer, tfile); +			sfres = ERROR; +			goto return_sfres; +		} +		close(sfd); +		sfd = tfd; +		lseek(sfd, 0, SEEK_SET); +	} + +	copycnt = 0; +sendagain: +	copycnt++; + +	if (copycnt < 2) +		(void) sprintf(buf, "%c%" PRId64 " %s\n", type, stb.st_size, +		    file); +	else +		(void) sprintf(buf, "%c%" PRId64 " %s_c%d\n", type, stb.st_size, +		    file, copycnt); +	amt = strlen(buf); +	for (i = 0;  ; i++) { +		if (write(pfd, buf, amt) != amt || +		    (resp = response(pp)) < 0 || resp == '\1') { +			sfres = REPRINT; +			goto return_sfres; +		} else if (resp == '\0') +			break; +		if (i == 0) +			pstatus(pp, +				"no space on remote; waiting for queue to drain"); +		if (i == 10) +			syslog(LOG_ALERT, "%s: can't send to %s; queue full", +			    pp->printer, pp->remote_host); +		sleep(5 * 60); +	} +	if (i) +		pstatus(pp, "sending to %s", pp->remote_host); +	/* +	 * XXX - we should change trstat_init()/trstat_write() to include +	 *	 the copycnt in the statistics record it may write. +	 */ +	if (type == '\3') +		trstat_init(pp, file, job_dfcnt); +	for (i = 0; i < stb.st_size; i += SPL_BUFSIZ) { +		amt = SPL_BUFSIZ; +		if (i + amt > stb.st_size) +			amt = stb.st_size - i; +		if (sizerr == 0 && read(sfd, buf, amt) != amt) +			sizerr = 1; +		if (write(pfd, buf, amt) != amt) { +			sfres = REPRINT; +			goto return_sfres; +		} +	} + +	if (sizerr) { +		syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file); +		/* tell recvjob to ignore this file */ +		(void) write(pfd, "\1", 1); +		sfres = ERROR; +		goto return_sfres; +	} +	if (write(pfd, "", 1) != 1 || response(pp)) { +		sfres = REPRINT; +		goto return_sfres; +	} +	if (type == '\3') { +		trstat_write(pp, TR_SENDING, stb.st_size, logname, +		    pp->remote_host, origin_host); +		/* +		 * Usually we only need to send one copy of a datafile, +		 * because the control-file will simply print the same +		 * file multiple times.  However, some printers ignore +		 * the control file, and simply print each data file as +		 * it arrives.  For such "remote hosts", we need to +		 * transfer the same data file multiple times.  Such a +		 * a host is indicated by adding 'rc' to the printcap +		 * entry. +		 * XXX - Right now this ONLY works for remote hosts which +		 *	do ignore the name of the data file, because +		 *	this sends the file multiple times with slight +		 *	changes to the filename.  To do this right would +		 *	require that we also rewrite the control file +		 *	to match those filenames. +		 */ +		if (pp->resend_copies && (copycnt < copyreq)) { +			lseek(sfd, 0, SEEK_SET); +			goto sendagain; +		} +	} +	sfres = OK; + +return_sfres: +	(void)close(sfd); +	if (tfd != -1) { +		/* +		 * If tfd is set, then it is the same value as sfd, and +		 * therefore it is already closed at this point.  All +		 * we need to do is remove the temporary file. +		 */ +		tfd = -1; +		unlink(tfile); +	} +	return (sfres); +} + +/* + * Some print servers send the control-file first, and then start sending the + * matching data file(s).  That is not the correct order.  If some queue is + * already printing an active job, then when that job is finished the queue + * may proceed to the control file of any incoming print job.  This turns + * into a race between the process which is receiving the data file, and the + * process which is actively printing the very same file.  When the remote + * server sends files in the wrong order, it is even possible that a queue + * will start to print a data file before the file has been created! + * + * So before we start to print() or send() a data file, we call this routine + * to make sure the data file is not still changing in size.  Note that this + * problem will only happen for jobs arriving from a remote host, and that + * the process which has decided to print this job (and is thus making this + * check) is *not* the process which is receiving the job. + * + * A second benefit of this is that any incoming job is guaranteed to appear + * in a queue listing for at least a few seconds after it has arrived.  Some + * lpr implementations get confused if they send a job and it disappears + * from the queue before they can check on it. + */ +#define	MAXWAIT_ARRIVE	16	    /* max to wait for the file to *exist* */ +#define	MAXWAIT_4DATA	(20*60)	    /* max to wait for it to stop changing */ +#define	MINWAIT_4DATA	4	    /* This value must be >= 1 */ +#define	DEBUG_MINWAIT	1 +static void +wait4data(struct printer *pp, const char *dfile) +{ +	const char *cp; +	int statres; +	u_int sleepreq; +	size_t dlen, hlen; +	time_t amtslept, cur_time, prev_mtime; +	struct stat statdf; + +	/* Skip these checks if the print job is from the local host. */ +	dlen = strlen(dfile); +	hlen = strlen(local_host); +	if (dlen > hlen) { +		cp = dfile + dlen - hlen; +		if (strcmp(cp, local_host) == 0) +			return; +	} + +	/* +	 * If this data file does not exist, then wait up to MAXWAIT_ARRIVE +	 * seconds for it to arrive. +	 */ +	amtslept = 0; +	statres = stat(dfile, &statdf); +	while (statres < 0 && amtslept < MAXWAIT_ARRIVE) { +		if (amtslept == 0) +			pstatus(pp, "Waiting for data file from remote host"); +		amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA); +		statres = stat(dfile, &statdf); +	} +	if (statres < 0) { +		/* The file still does not exist, so just give up on it. */ +		syslog(LOG_WARNING, "%s: wait4data() abandoned wait for %s", +		    pp->printer, dfile); +		return; +	} + +	/* +	 * The file exists, so keep waiting until the data file has not +	 * changed for some reasonable amount of time.  Extra care is +	 * taken when computing wait-times, just in case there are data +	 * files with a last-modify time in the future.  While that is +	 * very unlikely to happen, it can happen when the system has +	 * a flakey time-of-day clock. +	 */ +	prev_mtime = statdf.st_mtime; +	cur_time = time(NULL); +	if (statdf.st_mtime >= cur_time - MINWAIT_4DATA) { +		if (statdf.st_mtime >= cur_time)	/* some TOD oddity */ +			sleepreq = MINWAIT_4DATA; +		else +			sleepreq = cur_time - statdf.st_mtime; +		if (amtslept == 0) +			pstatus(pp, "Waiting for data file from remote host"); +		amtslept += sleepreq - sleep(sleepreq); +		statres = stat(dfile, &statdf); +	} +	sleepreq = MINWAIT_4DATA; +	while (statres == 0 && amtslept < MAXWAIT_4DATA) { +		if (statdf.st_mtime == prev_mtime) +			break; +		prev_mtime = statdf.st_mtime; +		amtslept += sleepreq - sleep(sleepreq); +		statres = stat(dfile, &statdf); +	} + +	if (statres != 0) +		syslog(LOG_WARNING, "%s: %s disappeared during wait4data()", +		    pp->printer, dfile); +	else if (amtslept > MAXWAIT_4DATA) +		syslog(LOG_WARNING, +		    "%s: %s still changing after %lu secs in wait4data()", +		    pp->printer, dfile, (unsigned long)amtslept); +#if DEBUG_MINWAIT +	else if (amtslept > MINWAIT_4DATA) +		syslog(LOG_INFO, "%s: slept %lu secs in wait4data(%s)", +		    pp->printer, (unsigned long)amtslept, dfile); +#endif +} +#undef	MAXWAIT_ARRIVE +#undef	MAXWAIT_4DATA +#undef	MINWAIT_4DATA + +/* + *  This routine is called to execute one of the filters as was + *  specified in a printcap entry.  While the child-process will read + *  all of 'infd', it is up to the caller to close that file descriptor + *  in the parent process. + */ +static int +execfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd) +{ +	pid_t fpid, wpid; +	int errfd, retcode, wstatus; +	FILE *errfp; +	char buf[BUFSIZ], *slash; + +	fpid = dofork(pp, DORETURN); +	if (fpid != 0) { +		/* +		 * This is the parent process, which just waits for the child +		 * to complete and then returns the result.  Note that it is +		 * the child process which reads the input stream. +		 */ +		if (fpid < 0) +			retcode = 100; +		else { +			while ((wpid = wait(&wstatus)) > 0 && +			    wpid != fpid) +				; +			if (wpid < 0) { +				retcode = 100; +				syslog(LOG_WARNING, +				    "%s: after execv(%s), wait() returned: %m", +				    pp->printer, f_cmd); +			} else +				retcode = WEXITSTATUS(wstatus); +		} + +		/* +		 * Copy everything the filter wrote to stderr from our +		 * temporary errors file to the "lf=" logfile. +		 */ +		errfp = fopen(tempstderr, "r"); +		if (errfp) { +			while (fgets(buf, sizeof(buf), errfp)) +				fputs(buf, stderr); +			fclose(errfp); +		} + +		return (retcode); +	} + +	/* +	 * This is the child process, which is the one that executes the +	 * given filter. +	 */ +	/* +	 * If the first parameter has any slashes in it, then change it +	 * to point to the first character after the last slash. +	 */ +	slash = strrchr(f_av[0], '/'); +	if (slash != NULL) +		f_av[0] = slash + 1; +	/* +	 * XXX - in the future, this should setup an explicit list of +	 *       environment variables and use execve()! +	 */ + +	/* +	 * Setup stdin, stdout, and stderr as we want them when the filter +	 * is running.  Stderr is setup so it points to a temporary errors +	 * file, and the parent process will copy that temporary file to +	 * the real logfile after the filter completes. +	 */ +	dup2(infd, STDIN_FILENO); +	dup2(outfd, STDOUT_FILENO); +	errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664); +	if (errfd >= 0) +		dup2(errfd, STDERR_FILENO); +	closelog(); +	closeallfds(3); +	execv(f_cmd, f_av); +	syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd); +	exit(2); +	/* NOTREACHED */ +} + +/* + * Check to make sure there have been no errors and that both programs + * are in sync with eachother. + * Return non-zero if the connection was lost. + */ +static char +response(const struct printer *pp) +{ +	char resp; + +	if (read(pfd, &resp, 1) != 1) { +		syslog(LOG_INFO, "%s: lost connection", pp->printer); +		return (-1); +	} +	return (resp); +} + +/* + * Banner printing stuff + */ +static void +banner(struct printer *pp, char *name1, char *name2) +{ +	time_t tvec; + +	time(&tvec); +	if (!pp->no_formfeed && !pp->tof) +		(void) write(ofd, pp->form_feed, strlen(pp->form_feed)); +	if (pp->short_banner) {	/* short banner only */ +		if (class[0]) { +			(void) write(ofd, class, strlen(class)); +			(void) write(ofd, ":", 1); +		} +		(void) write(ofd, name1, strlen(name1)); +		(void) write(ofd, "  Job: ", 7); +		(void) write(ofd, name2, strlen(name2)); +		(void) write(ofd, "  Date: ", 8); +		(void) write(ofd, ctime(&tvec), 24); +		(void) write(ofd, "\n", 1); +	} else {	/* normal banner */ +		(void) write(ofd, "\n\n\n", 3); +		scan_out(pp, ofd, name1, '\0'); +		(void) write(ofd, "\n\n", 2); +		scan_out(pp, ofd, name2, '\0'); +		if (class[0]) { +			(void) write(ofd,"\n\n\n",3); +			scan_out(pp, ofd, class, '\0'); +		} +		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15); +		(void) write(ofd, name2, strlen(name2)); +		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12); +		(void) write(ofd, ctime(&tvec), 24); +		(void) write(ofd, "\n", 1); +	} +	if (!pp->no_formfeed) +		(void) write(ofd, pp->form_feed, strlen(pp->form_feed)); +	pp->tof = 1; +} + +static char * +scnline(int key, char *p, int c) +{ +	register int scnwidth; + +	for (scnwidth = WIDTH; --scnwidth;) { +		key <<= 1; +		*p++ = key & 0200 ? c : BACKGND; +	} +	return (p); +} + +#define TRC(q)	(((q)-' ')&0177) + +static void +scan_out(struct printer *pp, int scfd, char *scsp, int dlm) +{ +	register char *strp; +	register int nchrs, j; +	char outbuf[LINELEN+1], *sp, c, cc; +	int d, scnhgt; + +	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) { +		strp = &outbuf[0]; +		sp = scsp; +		for (nchrs = 0; ; ) { +			d = dropit(c = TRC(cc = *sp++)); +			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d)) +				for (j = WIDTH; --j;) +					*strp++ = BACKGND; +			else +				strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc); +			if (*sp == dlm || *sp == '\0' ||  +			    nchrs++ >= pp->page_width/(WIDTH+1)-1) +				break; +			*strp++ = BACKGND; +			*strp++ = BACKGND; +		} +		while (*--strp == BACKGND && strp >= outbuf) +			; +		strp++; +		*strp++ = '\n'; +		(void) write(scfd, outbuf, strp-outbuf); +	} +} + +static int +dropit(int c) +{ +	switch(c) { + +	case TRC('_'): +	case TRC(';'): +	case TRC(','): +	case TRC('g'): +	case TRC('j'): +	case TRC('p'): +	case TRC('q'): +	case TRC('y'): +		return (DROP); + +	default: +		return (0); +	} +} + +/* + * sendmail --- + *   tell people about job completion + */ +static void +sendmail(struct printer *pp, char *userid, int bombed) +{ +	register int i; +	int p[2], s; +	register const char *cp; +	struct stat stb; +	FILE *fp; + +	pipe(p); +	if ((s = dofork(pp, DORETURN)) == 0) {		/* child */ +		dup2(p[0], STDIN_FILENO); +		closelog(); +		closeallfds(3); +		if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL) +			cp++; +		else +			cp = _PATH_SENDMAIL; +		execl(_PATH_SENDMAIL, cp, "-t", (char *)0); +		_exit(0); +	} else if (s > 0) {				/* parent */ +		dup2(p[1], STDOUT_FILENO); +		printf("To: %s@%s\n", userid, origin_host); +		printf("Subject: %s printer job \"%s\"\n", pp->printer, +			*jobname ? jobname : "<unknown>"); +		printf("Reply-To: root@%s\n\n", local_host); +		printf("Your printer job "); +		if (*jobname) +			printf("(%s) ", jobname); + +		switch (bombed) { +		case OK: +			cp = "OK"; +			printf("\ncompleted successfully\n"); +			break; +		default: +		case FATALERR: +			cp = "FATALERR"; +			printf("\ncould not be printed\n"); +			break; +		case NOACCT: +			cp = "NOACCT"; +			printf("\ncould not be printed without an account on %s\n", +			    local_host); +			break; +		case FILTERERR: +			cp = "FILTERERR"; +			if (stat(tempstderr, &stb) < 0 || stb.st_size == 0 +			    || (fp = fopen(tempstderr, "r")) == NULL) { +				printf("\nhad some errors and may not have printed\n"); +				break; +			} +			printf("\nhad the following errors and may not have printed:\n"); +			while ((i = getc(fp)) != EOF) +				putchar(i); +			(void) fclose(fp); +			break; +		case ACCESS: +			cp = "ACCESS"; +			printf("\nwas not printed because it was not linked to the original file\n"); +		} +		fflush(stdout); +		(void) close(STDOUT_FILENO); +	} else { +		syslog(LOG_WARNING, "unable to send mail to %s: %m", userid); +		return; +	} +	(void) close(p[0]); +	(void) close(p[1]); +	wait(NULL); +	syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)", +	    userid, *jobname ? jobname : "<unknown>", pp->printer, cp); +} + +/* + * dofork - fork with retries on failure + */ +static int +dofork(const struct printer *pp, int action) +{ +	pid_t forkpid; +	int i, fail; +	struct passwd *pwd; + +	forkpid = -1; +	if (daemon_uname == NULL) { +		pwd = getpwuid(pp->daemon_user); +		if (pwd == NULL) { +			syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file", +			    pp->printer, pp->daemon_user); +			goto error_ret; +		} +		daemon_uname = strdup(pwd->pw_name); +		daemon_defgid = pwd->pw_gid; +	} + +	for (i = 0; i < 20; i++) { +		forkpid = fork(); +		if (forkpid < 0) { +			sleep((unsigned)(i*i)); +			continue; +		} +		/* +		 * Child should run as daemon instead of root +		 */ +		if (forkpid == 0) { +			errno = 0; +			fail = initgroups(daemon_uname, daemon_defgid); +			if (fail) { +				syslog(LOG_ERR, "%s: initgroups(%s,%u): %m", +				    pp->printer, daemon_uname, daemon_defgid); +				break; +			} +			fail = setgid(daemon_defgid); +			if (fail) { +				syslog(LOG_ERR, "%s: setgid(%u): %m", +				    pp->printer, daemon_defgid); +				break; +			} +			fail = setuid(pp->daemon_user); +			if (fail) { +				syslog(LOG_ERR, "%s: setuid(%ld): %m", +				    pp->printer, pp->daemon_user); +				break; +			} +		} +		return (forkpid); +	} + +	/* +	 * An error occurred.  If the error is in the child process, then +	 * this routine MUST always exit().  DORETURN only effects how +	 * errors should be handled in the parent process. +	 */ +error_ret: +	if (forkpid == 0) { +		syslog(LOG_ERR, "%s: dofork(): aborting child process...", +		    pp->printer); +		exit(1); +	} +	syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer); + +	sleep(1);		/* throttle errors, as a safety measure */ +	switch (action) { +	case DORETURN: +		return (-1); +	default: +		syslog(LOG_ERR, "bad action (%d) to dofork", action); +		/* FALLTHROUGH */ +	case DOABORT: +		exit(1); +	} +	/*NOTREACHED*/ +} + +/* + * Kill child processes to abort current job. + */ +static void +abortpr(int signo __unused) +{ + +	(void) unlink(tempstderr); +	kill(0, SIGINT); +	if (of_pid > 0) +		kill(of_pid, SIGCONT); +	while (wait(NULL) > 0) +		; +	if (of_pid > 0 && tfd != -1) +		unlink(tfile); +	exit(0); +} + +static void +init(struct printer *pp) +{ +	char *s; + +	sprintf(&width[2], "%ld", pp->page_width); +	sprintf(&length[2], "%ld", pp->page_length); +	sprintf(&pxwidth[2], "%ld", pp->page_pwidth); +	sprintf(&pxlength[2], "%ld", pp->page_plength); +	if ((s = checkremote(pp)) != NULL) { +		syslog(LOG_WARNING, "%s", s); +		free(s); +	} +} + +void +startprinting(const char *printer) +{ +	struct printer myprinter, *pp = &myprinter; +	int status; + +	init_printer(pp); +	status = getprintcap(printer, pp); +	switch(status) { +	case PCAPERR_OSERR: +		syslog(LOG_ERR, "can't open printer description file: %m"); +		exit(1); +	case PCAPERR_NOTFOUND: +		syslog(LOG_ERR, "unknown printer: %s", printer); +		exit(1); +	case PCAPERR_TCLOOP: +		fatal(pp, "potential reference loop detected in printcap file"); +	default: +		break; +	} +	printjob(pp); +} + +/* + * Acquire line printer or remote connection. + */ +static void +openpr(const struct printer *pp) +{ +	int p[2]; +	char *cp; + +	if (pp->remote) { +		openrem(pp); +		/* +		 * Lpd does support the setting of 'of=' filters for +		 * jobs going to remote machines, but that does not +		 * have the same meaning as 'of=' does when handling +		 * local print queues.  For remote machines, all 'of=' +		 * filter processing is handled in sendfile(), and that +		 * does not use these global "output filter" variables. +		 */  +		ofd = -1; +		of_pid = 0; +		return; +	} else if (*pp->lp) { +		if (strchr(pp->lp, '@') != NULL) +			opennet(pp); +		else +			opentty(pp); +	} else { +		syslog(LOG_ERR, "%s: no line printer device or host name", +		    pp->printer); +		exit(1); +	} + +	/* +	 * Start up an output filter, if needed. +	 */ +	if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) { +		pipe(p); +		if (pp->remote) { +			strcpy(tfile, TFILENAME); +			tfd = mkstemp(tfile); +		} +		if ((of_pid = dofork(pp, DOABORT)) == 0) {	/* child */ +			dup2(p[0], STDIN_FILENO);	/* pipe is std in */ +			/* tfile/printer is stdout */ +			dup2(pp->remote ? tfd : pfd, STDOUT_FILENO); +			closelog(); +			closeallfds(3); +			if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL) +				cp = pp->filters[LPF_OUTPUT]; +			else +				cp++; +			execl(pp->filters[LPF_OUTPUT], cp, width, length, +			      (char *)0); +			syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer, +			    pp->filters[LPF_OUTPUT]); +			exit(1); +		} +		(void) close(p[0]);		/* close input side */ +		ofd = p[1];			/* use pipe for output */ +	} else { +		ofd = pfd; +		of_pid = 0; +	} +} + +/* + * Printer connected directly to the network + * or to a terminal server on the net + */ +static void +opennet(const struct printer *pp) +{ +	register int i; +	int resp; +	u_long port; +	char *ep; +	void (*savealrm)(int); + +	port = strtoul(pp->lp, &ep, 0); +	if (*ep != '@' || port > 65535) { +		syslog(LOG_ERR, "%s: bad port number: %s", pp->printer, +		    pp->lp); +		exit(1); +	} +	ep++; + +	for (i = 1; ; i = i < 256 ? i << 1 : i) { +		resp = -1; +		savealrm = signal(SIGALRM, alarmhandler); +		alarm(pp->conn_timeout); +		pfd = getport(pp, ep, port); +		alarm(0); +		(void)signal(SIGALRM, savealrm); +		if (pfd < 0 && errno == ECONNREFUSED) +			resp = 1; +		else if (pfd >= 0) { +			/* +			 * need to delay a bit for rs232 lines +			 * to stabilize in case printer is +			 * connected via a terminal server +			 */ +			delay(500); +			break; +		} +		if (i == 1) { +			if (resp < 0) +				pstatus(pp, "waiting for %s to come up", +					pp->lp); +			else +				pstatus(pp,  +					"waiting for access to printer on %s", +					pp->lp); +		} +		sleep(i); +	} +	pstatus(pp, "sending to %s port %lu", ep, port); +} + +/* + * Printer is connected to an RS232 port on this host + */ +static void +opentty(const struct printer *pp) +{ +	register int i; + +	for (i = 1; ; i = i < 32 ? i << 1 : i) { +		pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY); +		if (pfd >= 0) { +			delay(500); +			break; +		} +		if (errno == ENOENT) { +			syslog(LOG_ERR, "%s: %m", pp->lp); +			exit(1); +		} +		if (i == 1) +			pstatus(pp,  +				"waiting for %s to become ready (offline?)", +				pp->printer); +		sleep(i); +	} +	if (isatty(pfd)) +		setty(pp); +	pstatus(pp, "%s is ready and printing", pp->printer); +} + +/* + * Printer is on a remote host + */ +static void +openrem(const struct printer *pp) +{ +	register int i; +	int resp; +	void (*savealrm)(int); + +	for (i = 1; ; i = i < 256 ? i << 1 : i) { +		resp = -1; +		savealrm = signal(SIGALRM, alarmhandler); +		alarm(pp->conn_timeout); +		pfd = getport(pp, pp->remote_host, 0); +		alarm(0); +		(void)signal(SIGALRM, savealrm); +		if (pfd >= 0) { +			if ((writel(pfd, "\2", pp->remote_queue, "\n",  +				    (char *)0) +			     == 2 + strlen(pp->remote_queue)) +			    && (resp = response(pp)) == 0) +				break; +			(void) close(pfd); +		} +		if (i == 1) { +			if (resp < 0) +				pstatus(pp, "waiting for %s to come up",  +					pp->remote_host); +			else { +				pstatus(pp, +					"waiting for queue to be enabled on %s", +					pp->remote_host); +				i = 256; +			} +		} +		sleep(i); +	} +	pstatus(pp, "sending to %s", pp->remote_host); +} + +/* + * setup tty lines. + */ +static void +setty(const struct printer *pp) +{ +	struct termios ttybuf; + +	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) { +		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer); +		exit(1); +	} +	if (tcgetattr(pfd, &ttybuf) < 0) { +		syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer); +		exit(1); +	} +	if (pp->baud_rate > 0) +		cfsetspeed(&ttybuf, pp->baud_rate); +	if (pp->mode_set) { +		char *s = strdup(pp->mode_set), *tmp; + +		while ((tmp = strsep(&s, ",")) != NULL) { +			(void) msearch(tmp, &ttybuf); +		} +	} +	if (pp->mode_set != 0 || pp->baud_rate > 0) { +		if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) { +			syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer); +		} +	} +} + +#include <stdarg.h> + +static void +pstatus(const struct printer *pp, const char *msg, ...) +{ +	int fd; +	char *buf; +	va_list ap; +	va_start(ap, msg); + +	umask(S_IWOTH); +	fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE); +	if (fd < 0) { +		syslog(LOG_ERR, "%s: open(%s): %m", pp->printer, +		    pp->status_file); +		exit(1); +	} +	ftruncate(fd, 0); +	vasprintf(&buf, msg, ap); +	va_end(ap); +	writel(fd, buf, "\n", (char *)0); +	close(fd); +	free(buf); +} + +void +alarmhandler(int signo __unused) +{ +	/* the signal is ignored */ +	/* (the '__unused' is just to avoid a compile-time warning) */ +} diff --git a/usr.sbin/lpr/lpd/recvjob.c b/usr.sbin/lpr/lpd/recvjob.c new file mode 100644 index 000000000000..f103829b19e8 --- /dev/null +++ b/usr.sbin/lpr/lpd/recvjob.c @@ -0,0 +1,393 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1983, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */ +/* + * Receive printer jobs from the network, queue them and + * start the printer daemon. + */ +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <syslog.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "lp.h" +#include "lp.local.h" +#include "ctlinfo.h" +#include "extern.h" +#include "pathnames.h" + +#define ack()	(void) write(STDOUT_FILENO, sp, (size_t)1) + +/* + * The buffer size to use when reading/writing spool files. + */ +#define	SPL_BUFSIZ	BUFSIZ + +static char	 dfname[NAME_MAX];	/* data files */ +static int	 minfree;       /* keep at least minfree blocks available */ +static const char	*sp = ""; +static char	 tfname[NAME_MAX];	/* tmp copy of cf before linking */ + +static int	 chksize(int _size); +static void	 frecverr(const char *_msg, ...) __printf0like(1, 2); +static int	 noresponse(void); +static void	 rcleanup(int _signo); +static int	 read_number(const char *_fn); +static int	 readfile(struct printer *_pp, char *_file, size_t _size); +static int	 readjob(struct printer *_pp); + + +void +recvjob(const char *printer) +{ +	struct stat stb; +	int status; +	struct printer myprinter, *pp = &myprinter; + +	/* +	 * Perform lookup for printer name or abbreviation +	 */ +	init_printer(pp); +	status = getprintcap(printer, pp); +	switch (status) { +	case PCAPERR_OSERR: +		frecverr("cannot open printer description file"); +		break; +	case PCAPERR_NOTFOUND: +		frecverr("unknown printer %s", printer); +		break; +	case PCAPERR_TCLOOP: +		fatal(pp, "potential reference loop detected in printcap file"); +	default: +		break; +	} +	 +	(void) close(STDERR_FILENO);			/* set up log file */ +	if (open(pp->log_file, O_WRONLY|O_APPEND, 0664) < 0) { +		syslog(LOG_ERR, "%s: %m", pp->log_file); +		(void) open(_PATH_DEVNULL, O_WRONLY); +	} + +	if (chdir(pp->spool_dir) < 0) +		frecverr("%s: chdir(%s): %s", pp->printer, pp->spool_dir, +		    strerror(errno)); +	if (stat(pp->lock_file, &stb) == 0) { +		if (stb.st_mode & 010) { +			/* queue is disabled */ +			putchar('\1');		/* return error code */ +			exit(1); +		} +	} else if (stat(pp->spool_dir, &stb) < 0) +		frecverr("%s: stat(%s): %s", pp->printer, pp->spool_dir, +		    strerror(errno)); +	minfree = 2 * read_number("minfree");	/* scale KB to 512 blocks */ +	signal(SIGTERM, rcleanup); +	signal(SIGPIPE, rcleanup); + +	if (readjob(pp)) +		printjob(pp); +} + +/* + * Read printer jobs sent by lpd and copy them to the spooling directory. + * Return the number of jobs successfully transferred. + */ +static int +readjob(struct printer *pp) +{ +	register int size; +	int cfcnt, dfcnt; +	char *cp, *clastp, *errmsg; +	char givenid[32], givenhost[MAXHOSTNAMELEN]; + +	ack(); +	cfcnt = 0; +	dfcnt = 0; +	for (;;) { +		/* +		 * Read a command to tell us what to do +		 */ +		cp = line; +		clastp = line + sizeof(line) - 1; +		do { +			size = read(STDOUT_FILENO, cp, (size_t)1); +			if (size != (ssize_t)1) { +				if (size < (ssize_t)0) { +					frecverr("%s: lost connection", +					    pp->printer); +					/*NOTREACHED*/ +				} +				return (cfcnt); +			} +		} while ((*cp++ != '\n') && (cp <= clastp)); +		if (cp > clastp) { +			frecverr("%s: readjob overflow", pp->printer); +			/*NOTREACHED*/ +		} +		*--cp = '\0'; +		cp = line; +		switch (*cp++) { +		case '\1':	/* cleanup because data sent was bad */ +			rcleanup(0); +			continue; + +		case '\2':	/* read cf file */ +			size = 0; +			dfcnt = 0; +			while (*cp >= '0' && *cp <= '9') +				size = size * 10 + (*cp++ - '0'); +			if (*cp++ != ' ') +				break; +			/* +			 * host name has been authenticated, we use our +			 * view of the host name since we may be passed +			 * something different than what gethostbyaddr() +			 * returns +			 */ +			strlcpy(cp + 6, from_host, sizeof(line) +			    + (size_t)(line - cp - 6)); +			if (strchr(cp, '/')) { +				frecverr("readjob: %s: illegal path name", cp); +				/*NOTREACHED*/ +			} +			strlcpy(tfname, cp, sizeof(tfname)); +			tfname[sizeof (tfname) - 1] = '\0'; +			tfname[0] = 't'; +			if (!chksize(size)) { +				(void) write(STDOUT_FILENO, "\2", (size_t)1); +				continue; +			} +			if (!readfile(pp, tfname, (size_t)size)) { +				rcleanup(0); +				continue; +			} +			errmsg = ctl_renametf(pp->printer, tfname); +			tfname[0] = '\0'; +			if (errmsg != NULL) { +				frecverr("%s: %s", pp->printer, errmsg); +				/*NOTREACHED*/ +			} +			cfcnt++; +			continue; + +		case '\3':	/* read df file */ +			*givenid = '\0'; +			*givenhost = '\0'; +			size = 0; +			while (*cp >= '0' && *cp <= '9') +				size = size * 10 + (*cp++ - '0'); +			if (*cp++ != ' ') +				break; +			if (strchr(cp, '/')) { +				frecverr("readjob: %s: illegal path name", cp); +				/*NOTREACHED*/ +			} +			if (!chksize(size)) { +				(void) write(STDOUT_FILENO, "\2", (size_t)1); +				continue; +			} +			strlcpy(dfname, cp, sizeof(dfname)); +			dfcnt++; +			trstat_init(pp, dfname, dfcnt); +			(void) readfile(pp, dfname, (size_t)size); +			trstat_write(pp, TR_RECVING, (size_t)size, givenid, +			    from_host, givenhost); +			continue; +		} +		frecverr("protocol screwup: %s", line); +		/*NOTREACHED*/ +	} +} + +/* + * Read files send by lpd and copy them to the spooling directory. + */ +static int +readfile(struct printer *pp, char *file, size_t size) +{ +	register char *cp; +	char buf[SPL_BUFSIZ]; +	size_t amt, i; +	int err, fd, j; + +	fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD); +	if (fd < 0) { +		frecverr("%s: readfile: error on open(%s): %s", +			 pp->printer, file, strerror(errno)); +		/*NOTREACHED*/ +	} +	ack(); +	err = 0; +	for (i = 0; i < size; i += SPL_BUFSIZ) { +		amt = SPL_BUFSIZ; +		cp = buf; +		if (i + amt > size) +			amt = size - i; +		do { +			j = read(STDOUT_FILENO, cp, amt); +			if (j <= 0) { +				frecverr("%s: lost connection", pp->printer); +				/*NOTREACHED*/ +			} +			amt -= j; +			cp += j; +		} while (amt > 0); +		amt = SPL_BUFSIZ; +		if (i + amt > size) +			amt = size - i; +		if (write(fd, buf, amt) != (ssize_t)amt) { +			err++; +			break; +		} +	} +	(void) close(fd); +	if (err) { +		frecverr("%s: write error on close(%s)", pp->printer, file); +		/*NOTREACHED*/ +	} +	if (noresponse()) {		/* file sent had bad data in it */ +		if (strchr(file, '/') == NULL) +			(void) unlink(file); +		return (0); +	} +	ack(); +	return (1); +} + +static int +noresponse(void) +{ +	char resp; + +	if (read(STDOUT_FILENO, &resp, (size_t)1) != 1) { +		frecverr("lost connection in noresponse()"); +		/*NOTREACHED*/ +	} +	if (resp == '\0') +		return(0); +	return(1); +} + +/* + * Check to see if there is enough space on the disk for size bytes. + * 1 == OK, 0 == Not OK. + */ +static int +chksize(int size) +{ +	int64_t spacefree; +	struct statfs sfb; + +	if (statfs(".", &sfb) < 0) { +		syslog(LOG_ERR, "%s: %m", "statfs(\".\")"); +		return (1); +	} +	spacefree = sfb.f_bavail * (sfb.f_bsize / 512); +	size = (size + 511) / 512; +	if (minfree + size > spacefree) +		return(0); +	return(1); +} + +static int +read_number(const char *fn) +{ +	char lin[80]; +	register FILE *fp; + +	if ((fp = fopen(fn, "r")) == NULL) +		return (0); +	if (fgets(lin, sizeof(lin), fp) == NULL) { +		fclose(fp); +		return (0); +	} +	fclose(fp); +	return (atoi(lin)); +} + +/* + * Remove all the files associated with the current job being transferred. + */ +static void +rcleanup(int signo __unused) +{ +	if (tfname[0] && strchr(tfname, '/') == NULL) +		(void) unlink(tfname); +	if (dfname[0] && strchr(dfname, '/') == NULL) { +		do { +			do +				(void) unlink(dfname); +			while (dfname[2]-- != 'A'); +			dfname[2] = 'z'; +		} while (dfname[0]-- != 'd'); +	} +	dfname[0] = '\0'; +} + +#include <stdarg.h> + +static void +frecverr(const char *msg, ...) +{ +	va_list ap; +	va_start(ap, msg); +	syslog(LOG_ERR, "Error receiving job from %s:", from_host); +	vsyslog(LOG_ERR, msg, ap); +	va_end(ap); +	/* +	 * rcleanup is not called until AFTER logging the error message, +	 * because rcleanup will zap some variables which may have been +	 * supplied as parameters for that msg... +	 */ +	rcleanup(0); +	/*  +	 * Add a minimal delay before returning the final error code to +	 * the sending host.  This 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 result might be +	 * obscure performance problems and a flood of syslog messages on +	 * the receiving host. +	 */  +	sleep(2);		/* a paranoid throttling measure */ +	putchar('\1');		/* return error code */ +	exit(1); +} | 
