I have a fairly old but great laser multifunction Canon MF4150 that never really supported scanning from anything but windows and whose MacOS printing drivers lag behind OS releases by a large margin causing frustration after each new release. Still, it has been over 10 years and it works great and I’m absolutely happy with its performance.

This post is a walk-through of configuring an instance of CentOS to make the device network-accessible with the following features:

  • Network printing
  • AirPrint support
  • Network scanning
  • macOS, iOS, and Windows clients

There are a bunch of tutorials online on achieving similar goals but I had to tweak them to make it work — I was stumbling on issues all the time.

What to follow

Choice of HW and OS

The HW choice was rather trivial — I have Synology DSM running in the printer’s vicinity with tons of RAM and 4 core CPU that is idle most of the time. So running something in the VM there wasn’t unreasonable.

The OS on the other hand is a bit trickier. I have attempted to use Alpine Linux as a starting point but I could not make Canon UFR LT II drivers work there. So I went backward and picked OS that is lightweight and also is compatible with the printer drivers. CentOS seemed fitting to I went with it.

Assumptions:

VM instance hostname: printers
VM instance address:  10.0.17.242
Subnet:               10.0.17.0/24

Configuring CentOS instance

Download Minimal ISO. Mount in the VM and install with default settings:

  • Configure Hostname to something meaningful. I called it Printers
  • Select default security profile
  • Create user admin

Setup SSH, login, set the timezone, and update the software. It also makes sense to install guest agents to ensure consistency of the snapshots later:

timedatectl set-timezone America/Los_Angeles
yum -y upgrade
yum -y install qemu-guest-agent

You can get the list of timezones by running timedatectl list-timezones.

We will configure the firewall properly later to allow Avahi, CUPS, and SANE to expose services, but at this point, since this is a machine in a trusted LAN we just disable firewall and revert to simple IPsec config with persistence:

systemctl stop firewalld
systemctl disable firewalld
yum install -y iptables-services
systemctl enable iptables
iptables -I INPUT -j ACCEPT
service iptables save

Printing

We will be printing via CUPS and use Avahi for AirPrint. So, install:

yum install -y cups ghostscript avahi python-cups python-lxml

Python bits are dependencies for airprint-generate scripts. cups is self-explanatory and ghostscript is a dependency for Canon printer drivers.

Configuring CUPS

Edit /etc/cups/cups-files.conf and add admin to SystemGroup:

# Administrator user group used to match @SYSTEM in cupsd.conf policy rules...
SystemGroup sys root admin

Edit /etc/cups/cupsd.conf to add Listen interface other than localhost and allow connections from your LAN under <Location /> and <Location /admin>. For example:

# Listen on LAN interface
Listen 10.0.17.242:631

# Or Listen on all interfaces:
Port 631

# Restrict access to the server...
<Location />
  Allow 10.0.17.0/24
  Allow localhost
  Allow 127.0.0.1
  Require valid-user
  Satisfy any
  Order deny,allow
  Deny from all
</Location>

# Restrict access to the admin pages...
<Location /admin>
  Allow 10.0.17.0/24
  Allow localhost
  Allow 127.0.0.1
  Require user @SYSTEM
  Order deny,allow
  Deny from all
</Location>

Scroll down to policies and review permissions for users to manage printers and jobs if needed. For more information see man cupsd.conf. The defaults are appropriate in most cases.

After changing configuration restart CUPS via systemctl restart cups.

Installing Canon UFR II Drivers

From Canon MF4150 Driver Page download Linux 64 drivers and unpack them.

There are two ways to install: if you feel brave, use their install script:

cd linux-UFRII-drv-v350-usen
sudo ./install.sh

It worked fine on CentOS but I had issues with it on other OSes before. Alternatively,

cd linux-UFRII-drv-v350-usen/64-bit_Driver/RPM/
yum -y --nogpgcheck localinstall cndrvcups-common-3.90-1.x86_64.rpm
yum -y --nogpgcheck localinstall cndrvcups-ufr2-us-3.50-1.x86_64.rpm

This will install the rpms and take care of dependencies.

Configuring and sharing printer in CUPS

Now connect the printer to the machine, go to printers:631 and add that printer in CUPS.

Configuring AirPrint services with avahi

To generate Avahi .service manifests we’ll take advantage of airprint-generate script by jpawlowski:

yum -y install wget pygobject2 python-cups python-lxml 
wget -O /usr/share/cups/mime/apple.convs --no-check-certificate https://raw.github.com/jpawlowski/airprint-generate/master/apple.convs
wget -O /usr/local/bin/airprint-generate.py --no-check-certificate https://raw.github.com/jpawlowski/airprint-generate/master/airprint-generate.py
wget -O /usr/local/bin/avahisearch.py --no-check-certificate https://raw.github.com/jpawlowski/airprint-generate/master/avahisearch.py
chmod 755 /usr/local/bin/airprint-generate.py /usr/local/bin/avahisearch.py

# Remove ugly Sec.Airprint prefix from the broadcast
sed -ibak 's/Sec\.AirPrint //' /usr/local/bin/airprint-generate.py

rm -rf /etc/avahi/services && /usr/local/bin/airprint-generate.py --cups -d /etc/avahi/services
systemctl restart avahi-daemon
systemctl restart cups

The last three lines should be re-run every time you add/remove printers and/or change the configuration in any significant way.

That’s it

Now the printer should show up in the Print menu on iOS devices. If this is not the case — verify that iptables allow all, that locations in CUPS are set up to allow your LAN and that the user’s privileges are appropriately assigned.

Scanning with SANE

For scanning, we will use an awesome SANE which worked great even on Alpine Linux. We’ll need to build it from the latest source though — the included distribution in CentOS had some weird issues when the scanner would be detectable but not usable.

Building and Installing sane-backends

yum -y install git gcc gcc-c++ make libusb-devel avahi-devel libpng-devel systemd-devel
cd
git clone https://alioth.debian.org/anonscm/git/sane/sane-backends.git
cd sane-backends
./configure && make && make install

Test:

root@printers ~]# scanimage -L
device 'pixma:04A926A3_SDF680284650B' is a CANON Canon imageCLASS MF4150 multi-function peripheral

and the real test:

[root@printers ~]# scanimage -vvvv --source Flatbed --resolution=300dpi --mode Color  --format png  > /tmp/test.png
scanimage: value for --resolution is: 300
scanimage: scanning image of size 2560x3508 pixels at 24 bits/pixel
scanimage: acquiring RGB frame
scanimage: min/max graylevel value = 5/255
scanimage: read 26941440 bytes in total
Closing device
Calling sane_exit
scanimage: finished

To improve performance comment out everything except required drivers (pixma in this case) from /usr/local/etc/sane.d/dll.conf

Configuring network access to the scanner

Note how we compiled the sane-backends with systemd support? Now, this becomes handy. Read man saned and create the support files for systemd as described there:

/etc/systemd/system/saned.socket

[Unit]
Description=saned incoming socket

[Socket]
ListenStream=6566
Accept=yes
MaxConnections=1

[Install]
WantedBy=sockets.target

/etc/systemd/system/[email protected]

[Unit]
Description=Scanner Service
Requires=saned.socket

[Service]
ExecStart=/usr/local/sbin/saned
User=saned
Group=saned
StandardInput=null
StandardOutput=syslog
StandardError=syslog
Environment=SANE_CONFIG_DIR=/usr/local/etc/sane.d 
#Environment=SANE_DEBUG_DLL=3 SANE_DEBUG_NET=3

[Install]
Also=saned.socket

If something goes not as planned feel free to adjust verbosity up to 128.

Set owner to root and permissions to 644 on those files to avoid warning about the execute flag.

chown root:root /etc/systemd/system/[email protected] /etc/systemd/system/saned.socket
chmod 644 /etc/systemd/system/[email protected] /etc/systemd/system/saned.socket

Also actually create the user saned:

groupadd saned
useradd saned -g saned

/etc/udev/rules.d/65-sane-backends.rules

Allow user saned access to the device by brutally giving access to this device to everyone:

ATTRS{idVendor}=="04a9", ATTRS{idProduct}=="26a3", MODE="0666"

To figure out vendor id and usb id run lsusb. If you don’t have lsusb — install usbutils. To figure out that it is shipped with usbutils run yum whatprovides lsusb.

The proper way to achieve this perhaps would be to add a special group e.g mfaccess, add saned and cups users to it and assign permissions to devices that are matched with libsane to that group to avoid giving everyone write access — something like this:

ENV{libsane_matched}=="yes", GROUP="mfaccess", MODE="0660"

However I have not done this yet.

Useful commands for debugging udev configuration:

  • udevadm trigger --action=add to trigger an action add
  • udevadm control --reload-rules to reload rules

/usr/local/etc/sane.d/saned.conf

10.0.17.0/24
printers
localhost

Start the services

systemctl enable saned.socket
systemctl start saned.socket

Testing network access to the scanner

On the client edit /opt/local/etc/sane.d/net.conf and add server address 10.0.17.242 or printers. Then just test:

scanimage -L
device 'net:10.0.17.242:pixma:04A926A3_SDF680284650B' is a CANON Canon imageCLASS MF4150 multi-function peripheral

References and Credits