Sharing a scanner over the network using SANE is fairly straightforward. Here's how I shared a scanner on a server (running Debian jessie) with a client (running Ubuntu trusty).
Install SANE
The packages you need on both the client and the server are:
You should check whether or your scanner is supported by the latest stable release or by the latest development version.
In my case, I needed to get a Canon LiDE 220 working so I had to grab the libsane 1.0.25+git20150528-1 package from Debian experimental.
Test the scanner locally
Once you have SANE installed, you can test it out locally to confirm that it detects your scanner:
scanimage -L
Note that you may need to be root for this to work. We'll fix that in the next section.
This should give you output similar to this:
device `genesys:libusb:001:006' is a Canon LiDE 220 flatbed scanner
If that doesn't work, make sure that the scanner is actually detected by the USB stack:
$ lsusb | grep Canon
Bus 001 Device 006: ID 04a9:190f Canon, Inc.
and that its USB ID shows up in the SANE backend it needs:
$ grep 190f /etc/sane.d/genesys.conf
usb 0x04a9 0x190f
To do a test scan, simply run:
scanimage > test.pnm
and then take a look at the (greyscale) image it produced (test.pnm
).
Letting normal users access the scanner
In order for users to be able to see the scanner, they will need to be in
the scanner
group:
adduser francois scanner
adduser saned scanner
with the second one being for remote users.
Next, you'll need to put this in /etc/udev/rules.d/55-libsane.rules
:
SUBSYSTEM=="usb", ATTRS{idVendor}=="04a9", MODE="0660", GROUP="scanner", ENV{libsane_matched}="yes"
and then restart udev:
systemctl restart udev.service
That 04a9
ID is the first part of what you saw in lsusb
, but you can
also see it in the output of sane-find-scanner
.
Finally, test the scanner as your normal user:
scanimage > test.pnm
to confirm that everything is working.
Configure the server
With the scanner working locally, it's time to expose it to network clients
by adding the client IP addresses to /etc/sane.d/saned.conf
:
## Access list
192.168.1.3
and then opening the appropriate ports on your firewall
(typically /etc/network/iptables
in Debian):
-A INPUT -s 192.168.1.3 -p tcp --dport 6566 -j ACCEPT
-A INPUT -s 192.168.1.3 -p udp -j ACCEPT
Then you need to ensure that the SANE server is running by setting the
following in /etc/default/saned
:
RUN=yes
if you're using the sysv init system, or by running this command:
systemctl enable saned.socket
if using systemd.
I actually had to reboot to make saned visible to systemd, so if you still run into these errors:
$ service saned start
Failed to start saned.service: Unit saned.service is masked.
you're probably just one reboot away from getting it to work.
One more time you may want to do on the server is to comment out the pixma
line in /etc/sane.d/dll.conf
if you don't need that backend. When it's
enabled (in use or not), it ends up sending broadcast packets on UDP ports
8612 and 8610 on the local network. If you want to avoid seeing this on
your endpoint firewalls, simply disable that backend.
Configure the client
On the client, all you need to do is add the following to
/etc/sane.d/net.conf
:
connect_timeout = 60
myserver
where myserver
is the hostname or IP address of the server running saned.
If you have a firewall runnning on the client, make sure you allow SANE traffic from the server:
-A INPUT -s 192.168.1.2 -p tcp --sport 6566 -j ACCEPT
Test the scanner remotely
With everything in place, you should be able to see the scanner from the client computer:
$ scanimage -L
device `net:myserver:genesys:libusb:001:006' is a Canon LiDE 220 flatbed scanner
and successfully perform a test scan using this command:
scanimage > test.pnm
Troubleshooting connection problems
If you see the following error in your logs (systemctl status saned.socket
):
saned.socket: Too many incoming connections (1), dropping connection.
then you can work around this bug in the systemd unit by overriding the systemd unit that comes with the package:
cp /lib/systemd/system/saned.socket /etc/systemd/system/saned.socket
then replace:
[Socket]
MaxConnections=1
with:
[Socket]
MaxConnections=64
before finally restarting the service:
systemctl daemon-reload
systemctl restart saned.socket
In order to automatically update my monitor setup and activate/deactivate my external monitor when plugging my ThinkPad into its dock, I found a way to hook into the ACPI events and run arbitrary scripts.
This was tested on a T420 with a ThinkPad Dock Series 3 as well as a T440p and a T460p with a ThinkPad Ultra Dock.
The only requirement is the ThinkPad kernel module. On most ThinkPads
it's the tp_smapi
module
(which you can find in the tp-smapi-dkms
package in Debian)
but on newer hardware, that interface is
gone and you can simply
use the thinkpad_acpi
module built into the
kernel. That's what generates the ibm/hotkey
events we will listen
for.
Hooking into the events
Create the following ACPI event scripts as suggested in this guide.
Firstly, /etc/acpi/events/thinkpad-dock
:
event=ibm/hotkey LEN0068:00 00000080 00006030
action=su francois -c "/home/francois/bin/external-monitor dock"
Secondly, /etc/acpi/events/thinkpad-undock
:
event=ibm/hotkey LEN0068:00 00000080 00004011
action=su francois -c "/home/francois/bin/external-monitor undock"
then restart acpid:
sudo systemctl restart acpid.service
Note that I'm not using the real "docking" event (ibm/hotkey LEN0068:00 00000080 00004010
)
because it seems to be triggered too early and the new displays aren't ready.
Finding the right events
To make sure the events are the right ones, lift them off of:
sudo acpi_listen
and ensure that your script is actually running by adding:
logger "ACPI event: $*"
at the begininng of it and then looking in /var/log/syslog
for lines
like:
logger: external-monitor undock
logger: external-monitor dock
If that doesn't work for some reason, try using an ACPI event script like this:
event=ibm/hotkey
action=logger %e
to see which event you should hook into.
Using xrandr inside an ACPI event script
Because the script will be running outside of your user session, the
xrandr
calls must explicitly set the display variable (-d
). This is what
I used:
#!/bin/sh
logger "ACPI event: $*"
xrandr -d :0.0 --output DP2 --auto
xrandr -d :0.0 --output eDP1 --auto
xrandr -d :0.0 --output DP2 --left-of eDP1