Initial revision

This commit is contained in:
Michael Brown
2005-05-17 16:44:57 +00:00
parent 75a5374d79
commit 1097cf8685
164 changed files with 24592 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
# Comment out the second command and uncomment the first command
# below if you don't want to use libwrap (hosts.{allow,deny} access control)
# If you don't have it in /var/log/subsys, uncomment and define
#CFLAGS+=-DLOCKFILE_DIR=\"/var/log\"
LIBWRAP=-lwrap
p910nd: p910nd.c
# $(CC) -Wall $(CFLAGS) -o $@ p910nd.c
$(CC) -Wall $(CFLAGS) -DUSE_LIBWRAP -o $@ p910nd.c $(LIBWRAP)

9
contrib/p910nd-0.8/banner.pl Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/perl
while (1)
{
exit 0 if read(STDIN,$c,1) == 0;
last if ($cl eq "\031" && $c eq "\001");
$cl = $c;
}
kill 'STOP',$$;
exit 0

58
contrib/p910nd-0.8/client.pl Executable file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/perl
# edit this to the printer hostname
$them = 'ken';
$port = 9101;
open(STDIN, "$ARGV[0]") if $#ARGV >= 0;
use Socket;
#use Sys::Hostname;
#$hostname = hostname;
($name, $aliases, $proto) = getprotobyname('tcp');
($name, $aliases, $port) = getservbyname($port, 'tcp')
unless $port =~ /^\d+$/;
#$thisaddr = inet_aton($hostname);
#defined($thisaddr) or &errexit("inet_aton: cannot resolve $hostname\n");
$thataddr = inet_aton($them);
defined($thataddr) or &errexit("inet_aton: cannot resolve $them\n");
socket(S, PF_INET, SOCK_STREAM, $proto) or &errexit("socket: $!\n");
#$this = sockaddr_in(0, $thisaddr);
#bind(S, $this) || &errexit("bind: $!\n");
$that = sockaddr_in($port, $thataddr);
connect(S, $that) || &errexit("connect: $!\n");
select(S); $| = 1; select(STDOUT);
$buffer = '';
while (1)
{
$rin = '';
vec($rin, fileno(S), 1) = 1;
$nfound = select($rout=$rin, $wout=$rin, undef, undef);
if (vec($rout, fileno(S), 1)) {
print STDERR "$buffer\n" if
defined($nread = sysread(S, $buffer, 8192));
}
if (vec($wout, fileno(S), 1)) {
$nread = read(STDIN, $buffer, 8192);
last if $nread == 0;
&errexit("write: $!\n") unless
defined($written = syswrite(S,$buffer,$nread));
}
}
close(S);
exit 0;
sub errexit
{
print STDERR @_;
exit 2;
}

View File

@@ -0,0 +1,93 @@
.TH P910ND 8 "1 August 2004"
.SH NAME
p910nd \- port 9100+n printer daemon
.SH SYNOPSIS
.B p910nd
[\fB-f device\fR]
[\fB-i bindaddr\fR]
[\fB-bv\fR]
[\fB0|1|2\fR]
.SH DESCRIPTION
.I p910nd
is a small daemon that copies any data received on the port
it is listening on to the corresponding printer port.
It is primarily intended for diskless Linux hosts running as printer drivers
but there is no reason why it could not be used on diskful hosts.
Port 9100 is copied to /dev/lp0, 9101 to /dev/lp1 and 9102 to /dev/lp2.
The default is port 9100 to /dev/lp0.
.LP
The \fB-f\fR option can be used to specify a different printer device,
e.g. /dev/usblp0.
.LP
The \fB-i\fR option can be used to specify binding to one address instead
of all interfaces which is the default.
.LP
The \fB-b\fR option turns on bidirectional copying.
.LP
The \fB-v\fR option shows the version number.
.SH INSTALLATION
.I p910nd
can be run as a standalone daemon or from inetd.
It will automatically detect if it is running under inetd.
.LP
A sample SysVinit script,
.IR p910nd.sh ,
is provided for operation as a daemon.
.I p910nd
will change its name under ps to match the printer port, i.e.
.I p9100d, p9101d
and
.IR p9102d .
.LP
When running under inetd, the
.I /etc/inetd.conf
entry should look something like this (with tcpwrappers protection):
.sp
.nf
p9101 stream tcp nowait root /usr/sbin/tcpd /sbin/p910nd
.fi
.sp
Don't forget to add an entry in
.I /etc/services
for the corresponding port.
.LP
If operating with lprng, use the host%port syntax for the
printer device to send jobs to it.
.LP
If operating with CUPS, this is supported as the AppSocket
protocol, also known as the JetDirect (probably TM) protocol.
.LP
If operating with classic Berkeley lpd, a sample client,
.IR client.pl ,
is provided.
This should be installed as the ifilter (if=) in /etc/printcap.
.I banner.pl
should be installed as the ofilter (of=) in /etc/printcap.
It may be necessary to create a dummy spool file for lpd (lp=).
This file will be opened but not written to.
The corresponding C versions are left as an exercise for the reader.
.LP
When running under inetd, more than one instance could be started.
To avoid problems with multiple instances attempting to access the
printer at the same time, make sure that only one client is active
at any one time. This can be done by designating one host as the
spooler and sending all jobs to this host. You will probably
need to set up an intermediate queue anyway to provide print job filtering.
.LP
If compiled with USE_LIBWRAP and linked with -lwrap, it uses the libwrap
library (tcpwrappers). Access control can be done with /etc/hosts.allow
and /etc/hosts.deny. The service name is p910nd.
.SH DIAGNOSTICS
.I p910nd
logs error messages to syslog.
.SH "SEE ALSO"
printcap(5), hosts_access(5)
.SH FILES
/var/run/p9100d.pid, /var/lock/subsys/p9100d, /etc/hosts.allow, /etc/hosts.deny
.SH COPYRIGHT
.I p910nd
is under the GNU Public License
.SH AUTHOR
Ken Yap (ken_yap@users.sourceforge.net)
.SH DATE
Version 0.8 October 2004

420
contrib/p910nd-0.8/p910nd.c Normal file
View File

@@ -0,0 +1,420 @@
/*
* Port 9100+n daemon
* Accepts a connection from port 9100+n and copy stream to
* /dev/lpn, where n = 0,1,2.
*
* Run standalone as: p910nd [0|1|2]
*
* Run under inetd as:
* p910n stream tcp nowait root /usr/sbin/tcpd p910nd [0|1|2]
* where p910n is an /etc/services entry for
* port 9100, 9101 or 9102 as the case may be.
* root can be replaced by any uid with rw permission on /dev/lpn
*
* Port 9100+n will then be passively opened
* n defaults to 0
*
* Version 0.8
* Allow specifying address to bind to
*
* Version 0.7
* Bidirectional data transfer
*
* Version 0.6
* Arne Bernin fixed some cast warnings, corrected the version number
* and added a -v option to print the version.
*
* Version 0.5
* -DUSE_LIBWRAP and -lwrap enables hosts_access (tcpwrappers) checking.
*
* Version 0.4
* Ken Yap (ken_yap@users.sourceforge.net), April 2001
* Placed under GPL.
*
* Added -f switch to specify device which overrides /dev/lpn.
* But number is still required get distinct ports and locks.
*
* Added locking so that two invocations of the daemon under inetd
* don't try to open the printer at the same time. This can happen
* even if there is one host running clients because the previous
* client can exit after it has sent all data but the printer has not
* finished printing and inetd starts up a new daemon when the next
* request comes in too soon.
*
* Various things could be Linux specific. I don't
* think there is much demand for this program outside of PCs,
* but if you port it to other distributions or platforms,
* I'd be happy to receive your patches.
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <netdb.h>
#include <syslog.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef USE_LIBWRAP
#include "tcpd.h"
int allow_severity, deny_severity;
extern int hosts_ctl(char *daemon, char *client_name,
char *client_addr, char *client_user);
#endif
#define BASEPORT 9100
#define PIDFILE "/var/run/p910%cd.pid"
#ifdef LOCKFILE_DIR
#define LOCKFILE LOCKFILE_DIR "/p910%cd"
#else
#define LOCKFILE "/var/lock/subsys/p910%cd"
#endif
#define PRINTERFILE "/dev/lp%c"
#define LOGOPTS LOG_ERR
static char *progname;
static char version[] = "p910nd Version 0.8";
static int lockfd = -1;
static char *device = 0;
static int bidir = 0;
static char *bindaddr = 0;
void usage(void)
{
fprintf(stderr, "Usage: %s [-f device] [-i bindaddr] [-bv] [0|1|2]\n", progname);
exit(1);
}
void show_version (void)
{
fprintf(stdout, "%s \n", version);
}
FILE *open_printer(int lpnumber)
{
FILE *f;
char lpname[sizeof(PRINTERFILE)];
#ifdef TESTING
(void)snprintf(lpname, sizeof(lpname), "/dev/tty");
#else
(void)snprintf(lpname, sizeof(lpname), PRINTERFILE, lpnumber);
#endif
if (device == 0)
device = lpname;
if ((f = fopen(device, bidir ? "w+" : "w")) == NULL)
{
syslog(LOGOPTS, "%s: %m\n", device);
exit(1);
}
return (f);
}
int get_lock(int lpnumber)
{
char lockname[sizeof(LOCKFILE)];
struct flock lplock;
(void)snprintf(lockname, sizeof(lockname), LOCKFILE, lpnumber);
if ((lockfd = open(lockname, O_CREAT|O_RDWR)) < 0)
{
syslog(LOGOPTS, "%s: %m\n", lockname);
return (0);
}
memset(&lplock, 0, sizeof(lplock));
lplock.l_type = F_WRLCK;
lplock.l_pid = getpid();
if (fcntl(lockfd, F_SETLKW, &lplock) < 0)
{
syslog(LOGOPTS, "%s: %m\n", lockname);
return (0);
}
return (1);
}
void free_lock(void)
{
if (lockfd >= 0)
(void)close(lockfd);
}
/* Copy network socket to FILE f until EOS */
int copy_stream(int fd, FILE *f)
{
int nread;
char buffer[8192];
if (bidir) {
FILE *nf;
if ((nf = fdopen(fd, "w")) == NULL) {
syslog(LOGOPTS, "fdopen: %m\n");
}
for (;;) {
fd_set readfds;
int result;
int maxfd = fileno(f) > fd ? fileno(f) : fd;
FD_ZERO(&readfds);
FD_SET(fileno(f), &readfds);
FD_SET(fd, &readfds);
result = select(maxfd + 1, &readfds, 0, 0, 0);
if (result < 0)
return (result);
if (result == 0)
continue;
if (FD_ISSET(fd, &readfds)) {
nread = read(fd, buffer, sizeof(buffer));
if (nread <= 0)
break;
(void)fwrite(buffer, sizeof(char), nread, f);
}
if (FD_ISSET(fileno(f), &readfds)) {
nread = read(fileno(f), buffer, sizeof(buffer));
if (nread > 0 && nf != NULL) {
(void)fwrite(buffer, sizeof(char), nread, nf);
(void)fflush(nf);
}
}
}
(void)fflush(f);
(void)fclose(nf);
return (0);
} else {
while ((nread = read(fd, buffer, sizeof(buffer))) > 0)
(void)fwrite(buffer, sizeof(char), nread, f);
(void)fflush(f);
return (nread);
}
}
void one_job(int lpnumber)
{
FILE *f;
struct sockaddr_in client;
socklen_t clientlen = sizeof(client);
if (getpeername(0, (struct sockaddr*) &client, &clientlen) >= 0)
syslog(LOGOPTS, "Connection from %s port %hu\n",
inet_ntoa(client.sin_addr),
ntohs(client.sin_port));
if (get_lock(lpnumber) == 0)
return;
f = open_printer(lpnumber);
if (copy_stream(0, f) < 0)
syslog(LOGOPTS, "copy_stream: %m\n");
fclose(f);
free_lock();
}
void server(int lpnumber)
{
struct rlimit resourcelimit;
#ifdef USE_GETPROTOBYNAME
struct protoent *proto;
#endif
int netfd, fd, one = 1;
socklen_t clientlen;
struct sockaddr_in netaddr, client;
char pidfilename[sizeof(PIDFILE)];
FILE *f;
int ipret;
#ifndef TESTING
switch (fork())
{
case -1:
syslog(LOGOPTS, "fork: %m\n");
exit (1);
case 0: /* child */
break;
default: /* parent */
exit(0);
}
/* Now in child process */
resourcelimit.rlim_max = 0;
if (getrlimit(RLIMIT_NOFILE, &resourcelimit) < 0)
{
syslog(LOGOPTS, "getrlimit: %m\n");
exit(1);
}
for (fd = 0; fd < resourcelimit.rlim_max; ++fd)
(void)close(fd);
if (setsid() < 0)
{
syslog(LOGOPTS, "setsid: %m\n");
exit(1);
}
(void)chdir("/");
(void)umask(022);
fd = open("/dev/null", O_RDWR); /* stdin */
(void)dup(fd); /* stdout */
(void)dup(fd); /* stderr */
(void)snprintf(pidfilename, sizeof(pidfilename), PIDFILE, lpnumber);
if ((f = fopen(pidfilename, "w")) == NULL)
{
syslog(LOGOPTS, "%s: %m\n", pidfilename);
exit(1);
}
(void)fprintf(f, "%d\n", getpid());
(void)fclose(f);
if (get_lock(lpnumber) == 0)
exit(1);
#endif
f = open_printer(lpnumber);
#ifdef USE_GETPROTOBYNAME
if ((proto = getprotobyname("tcp")) == NULL)
{
syslog(LOGOPTS, "Cannot find protocol for TCP!\n");
exit(1);
}
if ((netfd = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0)
#else
if ((netfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
#endif
{
syslog(LOGOPTS, "socket: %m\n");
exit(1);
}
if (setsockopt(netfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
{
syslog(LOGOPTS, "setsocketopt: %m\n");
exit(1);
}
netaddr.sin_port = htons(BASEPORT + lpnumber - '0');
if (bindaddr == 0) {
netaddr.sin_addr.s_addr = htonl(INADDR_ANY);
} else {
ipret = inet_pton(AF_INET, bindaddr, &netaddr.sin_addr.s_addr);
if (ipret < 0) {
syslog(LOGOPTS, "inet_pton: %m\n");
exit(1);
} else if (ipret == 0) {
syslog(LOGOPTS, "inet_pton: invalid bind IP address\n");
exit(1);
}
}
memset(netaddr.sin_zero, 0, sizeof(netaddr.sin_zero));
if (bind(netfd, (struct sockaddr*) &netaddr, sizeof(netaddr)) < 0)
{
syslog(LOGOPTS, "bind: %m\n");
exit(1);
}
if (listen(netfd, 5) < 0)
{
syslog(LOGOPTS, "listen: %m\n");
exit(1);
}
clientlen = sizeof(client);
memset(&client, 0, sizeof(client));
while ((fd = accept(netfd, (struct sockaddr*) &client, &clientlen)) >= 0)
{
#ifdef USE_LIBWRAP
if (hosts_ctl("p910nd", STRING_UNKNOWN,
inet_ntoa(client.sin_addr), STRING_UNKNOWN) == 0) {
syslog(LOGOPTS, "Connection from %s port %hd rejected\n",
inet_ntoa(client.sin_addr),
ntohs(client.sin_port));
close(fd);
continue;
}
#endif
syslog(LOGOPTS, "Connection from %s port %hd accepted\n",
inet_ntoa(client.sin_addr),
ntohs(client.sin_port));
/*write(fd, "Printing", 8);*/
if (copy_stream(fd, f) < 0)
syslog(LOGOPTS, "copy_stream: %m\n");
(void)close(fd);
}
syslog(LOGOPTS, "accept: %m\n");
free_lock();
exit(1);
}
int is_standalone(void)
{
struct sockaddr_in bind_addr;
socklen_t ba_len;
/*
* Check to see if a socket was passed to us from inetd.
*
* Use getsockname() to determine if descriptor 0 is indeed a socket
* (and thus we are probably a child of inetd) or if it is instead
* something else and we are running standalone.
*/
ba_len = sizeof(bind_addr);
if (getsockname(0, (struct sockaddr*) &bind_addr, &ba_len) == 0)
return (0); /* under inetd */
if (errno != ENOTSOCK) /* strange... */
syslog(LOGOPTS, "getsockname: %m\n");
return (1);
}
int main(int argc, char *argv[])
{
int c, lpnumber;
char *p;
if (argc <= 0) /* in case not provided in inetd.conf */
progname = "p910nd";
else
{
progname = argv[0];
if ((p = strrchr(progname, '/')) != 0)
progname = p + 1;
}
lpnumber = '0';
while ((c = getopt(argc, argv, "bi:f:v")) != EOF)
{
switch (c)
{
case 'b':
bidir = 1;
break;
case 'f':
device = optarg;
break;
case 'i':
bindaddr = optarg;
break;
case 'v':
show_version();
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc > 0)
{
if (isdigit(argv[0][0]))
lpnumber = argv[0][0];
}
/* change the n in argv[0] to match the port so ps will show that */
if ((p = strstr(progname, "p910n")) != NULL)
p[4] = lpnumber;
/* We used to pass (LOG_PERROR|LOG_PID|LOG_LPR|LOG_ERR) to syslog, but
* syslog ignored the LOG_PID and LOG_PERROR option. I.e. the intention
* was to add both options but the effect was to have neither.
* I disagree with the intention to add PERROR. --Stef */
openlog (p, LOG_PID, LOG_LPR);
if (is_standalone())
server(lpnumber);
else
one_job(lpnumber);
return (0);
}

39
contrib/p910nd-0.8/p910nd.sh Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
#
# p910nd.sh This shell script takes care of starting and stopping
# p910nd (port 9100+n printer daemon)
# This script only controls the one on port 9101.
# You can start others if you wish.
#
# Todo: Make it fully LSB
# See how we were called.
case "$1" in
start)
# Start daemons.
echo -n "Starting p910nd: "
# default port is 1 so it will appear as p9101d on a ps
start_daemon p910nd
echo
;;
stop)
# Stop daemons.
echo -n "Shutting down p910nd: "
killproc p9101d
echo
rm -f /var/run/p9101.pid
;;
status)
status p9101d
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: p910nd {start|stop|restart|status}"
exit 1
esac
exit 0