Writing

Software, technology, sysadmin war stories, and more. Feed
Tuesday, September 11, 2012

Printing on Unix has always been needlessly miserable

I've never been happy with the stock printing situation on Unix type systems. Back when I used to run systems which actually had users (other than myself, that is), I had to provide a working environment which included printing. This is because those users tended to log in to my machines and would use things like pine to do their mail. When they said "print", lpr and friends had to be ready to go on that machine or they would be unhappy. The problem is that lp* system software always made me miserable.

It's clear that the whole lpd situation exists to handle every conceivable situation which might arise. The fact that its configuration resembles a BSD termcap file pretty much proves that. It had the kind of flexibility that nobody hopefully needs any more. For example, does your printer really need you to emit multiple linefeed characters in order to make it jump down the right amount between lines? There's an option for that, just in case.

Does your printer even have a physical line-based mechanism? If it's a laser printer, I'm guessing it does not. It probably builds up some kind of "plan" in memory based on the input and then rolls it out via the drum all at once as the paper passes by. There's no head sliding back and forth like the old days.

Speaking of sliding print heads, I would hope that nobody needs to insert delays to allow their print head to make it all the way to the left any time a carriage return is emitted. There are options for that, too. I bet there are people who just think that "carriage return" is some strangely named control character and don't know there used to be a physical component to it. Lucky them.

This stuff was archaic back in the '90s when I first encountered it. All of our printers were sitting on the network and spoke TCP/IP. If you connected to them on port 9100, you could spit jobs at them as if you were plugged into the parallel port on the back. Trying to make lpr/lpd do this was an exercise in futility.

My boss just wanted to be able to run elm (yes, really) on the machine and on the occasions when he said "print", it needed to generate legible results on the laser printer on his desk. Sometimes he would print "sensitive" things which shouldn't be seen by the great unwashed masses, and thus such jobs were not to go to the shared department printer which was relatively far away.

However, for other users, using the shared printer was just fine. The other people who occasionally printed things from there were all able to fetch their jobs on that device and had no particular need for privacy, since they weren't managers. They were just fellow "leaf nodes" like myself.

All I wanted was something which would take a bunch of text and turn it into paper. I finally gave up on trying to come up with a usable configuration for lpd and instead wrote a handful of small stupid helper scripts and programs to make it work.

It was such an evil pile of hacks. The initial logic was simple enough: Give me a filename and I'll copy the contents to a temp location. Otherwise, I'll read from stdin and store in that same temp location. Then, I'll run "file" on it to figure out what it is. If it's PostScript, I'll hand it to "bsd2dos" to fix line endings and dump it to a second temp file. If it's not already PostScript, then I'd send it through "gs" (GhostScript) to make it that way and write to that second temp file.

Then, it would just take whatever was in that second temp file and would throw it at the printer via the network. Originally, things like netcat did not exist, so I had to write a really small and stupid helper tool which would connect to a host on port 9100 and would just send data from its stdin to that socket.

Version two came about when it became necessary to route jobs to different printers based on who was running the job. For that, I just had it look for a file called ".printer" in your home directory. If it was there, then it would connect to that host instead of the default. This way, only people who actually needed to worry about alternates had to run the secondary tool which set up that file. Everyone else took the defaults and everything was fine.

A subsequent version added a few more hacks to do neat things based on the results of running 'file' on the input. It still handled PostScript and text data as described above, but now it would also deal with GIF and JPEGs properly. Thanks to the NetPBM tools or whatever they may have been called back then, it was possible to run giftopnm or djpeg and then pnmtops to get something which would look presentable.

The upshot of all of this is that you could sit in the shell and "lpr foo", and it would probably work for all of the common cases we encountered on that system: print an image, print a plain text file, or print something which was already PostScript.

Notice that I've not even touched the matter of queuing. As far as I was concerned, queuing was evil. You either wanted your job to start printing right then while you were waiting, or within a brief delay, again with you keeping an eye on things. Having jobs sit there forever if something broke was idiotic. Who wants that?

The only real problem with my original hacky "throw at port 9100" scheme was that it fared poorly when other people were printing at the very same time. I wound up changing that to add a retry mechanism so it would try every 15 seconds for up to 10 times. This way, you had to wait at most 2.5 minutes to find out if it was going to work or not. Our printer was never legitimately busy that long, so if a job failed to "go" in that window, something else was happening and needed to be fixed.

I suppose if you work in an environment where you have a monster printer with dozens of users that never goes idle, then you might actually want a "real" queue system. You'd fire off your job, and it would "get in line", and would eventually print, or so you hope. Then, you'd personally go "get in line" at the printer and wait for it to show up.

I have yet to see that happen any place where I have worked, consulted, or otherwise helped out on occasion. I guess it must exist somewhere, since why else do these print software designers build these things this way?

Incidentally, CUPS did not yet exist back when I started dealing with it. If it had, I probably would have jumped off a bridge. When I did encounter it a few years later, it was just as useless as lpr. Let me put it this way: if you have to write a script like this to trick stuff into actually running, something is very wrong.

#!/bin/sh
echo "system default destination: Stylus880"
echo "printer Stylus880 is idle."

Over the years, I watched as numerous security CUPS vulnerabilities rolled by. Given that I wasn't running it and indeed, had no "printer daemon" at all, I just laughed.

Let's think about this. As the '90s turned into the '00s, Unixy systems largely turned into "one user at a time" and even "only one user ever". Meanwhile, most ordinary people probably still have their printers directly connected. Given all of this, did a massive daemon/queuing scheme ever make sense for them? I don't think so.

I've gotten stuck running this kind of stuff when I existed in an environment as a "mere user". I just love the stupid blocking user interfaces where accidentally hitting ^P will lock up the entire program while it goes out and finds every printer available on the whole network. Yes, clearly, that's what I want to do every time I happen to push ^P by mistake in Firefox instead of in bash when I try to go back a line in my command history. Thank you for providing this opportunity to hate you so much more, losing print software.