Solving the CUPS “hpcups failed” error

I thought when I took my new job that my days of dealing with printer headaches were over.  Alas, it was not to be.  A few weeks ago, I needed to print out a form for work.  I tried to print to the shared laser printer down the hall.  Nothing.  So I tried the color printer. Nothing again.  I was perplexed because both printers had worked previously, so being a moderately competent sysadmin, I looked in the CUPS logs.  I saw a line in error_log that read printer-state-message="/usr/lib/cups/filter/hpcups failed". That seemed like it was the problem, so I tried to find a solution and couldn’t come up with anything immediately.

Since a quick fix didn’t seem to be on the horizon, I decided that I had better things to do with my time and I just used my laptop to print.  That worked, so I forgot about the printing issue.  Shortly thereafter, the group that maintains the printers added the ones on our floor to their CUPS server.  I stopped CUPS on my desktop and switched to their server and printing worked again, thus I had even less incentive to track down the problem.

Fast forward to yesterday afternoon when my wife tried to print a handbill for an event she is organizing in a few weeks.  Since my desktop at home is a x86_64 Fedora 12 system, too, it didn’t surprise me too much when she told me she couldn’t print.  Sure, enough, when I checked the logs, I saw the same error.  I tried all of the regular stalling tactics: restarting CUPS, power cycling the printer, just removing the job and trying again.  Nothing worked.

The first site I found was an Ubuntu bug report which seemed to suggest maybe I should update the printer’s firmware.  That seemed like a really unappealing prospect to me, but as I scrolled down I saw comment #8.  This suggested that maybe I was looking in the wrong place for my answer.  A few lines above the hpcups line, there was an error message that read prnt/hpcups/HPCupsFilter.cpp 361: DEBUG: Bad PPD - hpPrinterLanguage not found.

A search for this brought me to a page about the latest version of hplip. Apparently, the new version required updated PPD files, which are the files that describe the printer to the print server.  In this case, updating the PPD file was simple, and didn’t involve having to find it on HP’s or a third-party website.  All I had to do was use the CUPS web interface and modify the printer, keeping everything the same except selecting the hpcups 3.10.2 driver instead of the 3.9.x that it had been using.  As soon as I made that change, printing worked exactly as expected.

The lesson here, besides the ever-present “printing is evil” is that the error message you think is the clue might not always be.  When you get stuck trying to figure a problem out, look around for other clues.  Tunnel vision only works if you’re on the right track to begin with.

Setting up a print server

Matt made a comment on an earlier post asking if I knew of any good how-to articles about setting up a print server.  I did a little bit of looking, and it seems like most of the good articles assume that the server is already up and running.  I will not pretend this is a good article, and it certainly isn’t comprehensive, but it is what it is.  Basically, I’ll just run down the cupsd.conf file on my print server and point out differences and why I made them different.

Now some people might ask “well, Ben, what about Windows-based print servers?”  That’s a valid question.  It has been my experience that Windows print servers work very well with Windows clients.  In a heterogeneous environment, though, Windows servers don’t perform well.  That’s where the Common Unix Print System (CUPS) comes in.  CUPS runs on pretty much any Unix or Linux system (including Mac OS X, which is part of the reason Apple purchased CUPS in 2007). Most, if not all, Linux distributions ship with CUPS, and the source code is available if you prefer to roll your own.  We are going to assume you’ve already done a yum install cups (or apt-get install cupsys or ./configure;make;make install or whatever).

I’m assuming your CUPS configuration files live in /etc/cups, which is the default on Fedora/Red Hat/CentOS systems.  If it lives somewhere else, adjust accordingly.  There are several files in the configuration directory, but if you’re setting up a server, the one you’re immediately interested in is cupsd.conf.  This is the configuration file for the CUPS server daemon.

There are a lot of options that you can specify, I don’t know what all of them are, but that’s what the documentation is for.  What I do know is that lines that start with ‘#’ are comments, and are there to help you remember later what you did when you set your server up.  The options are mostly in the format “option_name option_value” and some options are grouped into sections with <label> </label> tags, similar to HTML and XML.  So let’s look at what I did, keeping in mind that my options are set for my server, and your needs may be different.

SystemGroup sys root eascomp

The “SystemGroup” option defines who can administer the print server.  In this case, the ‘eascomp’ group contains all of my department’s IT staff.  This allows anyone on the team to do routine tasks on the server (e.g. deleting jobs, restarting a queue, etc) without having to explicitly define each command in /etc/sudoers.  Note that this does not give the specified group(s) permission to edit the config files by hand, that is still controlled by regular Unix file permissions.

#Listen localhost:631
Port 80
Port 631

By default, CUPS is set up to only listen to requests coming from the machine it is running on.  That doesn’t do much good when you’re trying to run a print service for multiple machines, so I commented the line out.  I then added the ‘Port 631’ line to tell CUPS to listen for requests from any host on port 631 (we’ll make the security a bit better later).  631 is the standard port for the Internet Printing Protocol (IPP).  However, Windows does not seem to understand IPP by default, so when you add a CUPS printer to Windows, you have to specify the port (e.g. http://print.my.employer.edu:631).  In order to remove this pain, I also told CUPS to listen on port 80, the standard http port.  If you plan to run a web server on the same machine, you can’t listen on port 80.

<Location />
 Order deny,allow
  Allow  *.my.employer.edu
  Allow 10.79.127.*
  Allow 10.79.168.*
  Allow 10.79.169.*
  Deny all
</Location>

This section controls where clients can access the server from.  In most of the real-world cases I can think of, you’ll want to block all hosts by default and then allow only the specific hosts or networks that need access.  Only bad things can happen if you run a print server for the whole world to use.  The order of allow and deny after the Order command is very important.  Whichever comes first is the default, so you’ll want to make sure you use “deny,allow” unless you’ve got a completely self-contained network.  In my example above, I deny everywhere and then explicitly add the IP addresses and DNS entries for my department.  (It isn’t strictly necessary to do it both ways, one or the other works fine in most cases.  It just so happens that I’ve got a few IPs that don’t have DNS records and I’m too lazy to fix that).

That’s all there is to it.  You’ve now got your own cupsd.conf and you’re ready to start the server (/etc/init.d/cups start, unless you’re Nathan, in which case it’s “go CUPS go!”).  It’s also worth noting that you can do some CUPS configuration through a web browser (http://localhost:631) or through a GUI program that your distro provides.  I prefer to do it by hand, because that’s easier to do remotely and it gives you more control over what you’re actually changing.

So now that you’ve got your server set up, it’s time to add printers.  At this point, I think the rest of the Internet does a good job of explaining things, so I won’t duplicate the effort.  Don’t worry though, I seem to be on a CUPS kick lately, and I doubt this post will be the last on the subject.  I do want to note, however, that just because you have CUPS itself running, that doesn’t mean you’re done.  I strongly suggest modifying your firewall rules to allow only the correct hosts access to the port(s) you’re running CUPS on (you’re doing default deny on your firewall, right?).

So that’s why the printer won’t print…

A few weeks ago, I thought I’d try to fix some printing problems but upgrading the version of CUPS that was installed on our print server.  Of course, since the print server runs Solaris 9, this wasn’t the easiest of tasks.  My first stop was to look on Sun Freeware for a package.  Unfortunately, CUPS was not among the hundreds of packages available.  So I Googled around a bit and found a package on Sun’s website.  Too bad it was the same version I already have.  Finally, I surrendered and grabbed the source.  Which refused to compile.  Granted, a smarter admin could probably have compiled it no problem, but I couldn’t get it working.  We were now about 2 business days from the outage I had scheduled.

Then I had one of my more brilliant thoughts.  Why not just set up a VM and run a Linux CUPS server?  So I did.  Not to brag or anything, but I had it up and running and the old server is forwarding port 631 to the new so the change is mostly invisible to the users.  Go me.  But there were a few problems.

The morning of the switch, one of the secretaries reported that the main office printers weren’t working.  One of them had a funky job that killed the queue, so I cleared it and restarted the queue.  But the other one looked fine.  My colleague went downstairs and checked it out, but couldn’t find any problem with the printer itself.  Around lunch, I was looking through /etc/cups/printers.conf and I came to a sudden realization.

HP JetDirect cards listen on port 9100 for incoming print jobs.  When setting up that particular printer, I forgot to include the port number.  That would explain why the socket process was still running and the load was so high.  Once that error was fixed, I was back in business.

There was still one more problem to be worked out.  A rather cranky professor let me know a while later that the printers on the 3rd floor weren’t working.  We have two printers there, with a CUPS class that splits the load between them.  The idea being that if one of the printers jams, the other keeps going.  This keeps the users from complaining.  Except when no print jobs finish for several days.

So I took a look at the 3rd floor printers on my way into the office one morning.  One of them had jammed, there was no question about that.  Several sheets were stuck in whatever that doohickey is that sits under the toner cartridge in the LaserJet 8150.  When that was cleared, the printer began catching up.  But the other printer…no jams, no other obvious problems.  I went up to my office to take a look at the server side of things.

Wiser from my previous experience, I checked that the config was correct.  It was.  Because I couldn’t think of anything else to do, I tried to telnet to the printer.  Connection refused.  Well that sounds like a problem.  So I tried from the old server.  Success.  Nosing around in the printer config, I realized that someone had set access controls on the printer so that only the old server could access it.  Once that was updated, all was well.  For now?

Where I work, we have about thirty networked printers that we manage with CUPS.  When CUPS encounters something it doesn’t like, it has a tendency to shut down the affected queue.  Sometimes this happens when the server can no longer communicate with the target printer.  Sometimes, it happens when a job is submitted with a bad print driver.  There have been a few occasions when I’m fairly certain that gremlins were to blame.  Whatever the reason, when the queue gets disabled, people can still print to it, but the jobs just pile up.

You might expect that people would complain quickly when their print jobs come out.  That’s not always the case.  Earlier this week, one of the most heavily-used printers was disabled for about a day before anyone brought it to my attention.  By that time, tens of jobs were waiting, and it took over an hour (and a few paper refills) for them all to come out.  A quick “cupsenable” is all it takes to fix the problem, so there’s no reason to wait for it to get worse.

Today, I wrote a script (below) to monitor the queues and send a notification to our ticket system if one is down.  I considered having it run cupsenable first, but I decided it would be better to not mask the problem.  My wife wisely pointed out that if one particular queue is failing regularly, it might be worth knowing about.

#!/bin/bash
# check_cups
#
# Ben Cotton - 2009
#
#  Checks the status of the CUPS queues and notify if one is down
#
# The queues we want to check
queues=`lpstat -a | awk '{print $1}'`
# The e-mail address to notify when a queue goes byebye
toEmail='help@example.com'
# The e-mail address to use as the 'from'
fromEmail='cups@example.com'
# The directory to keep the lock files
lockDir='/var/run'
# The host to say we're complaining about
hostName=`hostname | awk -F. '{print $1}'`
# Where is mail?
mail='/bin/mail -s'
# Check the queues
for queue in $queues; do
lpq -P $queue | /usr/xpg4/bin/grep -q " not "
if [ $? -eq 0 ]; then
# Have we already complained about this queue?
if [  -a $lockDir/check_cups.$queue ]; then
true
else
touch $lockDir/check_cups.$queue
$mail $toEmail <<EOF
To: $toEmail
From: $fromEmail
Subject: Print queue $queue on $hostName down
The print queue $queue on host $hostName appears to be down.
EOF
fi
else
# The queue is up, check to see if it wasn't previously
if [ -a  $lockDir/check_cups.$queue ]; then
rm  $lockDir/check_cups.$queue
fi
fi
done

Printing is evil!

Is there an admin anywhere in the world who likes printing?  I seriously doubt it.  As much as I appreciate having hard copy of some things, I wouldn’t shed a tear if the oft-predicted “paperless office” were to arrive.  Recently, I encountered an odd printing problem that took the help of two colleagues to figure out.

A few weeks ago, one of our faculty sent in a trouble ticket saying that she couldn’t print from her Linux machines anymore.  Naturally, I assumed it was either user error or some config file had inadvertently been changed, so I hopped on to the box to see if I could reproduce the problem.  Nope, everything worked fine for me.  Since it was happening for her on multiple machines and not for me on the same machines, I figured it had to be an account-specific problem.

The first thing I did was to check her home directory quota.  I’ve found that’s generally a good place to start when there are account-specific problems.  Sure enough, I see that she’s nearly at quota.  Problem solved, I think, as I tell her to clear out some space and see if that fixes the problem.  Unfortunately, the problem persisted.  Okay, so the next step is to have her send the output of `printenv`.  Aha!  Her PRINTER variable is set to a printer which no longer exists.  I tell her to unset it and use `lpstat -a` to get a list of available printers.  Surely that will solve the problem.

Once again, my hopes are crushed.  She tells me that lpstat gives “get-printers failed: client-error-bad-request”.  What?  That doesn’t make any sense.  The client is obviously configured correctly because it works for me.  The server doesn’t care about accounts, it’ll print anything sent to it.  So what could be the problem?  I know slightly more than nothing about programming, but I figure I’ll ask her to run strace.  Maybe the output will be helpful, and if nothing else, it makes it look like I’m doing something.

So I compared her strace output with mine.  Line after line after line after painful line match (or close enough), and I’m about to give up, so I ask a friend for help.  He recruits his office mate, who notices one small difference.  She’s using the iso-8859-1 charset, while I’m using UTF-8.  So we poke around some more.  In our environment, the default value for $LANG is en_US.utf-8.  Hers was set to just en_US.  Well it turns out that at some point (apparently) CUPS decided that UTF-8 was the only charset that was going to be supported, so when we did our CUPS upgrade recently (the last install of CUPS was done in 2004!) it broke her printing.  So the simple fix was to change the first line in /usr/share/locale/en_US/cups_en_US to “utf-8”.  Tadaa!  And it only took a week to figure out.

At least it wasn’t a specific file causing a paper jam every time.