[buug] Managing without CUPS

Ian Zimmerman itz at buug.org
Thu Sep 6 22:50:48 PDT 2012


At the meeting tonight I mentioned that I no longer use the buggy &
bloated CUPS spooler for printing, or any other spooler.  Instead, I
pass input files through a foomatic-rip pipeline directly to the printer
port on a printer server machine, subject only to a UUCP-like locking
protocol.

Here are the 2 scripts involved:

/usr/local/bin/noqp:

#! /usr/bin/python

# Trivial script for No Queue Printing.

import subprocess as SP
import getopt
import sys
import os
import select

opts, args = getopt.getopt(sys.argv[1:], 'P:J:o:')

jobname = None
printer = None
popts = []
for k, v in opts:
    if k == '-J':
        if jobname is not None:
            sys.stderr.write('Multiple -J options\n')
            sys.exit(2)
        jobname = v
    elif k == '-P':
        if printer is not None:
            sys.stderr.write('Multiple -P options\n')
            sys.exit(2)
        printer = v
    elif k == '-o':
        popts.append(v)

# foomatic-rip in no spool mode has no default for -P so we have to
# supply it, one way or another
if printer is None:
    try:
        printer = os.environ['PRINTER']
    except KeyError:
        sys.stderr.write('No -P option and no PRINTER envvar\n')
        sys.exit(2)
    if not printer:
        sys.stderr.write('No -P option and PRINTER envvar empty\n')
        sys.exit(2)

# remote printer details
try:
    host, device = os.environ['NOQP_HOST'], os.environ['NOQP_DEVICE']
except KeyError:
    sys.stderr.write('NOQP_HOST or NOQP_DEVICE envvar unset\n')
    sys.exit(2)
if not host or not device:
    sys.stderr.write('NOQP_HOST or NOQP_DEVICE envvar empty\n')

# gather arguments for foomatic
foomargs = ['-P', printer]
if jobname:
    foomargs.extend(('-J', jobname))
for popt in popts:
    foomargs,extend(('-o', popt))

# set up the pipeline
sp_cat = SP.Popen(['cat'] + args, stdout=SP.PIPE, close_fds=True)

sp_rip = SP.Popen(['foomatic-rip'] + foomargs,
                  stdin=SP.PIPE, stdout=SP.PIPE, close_fds=True)

sp_ssh = SP.Popen(['ssh', host, 'lockedcat', device],
                  stdin=SP.PIPE, close_fds=True)

buf_cat_rip = ''
buf_rip_ssh = ''

rdict = {'cat': sp_cat.stdout, 'rip': sp_rip.stdout}
wdict = {'rip': sp_rip.stdin, 'ssh': sp_ssh.stdin}

# limit buffering, otherwise we might read the entire file into memory
MAX_BUF = 40 * select.PIPE_BUF

while rdict or wdict:
    if not buf_cat_rip and 'cat' not in rdict and 'rip' in wdict:
        sp_rip.stdin.close()
        del wdict['rip']
    if not buf_rip_ssh and 'rip' not in rdict and 'ssh' in wdict:
        sp_ssh.stdin.close()
        del wdict['ssh']
    wlist = []
    if 'rip' in wdict and buf_cat_rip:
        wlist.append(wdict['rip'])
    if 'ssh' in wdict and buf_rip_ssh:
        wlist.append(wdict['ssh'])
    rlist = []
    if 'cat' in rdict and len(buf_cat_rip) < MAX_BUF:
        rlist.append(rdict['cat'])
    if 'rip' in rdict and len(buf_rip_ssh) < MAX_BUF:
        rlist.append(rdict['rip'])
    rset, wset, _ = select.select(rlist, wlist, [], 1.0)
    if sp_rip.stdin in wset:
        amount = min(select.PIPE_BUF, len(buf_cat_rip))
        sp_rip.stdin.write(buf_cat_rip[:amount])
        buf_cat_rip = buf_cat_rip[amount:]
    if sp_ssh.stdin in wset:
        amount = min(select.PIPE_BUF, len(buf_rip_ssh))
        sp_ssh.stdin.write(buf_rip_ssh[:amount])
        buf_rip_ssh = buf_rip_ssh[amount:]
    if sp_cat.stdout in rset:
        new_cat = sp_cat.stdout.read(select.PIPE_BUF)
        if not new_cat:
            del rdict['cat']
            sp_cat.stdout.close()
        else:
            buf_cat_rip = buf_cat_rip + new_cat
    if sp_rip.stdout in rset:
        new_rip = sp_rip.stdout.read(select.PIPE_BUF)
        if not new_rip:
            del rdict['rip']
            sp_rip.stdout.close()
        else:
            buf_rip_ssh = buf_rip_ssh + new_rip

status = 0
for sp in (sp_cat, sp_rip, sp_ssh):
    sp.wait()
    if sp.returncode != 0:
        status = 1

sys.exit(status)


/usr/local/bin/lockedcat:

#! /bin/sh

set -e

device="$1"
lockfile="/run/lock/LCK..`basename ${device}`"

dotlockfile -r 100 -p "${lockfile}"

trap "dotlockfile -u ${lockfile}" EXIT HUP TERM INT

buffer -p 75 > "${device}"


-- 
Ian Zimmerman
gpg public key: 1024D/C6FF61AD
fingerprint: 66DC D68F 5C1B 4D71 2EE5  BD03 8A00 786C C6FF 61AD
http://www.gravatar.com/avatar/c66875cda51109f76c6312f4d4743d1e.png
Rule 420: All persons more than eight miles high to leave the court.


More information about the buug mailing list