I got this interesting question on my blog post ‘Setting up an Epson receipt printer‘:
Wondering if you had an idea on how to map a linux device to the netcat command so that I could “convert” the printer to be a local one? -Marco
I have previously written about the opposite use case: How to use a Raspberry Pi as a print server, where I used netcat to pass data to a local USB printer.
The setup
In this setup, I will assume that you have a working Ethernet printer, which accepts raw data on port 9100.
The aim is to make this appear as a file, so that you can print to it as if it were a local USB printer:
Before you begin
Make sure you have netcat. It’s not used for the real setup, but you will need it for testing. There are a few versions of this tool around, the one used here is:
apt-get install netcat
Verify that your printer accepts text on port 9100 via netcat:
echo "Hello world" | nc -q 1 192.168.x.y 9100
Set up a fake printer on localhost, and leave it running:
nc -klp 9100
Now, test that your fake printer shows output when you send it:
echo "Hello world" | nc -q 1 127.0.0.1 9100
Why I’m not using..
/dev/tcp
If you search for how to redirect data to a TCP socket, a common suggestion is:
echo "Hello world" > /dev/tcp/127.0.0.1/9100
This is a bash built-in, so unless your use case involves printing from bash, read on.
CUPS
CUPS does not expose the printer as a file, which is the aim here. It also takes a few seconds to print, so we get much faster results if we remove CUPS from the loop and speak to the printer directly.
Use socat to move data
Socat is capable of making a FIFO file (‘pipe’), and writing this out over the network.
apt-get install socat
Redirect /tmp/my-printer to localhost:9100:
socat PIPE:/tmp/my-printer TCP:localhost:9100
Next, test that you can see the printer file and write to it. This line should appear on your local netcat “fake printer”:
echo "Hello world" > /tmp/my-printer
This file is a “pipe”, so it behaves very similarly to a character device.
If the connection is dropped, socat will exit, and the file will be deleted.
There are some big problems though:
- If your printer is offline, and you try to print, you will create a regular file at “/tmp/my-printer”, breaking it.
- This is not a self-restarting service, nor does it start on boot
- Only the user who runs the command can print
Setting this up as a proper service
The usblp driver allows anybody from the lp group to print, so we will try to do something similar, and get a group-writable pipe:
$ ls /dev/usb/lp0 -Ahl
crw-rw---- 1 root lp 180, 0 Jun 12 12:51 /dev/usb/lp0
We don’t need to run socat as root, so make a new user called fileprint who is in the lp group.
useradd --groups lp fileprint
Add yourself to the lp group as well:
usermod -a -G lp mike
Next, write this systemd service file to /etc/systemd/system/fileprint.service
[Unit]
Description=fileprint
After=network.target
[Service]
User=fileprint
Group=lp
ExecStartPre=/usr/bin/mkfifo -m '0664' /var/run/fileprint/printer
ExecStart=/usr/bin/socat PIPE:/var/run/fileprint/printer TCP:127.0.0.1:9100
Restart=always
RuntimeDirectory=fileprint
What’s going on here?
- Runs as user fileprint and group lp
- Manages a runtime directory at /var/run/fileprint
- Creates a pipe that is group-writeable at /var/run/fileprint/printer
- Forwards traffic to 127.0.0.1:9100
- Re-starts automatically
Load and start the service:
systemctl daemon-reload
systemctl start fileprint
systemctl status fileprint
Test out printing to your local netcat “fake printer”:
echo "Test" > /var/run/fileprint/printer
Note that if you are not in the lp group (check with groups command), you should expect a permission error here — just the same as usblp.
Next, replace 127.0.0.1 with your real printer IP, reload systemd, and restart the service:
nano /etc/systemd/system/fileprint.service
systemctl daemon-reload
systemctl restart fileprint
systemctl status fileprint
Test it again, and your actual printer should print a line of text this time:
echo "Test" > /var/run/fileprint/printer
Finally, enable on boot:
systemctl enable fileprint
When the printer is unplugged, the /var/run/fileprint directory will vanish, so that you can see the missing printer as a “File Not Found” error — just the same as usblp.
Drawbacks
This will hold a connection open at all times, which (depending on your printer) may prevent other computers from using it.