One of the features of lpd is that it supports printing over the network to printers physically connected to a different machine. With the careful combination of filter scripts and assorted utilities, you can make lpr print transparently to printers on all sorts of networks.
To allow remote machines to print to your printer, you must list the
machines in /etc/hosts.equiv or /etc/hosts.lpd.
(Note that hosts.equiv has a host of other effects; be sure you
know what you are doing if you list any machine there). You can allow
only certain users on the other machines to print to your printer by
usign the rs attribute; read the
lpd
man page
for information on this.
lpd
To print to another machine, you make an /etc/printcap entry like this:
# REMOTE djet500
lp|dj|deskjet:\
:sd=/var/spool/lpd/dj:\
:rm=machine.out.there.com:\
:rp=printername:\
:lp=/dev/null:\
:sh:
Note that there is still a spool directory on the local machine
managed by lpd
. If the remote machine is busy or offline, print
jobs from the local machine wait in the spool area until they can be
sent.
rlpr
You can also use rlpr to send a print job directly to a queue on a remote machine without going through the hassle of configuring lpd to handle it. This is mostly useful in situations where you print to a variety of printers only occasionally. From the announcement for rlpr:
Rlpr uses TCP/IP to send print jobs to lpd servers anywhere on a network.
Unlike lpr, it *does not* require that the remote printers be explicitly known to the machine you wish to print from, (e.g. through /etc/printcap) and thus is considerably more flexible and requires less administration.
rlpr can be used anywhere a traditional lpr might be used, and is backwards compatible with traditional BSD lpr.
The main power gained by rlpr is the power to print remotely *from anywhere to anywhere* without regard for how the system you wish to print from was configured. Can work as a filter just like traditional lpr so that clients executing on a remote machine like netscape, xemacs, etc, etc can print to your local machine with little effort.
Rlpr is available from
SunSite
.
It is possible to direct an lpd queue through the
smbclient
program (part of the samba suite) to a TCP/IP based SMB print service.
Samba includes a script to do this called smbprint
. In short,
you put a configuration file for the specific printer in question in
the spool directory, and install the smbprint
script as the
if.
The /etc/printcap entry goes like this:
lp|remote-smbprinter:\
:lp=/dev/null:sh:\
:sd=/var/spool/lpd/lp:\
:if=/usr/local/sbin/smbprint:
You should read the documentation inside the smbprint
script for
more information on how to set this up.
You can also use smbclient
to submit a file directly to an
SMB printing service without involving lpd
. See the man page.
The ncpfs suite includes a utility called nprint
which provides
the same functionality as smbprint
but for NetWare. You can get
ncpfs from
ftp://linux01.gwdg.de/pub/ncpfs/. From the
LSM entry for version 0.16:
With ncpfs you can mount volumes of your netware server under Linux. You can also print to netware print queues and spool netware print queues to the Linux printing system. You need kernel 1.2.x or 1.3.54 and above. ncpfs does NOT work with any 1.3.x kernel below 1.3.54.
To make nprint
work via lpd, you write a little shell script to
print stdin on the NetWare printer, and install that as the if
for an lpd print queue. You'll get something like:
sub2|remote-NWprinter:\
:lp=/dev/null:sh:\
:sd=/var/spool/lpd/sub2:\
:if=/var/spool/lpd/nprint-script:
The nprint-script
might look approximately like:
#! /bin/sh
/usr/local/bin/nprint -S net -U name -P passwd -q printq-q -
The netatalk package includes something like nprint
and
smbclient
. Werner Eugster has documented the procedure for
printing to and from an Apple network far better than I ever will; see
his
web page.
Obscure caveat of the week: Netatalk does not work with SMC Etherpower PCI Card with a DEC tulip chip.
HPs and some other printers come with an ethernet interface which you can print to directly using lpd. You should follow the instructions that came with your printer or its network adaptor, but in general, such printers are "running" lpd, and provide one or more queues which you can print to. An HP, for example, might work with a printcap like:
lj-5|remote-hplj:\
:lp=/dev/null:sh:\
:sd=/var/spool/lpd/lj-5:\
:rm=printer.name.com:rp=raw:
In a large scale environment, especially a large environment where some printers do not support PostScript, it may be useful to establish a dedicated print server to which all machines print and on which all ghostscript jobs are run.
Some printers (and printer networking "black boxes") support only a cheesy little non-protocol involving plain TCP connections. Notable in this category are early-model JetDirect (including some JetDirectEx) cards. Basically, to print to the printer, you must open a TCP connection to the printer on a specified port (typically 9100) and stuff your print job into it. This can be implemented, among other ways, in Perl:
#!/usr/bin/perl
# Thanks to Dan McLaughlin for writing the original version of this
# script (And to Jim W. Jones for sitting next to Dan when writing me
# for help ;)
$fileName = @ARGV[0];
open(IN,"$fileName") || die "Can't open file $fileName";
$dpi300 = "\x1B*t300R";
$dosCr = "\x1B&k3G";
$ends = "\x0A";
$port = 9100 unless $port;
$them = "bach.sr.hp.com" unless $them;
$AF_INET = 2;
$SOCK_STREAM = 1;
$SIG{'INT'} = 'dokill';
$sockaddr = 'S n a4 x8';
chop($hostname = `hostname`);
($name,$aliases,$proto) = getprotobyname('tcp');
($name,$aliases,$port) = getservbyname($port,'tcp')
unless $port =~ /^\d+$/;;
($name,$aliases,$type,$len,$thisaddr) =
gethostbyname($hostname);
($name,$aliases,$type,$len,$thataddr) = gethostbyname($them);
$this = pack($sockaddr, $AF_INET, 0, $thisaddr);
$that = pack($sockaddr, $AF_INET, $port, $thataddr);
if (socket(S, $AF_INET, $SOCK_STREAM, $proto)) {
# print "socket ok\n";
}
else {
die $!;
}
# Give the socket an address.
if (bind(S, $this)) {
# print "bind ok\n";
}
else {
die $!;
}
# Call up the server.
if (connect(S,$that)) {
# print "connect ok\n";
}
else {
die $!;
}
# Set socket to be command buffered.
select(S); $| = 1; select(STDOUT);
# print S "@PJL ECHO Hi $hostname! $ends";
# print S "@PJL OPMSG DISPLAY=\"Job $whoami\" $ends";
# print S $dpi300;
# Avoid deadlock by forking.
if($child = fork) {
print S $dosCr;
print S $TimesNewR;
while (<IN>) {
print S;
}
sleep 3;
do dokill();
} else {
while(<S>) {
print;
}
}
sub dokill {
kill 9,$child if $child;
}
One oddity of lpd is that the if is not run for remote printers. If you find that you need to run an if, you can do so by setting up a double queue and requeueing the job. As an example, consider this printcap:
lj-5:remote-hplj:\
:lp=/dev/null:sh:\
:sd=/var/spool/lpd/lj-5:\
:if=/usr/lib/lpd/filter-lj-5:
lj-5-remote:lp=/dev/null:sh:rm=printer.name.com:\
:rp=raw:sd=/var/spool/lpd/lj-5-raw:
in light of this filter-lj-5
script:
#!/bin/sh
gs <options> -q -dSAFER -sOutputFile=- - | \
lpr -Plj-5-remote -U$5
The -U option to lpr only works if lpr is run as daemon, and it sets the submitter's name for the job in the resubmitted queue correctly. You should probably use a more robust method of getting the username, since in some cases it is not argument 5. See the man page for printcap.