Nothing compares more to the sense of power UNIX sysadmin experiences when being able to print from a command line on its UNIX system :p
I kinda omitted this topic (printing) for quite a lot of time – when I was using FreeBSD in the corporate environment I still printed from Windows VM on a network printers. Then they forced me to use Windows anyway. At home my wife always had a printer configured (as she uses it more) and the other printer also had USB port – so you could just copy the PDF or JPG file to a USB pendrive – attach it the printer and hit print button for the selected files. No configuration needed.
I was also disappointed when I tried several years ago to configure USB printer on FreeBSD … and failed.
Recently I though that its about fucking time to dig into that topic and have at least one working printer on FreeBSD.
This guide will focus on using two printers with CUPS on FreeBSD:
- HP Color LaserJet 200 M251nw Printer (attached over TCP/IP network)
- Samsung Black/White ML-1915 Printer (local USB attached)
There will be two different prompt types used for the commands:
- starting with % for commands that can be executed as regular user or root
- starting with # for commands that must be executed as root user
The Table of Contents for this article is shown below.
- CUPS Packages and Service Configuration
- Network Printer – HP M251nw
- Try to Print Some Document
- USB Printer – Samsung ML-1915
- Choose Default Printer
- CUPS Printers Config
- Command Line Printing
- Last Chance Fancy Pants
CUPS Packages and Service Configuration
There are only three pkg(8) packages needed for my printers – these are:
# pkg install cups cups-filters splix
We will also need to add some lines to the /etc/devfs.rules file.
These lines are important for printing with CUPS:
add path 'lpt*' mode 0660 group cups add path 'ulpt*' mode 0660 group cups add path 'unlpt*' mode 0660 group cups
The rest of the config is just the rest of my desktop config and can be omitted for printing.
The entire /etc/devfs.rules file looks as follows.
% cat /etc/devfs.rules [desktop=10] add path 'lpt*' mode 0660 group cups add path 'ulpt*' mode 0660 group cups add path 'unlpt*' mode 0660 group cups add path 'acd*' mode 0660 group operator add path 'cd*' mode 0660 group operator add path 'da*' mode 0660 group operator add path 'pass*' mode 0660 group operator add path 'xpt*' mode 0660 group operator add path 'fd*' mode 0660 group operator add path 'md*' mode 0660 group operator add path 'uscanner*' mode 0660 group operator add path 'ugen*' mode 0660 group operator add path 'usb/*' mode 0660 group operator add path 'video*' mode 0660 group operator add path 'cuse*' mode 0660 group operator
We will also need to add devfs_system_ruleset=desktop to the /etc/rc.conf file.
% grep desktop /etc/rc.conf devfs_system_ruleset=desktop
Now we need to restart the devfs daemon to read new config.
# service devfs restart
We can also make sure that devfs(8) know our ruleset config.
# devfs rule -s 10 show | column -t 100 path acd* group operator mode 660 200 path cd* group operator mode 660 300 path da* group operator mode 660 400 path pass* group operator mode 660 500 path xpt* group operator mode 660 600 path fd* group operator mode 660 700 path md* group operator mode 660 800 path uscanner* group operator mode 660 900 path lpt* group cups mode 660 1000 path ulpt* group cups mode 660 1100 path unlpt* group cups mode 660 1200 path ugen* group operator mode 660 1300 path usb/* group operator mode 660 1400 path video* group operator mode 660 1500 path cuse* group operator mode 660
The column(1) is not needed here – I used it only to format the output.
What amaze me to this day that column(1) command is still not available on such enterprise (and overpriced also) IBM AIX system 🙂
Here are the contents of fresh CUPS installation at /usr/local/etc/cups dir.
# tree -F --dirsfirst /usr/local/etc/cups /usr/local/etc/cups ├── ppd/ ├── ssl/ ├── cups-files.conf ├── cups-files.conf.sample ├── cupsd.conf ├── cupsd.conf.sample ├── snmp.conf └── snmp.conf.sample 3 directories, 6 files
You will need to add cupsd_enable=YES to the /etc/rc.conf file.
% grep cups /etc/rc.conf cupsd_enable=YES
Make sure that cupsd service is started and running.
# service cupsd start Starting cupsd. # service cupsd status cupsd is running as pid 44515. # sockstat -l4 | grep -e ADDRESS -e 631 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS root cupsd 44515 6 tcp4 127.0.0.1:631 *:*
Just in case – here are the groups in which my vermaden user is:
% id | tr ',' '\n' uid=1000(vermaden) gid=1000(vermaden) groups=1000(vermaden) 0(wheel) 5(operator) 44(video) 69(network) 145(webcamd) 920(vboxusers)
It was not needed to add my vermaden user to the cups group to print – but feel free to also test that if you face any problems.
Network Printer – HP M251nw
First I will go with the TCP/IP attached network printer – HP M251nw.
Before doing any steps or configuration on FreeBSD part we first need to connect that printer to the TCP/IP network. As the HP M251nw printer has WiFi – I decided to connect it to my wireless WiFi router instead of using RJ45 cable. I will not document that part as HP already provides decent guide on how to achieve that – https://youtu.be/jLDzQBAtKyQ – on YouTube service.
In my case I used the 10.0.0.9 IP address and I configured my WiFi router to always attach that MAC address to that IP address.
Next step is to open http://localhost:631/ page in your browser. You will see default CUPS web interface.
Hit the Administration tab on the top. Then click the Add Printer button in the middle of the page – you will be asked for username and password – use your username and your password here.
The HP M251nw network attached browser has already been detected by CUPS. Select it and click Continue button.
CUPS will suggest some long names and description as showed below.
… but we will use simpler and shorter name instead.
Next we need to choose which driver to use.
We will not find a HP M251nw driver on the CUPS list but there are two drivers that will work here:
- HP LaserJet Series PCL 6 CUPS (en)
- HP Color LaserJet Series PCL 6 CUPS (en)
As HP M251nw is color printer we will choose HP Color LaserJet Series PCL 6 CUPS here.
After a moment we will see a message that HP M251nw printer has been successfully added to CUPS.
You can notice that new PPD file appeared at CUPS dir named exactly like the printer name.
% ls -l /usr/local/etc/cups/ppd total 9K -rw-r----- 1 root cups 9721 2023-02-06 11:24 HP-M251nw.ppd -rw-r----- 1 root cups 9736 2023-02-06 11:23 HP-M251nw.ppd.O
This is how our HP M251nw printer status page looks like.
We should now setup the default printing options. From the Administration drop down menu select Set Default Options option. The only things I selected/set that are different from the CUPS defaults are A4 paper size and 1200 DPI resolution.
Try to Print Some Document
I will now use Atril PDF viewer to test how the printing on the HP M251nw works – I used a small one page PDF file with one of my old guides – the ZFS Madness one from 2014. From the File menu select Print… option – or just hit [CTRL]+[P] shortcut.
Then select HP-M251nw printer from the list and hit the Print button below.
After some noises and time (not much later) the printer dropped a printed page. Seems to work properly.
Lets now add USB printer.
USB Printer – Samsung ML-1915
To get needed PPD driver for the Samsung ML-1915 printer we installed the print/splix package.
Here is the exact driver we will use.
% pkg info -l splix | grep 1915 /usr/local/share/cups/model/samsung/ml1915.ppd
Before attaching the Samsung ML-1915 printer to your computer you may check what devices devd(8) will create.
First power on the Samsung ML-1915 printer.
Then attach the USB cable from the printer to your FreeBSD box (assuming that printer has AC power and is powered on).
You should see something similar from devd(8) daemon.
# nc -U /var/run/devd.pipe !system=DEVFS subsystem=CDEV type=CREATE cdev=usb/0.3.0 !system=DEVFS subsystem=CDEV type=CREATE cdev=ugen0.3 !system=DEVFS subsystem=CDEV type=CREATE cdev=usb/0.3.2 !system=DEVFS subsystem=CDEV type=CREATE cdev=usb/0.3.3 !system=USB subsystem=DEVICE type=ATTACH ugen=ugen0.3 cdev=ugen0.3 vendor=0x04e8 product=0x3297 devclass=0x00 devsubclass=0x00 sernum="Z2L9BACSC00641K." release=0x0100 mode=host port=2 parent=ugen0.2 !system=USB subsystem=INTERFACE type=ATTACH ugen=ugen0.3 cdev=ugen0.3 vendor=0x04e8 product=0x3297 devclass=0x00 devsubclass=0x00 sernum="Z2L9BACSC00641K." release=0x0100 mode=host interface=0 endpoints=2 intclass=0x07 intsubclass=0x01 intprotocol=0x02 !system=DEVFS subsystem=CDEV type=CREATE cdev=ulpt0 !system=DEVFS subsystem=CDEV type=CREATE cdev=unlpt0 +ulpt0 at bus=0 hubaddr=2 port=2 devaddr=3 interface=0 ugen=ugen0.3 vendor=0x04e8 product=0x3297 devclass=0x00 devsubclass=0x00 devproto=0x00 sernum="Z2L9BACSC00641K." release=0x0100 mode=host intclass=0x07 intsubclass=0x01 intprotocol=0x02 on uhub4
These are the created devices.
% ls -ltra /dev | tail -3 lrw-rw---- 1 root operator 9 2023-02-06 11:38 ugen0.3 -> usb/0.3.0 crw-rw---- 1 root cups 2, 113 2023-02-06 11:38 ulpt0 crw-rw---- 1 root cups 2, 114 2023-02-06 11:38 unlpt0
They are created with proper cups group.
Now we will go to the CUPS web page at http://localhost:631/ again to add the Samsung ML-1915 printer.
Go again to the Administration tab and click Add Printer button.
The Samsung ML-1915 should be already detected as local printer as shown below.
Select it and hit Continue button.
As earlier we will use shorter more reasonable name.
We will then select Samsung ML-1915, 2.0.0 (en, en) driver for this printer.
… and Samsung ML-1915 black/white printer has been added.
Same as earlier the PPD file is copied to the /usr/local/etc/cups/ppd CUPS dir.
% ls -l /usr/local/etc/cups/ppd total 14K -rw-r----- 1 root cups 9721 2023-02-06 11:24 HP-M251nw.ppd -rw-r----- 1 root cups 9736 2023-02-06 11:23 HP-M251nw.ppd.O -rw-r----- 1 root cups 12391 2023-02-06 11:58 Samsung-ML-1915.ppd
You now have two printers configured in CUPS.
Choose Default Printer
I will now choose the HP M251nw printer as the default for two reasons. First – its always available as its attached over WiFi. Second – its more powerful and provides color at the same time.
To do that I went to the Printers and clicked the HP M251nw printer.
Next from the Administration drop down menu I have chosen Set As Server Default option.
From now on – if not explicitly specified – all the print jobs will land on the HP M251nw printer.
CUPS Printers Config
After our actions CUPS stored two printers configuration in its /usr/local/etc/cups/printers.conf config file.
# cat /usr/local/etc/cups/printers.conf # Printer configuration file for CUPS v2.4.2 # Written by cupsd # DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING NextPrinterId 3 <DefaultPrinter HP-M251nw> PrinterId 1 UUID urn:uuid:b760d323-5f46-36cd-4ca0-d9015c9fb7ca Info Location MakeModel HP Color LaserJet Series PCL 6 CUPS DeviceURI socket://10.0.0.9 State Idle StateTime 1675683146 ConfigTime 1675679066 Type 8400972 Accepting Yes Shared No JobSheets none none QuotaPeriod 0 PageLimit 0 KLimit 0 OpPolicy default ErrorPolicy stop-printer Attribute marker-colors \#000000,#00FFFF,#FF00FF,#FFFF00 Attribute marker-levels 99,98,98,99 Attribute marker-names Black Cartridge HP CF210X,Cyan Cartridge HP CF211A,Magenta Cartridge HP CF213A,Yellow Cartridge HP CF212A Attribute marker-types toner,toner,toner,toner Attribute marker-change-time 1675683146 </DefaultPrinter> <Printer Samsung-ML-1915> PrinterId 2 UUID urn:uuid:4434851b-5516-3b73-702a-286dabf630b0 Info Location MakeModel Samsung ML-1915, 2.0.0 DeviceURI usb://Samsung/ML-191x%20252x%20Series?serial=Z2L9BACSC00641K. State Idle StateTime 1675681099 ConfigTime 1675681099 Type 12372 Accepting Yes Shared No JobSheets none none QuotaPeriod 0 PageLimit 0 KLimit 0 OpPolicy default ErrorPolicy stop-printer </Printer>
Command Line Printing
Besides being able to print from graphical applications that support CUPS we can also print directly from the command line if needed.
Use lpstat(1) command to see all available printers – including the default one.
% lpstat -p -d printer HP-M251nw is idle. enabled since Mon Feb 6 12:02:39 2023 printer Samsung-ML-1915 is idle. enabled since Mon Feb 6 11:58:19 2023 system default destination: HP-M251nw
You can check more information about the default printer with lpoptions(1) command.
% lpoptions -l PageSize/Media Size: Letter Legal Executive Tabloid A3 *A4 A5 B5 EnvISOB5 Env10 EnvC5 EnvDL EnvMonarch InputSlot/Media Source: *Default Auto MultiPurpose Upper Lower LargeCapacity Manual Envelope ColorModel/Output Mode: *RGB Gray Resolution/Output Resolution: 150dpi 300dpi 600dpi *1200dpi Duplex/Double-Sided Printing: *None DuplexNoTumble DuplexTumble OptionDuplex/Duplexer: True *False
… or even more details and information when executed without arguments.
I have used tr(1) tool to make the output more readable as by default all this information is separated only by spaces.
% lpoptions | tr ' ' '\n' copies=1 device-uri=socket://10.0.0.9 finishings=3 job-cancel-after=10800 job-hold-until=no-hold job-priority=50 job-sheets=none,none marker-change-time=1675681359 marker-colors=#000000,#00FFFF,#FF00FF,#FFFF00 marker-levels=99,98,98,99 marker-names='Black\ Cartridge\ HP\ CF210X,Cyan\ Cartridge\ HP\ CF211A,Magenta\ Cartridge\ HP\ CF213A,Yellow\ Cartridge\ HP\ CF212A' marker-types=toner,toner,toner,toner number-up=1 print-color-mode=color printer-commands=AutoConfigure,Clean,PrintSelfTestPage printer-info printer-is-accepting-jobs=true printer-is-shared=false printer-is-temporary=false printer-location printer-make-and-model='HP Color LaserJet Series PCL 6 CUPS' printer-state=3 printer-state-change-time=1675681359 printer-state-reasons=none printer-type=10629196 printer-uri-supported=ipp://localhost/printers/HP-M251nw
We will now print the same PDF document using command line with lp(1) command.
% lp ZFS-Madness-2014.pdf request id is HP-M251nw-02 (1 file(s))
Believe me or not – that PDF document got printed exactly the same as when invoked from Atril PDF browser.
Last Chance Fancy Pants
There is of course a chance that your printer will not be detected – or it will not print – or the driver will not attach to it properly … life happens.
What then? Fuck it. There is even more fun way to print … even without any drivers or configuration … directly with nc(1) command 🙂
First lets check of your printer listens on 9100 port – this is called HP JetDirect.
% grep 9100 /etc/services jetdirect 9100/tcp #HP JetDirect card pdl-datastream 9100/tcp #Printer PDL Data Stream pdl-datastream 9100/udp #Printer PDL Data Stream % nmap -A 10.0.0.9 Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-06 23:41 CET Nmap scan report for 10.0.0.9 Host is up (0.0072s latency). Not shown: 988 closed tcp ports (conn-refused) PORT STATE SERVICE VERSION 21/tcp open ftp oftpd | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_. |_ftp-bounce: bounce working! | ftp-syst: |_ SYST: . 23/tcp open telnet HP LaserJet printer telnetd (busy) 80/tcp open soap gSOAP 2.7 | http-server-header: | Virata-EmWeb/R6_2_1 |_ gSOAP/2.7 81/tcp open tcpwrapped 82/tcp open tcpwrapped 83/tcp open tcpwrapped 443/tcp open ssl/tcpwrapped | ssl-cert: Subject: commonName=NPI04344D/organizationName=Hewlett-Packard Co. | Not valid before: 2012-09-01T00:00:00 |_Not valid after: 2022-09-01T00:00:00 |_http-server-header: gSOAP/2.7 |_ssl-date: TLS randomness does not represent time 515/tcp open printer 631/tcp open soap gSOAP 2.7 | http-server-header: | Virata-EmWeb/R6_2_1 |_ gSOAP/2.7 5222/tcp open tcpwrapped | xmpp-info: | STARTTLS Failed | info: | features: | auth_mechanisms: | xmpp: | unknown: | compression_methods: | errors: | (timeout) |_ capabilities: 8080/tcp open soap gSOAP 2.7 | http-server-header: | Virata-EmWeb/R6_2_1 |_ gSOAP/2.7 9100/tcp open jetdirect? Service Info: OS: Unix; Device: printer Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 20.37 seconds
Lets try to connect to it with nc(1) tool.
% nc -v 10.9 9100 Connection to 10.9 9100 port [tcp/jetdirect] succeeded!
… and yes you do not have to always type that whole 10.0.0.9 address as the middle zeroes can be omitted and 10.9 will be interpretted as 10.0.0.9 address.
Something basic for a start – a plain text print.
% lsblk | nc 10.9 9100
In a moment you should have the output of lsblk(8) command printed on a page.
Lets try something more fancy like a PDF file then.
% nc 10.9 9100 < ZFS-Madness-2014.pdf
Yep. Printed. No CUPS configuration needed here.
Maybe I should start the article with that instead 🙂
Not sure what I can add here as I am definitely not printing expert.
Hope these instructions will help you to setup your printer on FreeBSD (or any other CUPS supported) system.