summaryrefslogtreecommitdiff
path: root/beh.in
blob: b73ba043ec54a5d03ec10767aefb8b2e704ad300 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!@PERL@
# The above Perl path may vary on your system; fix it!!! -*- perl -*-

# beh - Backend Error Handler

# A wrapper for CUPS backends to make error handling configurable

# Usually, if a CUPS backend exits with an error status other than zero
# (for example if a printer is not turned on or not reachable on the
# network), CUPS disables the print queue and one can only print again
# if a system administrator re-enables the queue manually. Even restarting
# CUPS (or rebooting) does not re-enable disabled queues.
#
# For system administrators this can get annoying, for newbie users
# who are not aware of this problem it looks like that CUPS is severely
# broken. They remove and re-install print queues, getting on the nerves
# of distro install support, people, or even switch back to a proprietary
# operating system.
#
# This script makes the handling of such backend errors configurable, so
# that the problem can easily be worked around. The new possibilities are:
#
#  - Let queues simply not being disabled. Simple approach, but job gets 
#    lost.
#
#  - Repeat a given number of times.
#
#  - Repeat infinitely often, until the job gets finally through. This
#    is the standard of LPRng, and it eliminates loss of the job.
#
#  - The interval between two attemts to run the backend can also be
#    configured.
#
#  - Configuration is done independently for each print queue. So local
#    printers and network printers can be treated differently.

# Save this file in your CUPS backend directory, usually
# /usr/lib/cups/backend/ or /usr/local/lib/cups/backend/
#
# Mark this filter world-readable and world-executable. Restart CUPS to
# make the new backend known to the spooler.
#
# See http://www.openprinting.org/cups-doc.html and the additional
# instructions below.

# beh - Backend Error Handler
#
# Copyright 2005 Till Kamppeter <till.kamppeter@gmail.com>
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2 of the License, or (at your
#  option) any later version.
#
#  This program is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
#  Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
#  USA.

# Usage: 
#
# cp beh /usr/lib/cups/backend/
# chmod 755 /usr/lib/cups/backend/beh
# killall -HUP cupsd (or "/etc/init.d/cups restart")
# lpadmin -p <queue name> -E -v beh:/<dd>/<att>/<delay>/<originaluri>
#
# with 
#   <queue name>:     The name of your print queue
#   <dd>:             Don't Disable, if "1", beh always exits with zero
#                     status, so the queue gets never disabled when the
#                     original backend exits with an error. "0" carries
#                     the error status of the last call of the backend
#                     (after <att> retries) on to CUPS, so the queue
#                     usually gets disabled.
#   <att>:            Attempts, number of attempts to recall the backend
#                     in case of an error. "0" means infinite retries. In
#                     this case <dd> gets meaningless.
#   <delay>:          Delay between two attempts to call the beckend, to
#                     be given in seconds and as an integer number.
#                     Meaningless if <att> is one.
#   <originaluri>:    The original URI, which your queue had before.
#
# All parameters, especially, <dd>, <att>, and <delay> have always to
# be specified, even if one of them is meaningless due to the setting of
# the others.
#
# beh works with every backend except the "hp" backend of HPLIP.
#
# Example URIs:
#
# beh:/1/3/5/socket://printer:9100
#
#   On the network printer with host name "printer" it is tried to access
#   3 times with 5 second delays between the attempts. If the job still
#   fails, the queue is not disabled (and the job discarded).
#
# beh:/0/10/60/socket://printer:9100
#
#   Retry 10 times in one minute intervals, disable the queue when still
#   not succeeding.
#
# beh:/1/0/60/usb://Brother/HL-5040%20series
#
#   On a Brother HL-5040 on the USB try infinitely often until the printer
#   comes back, in intervals of one minute. This way the job does not get
#   lost when the printer is turned off and one can intendedly delay
#   printing by simply switching off the printer. The ideal configuration
#   for desktop printers and/or home users.

# Acknowledgement
# 
# Thanks to Jeff Hardy (hardyjm at potsdam dot edu) for writing the
# "accsnmp" wrapper backend (http://fritz.potsdam.edu/projects/cupsapps/). 
# This backend showed me the trick how to write a universal wrapper 
# backend in a scripting language.

use strict;

$0 =~ m!^(.*)/([^/]+)\s*$!;
my $progname = ($2 || $0);
my $progpath = ($1 || "@CUPS_BACKENDS@");

if (!$ARGV[0]){
    print "network $progname \"Unknown\" \"Backend Error Handler\"\n";
    exit 0;
}

if (scalar(@ARGV) < 5 || scalar(@ARGV) > 6){
    print STDERR "ERROR: Usage: $progname job-id user title copies options [file]\n";
    exit 1;
}

my ($jobID, $userName, $jobTitle, $copies, $printOptions, $printFile) = 
    @ARGV;

my $tempFile;
if (!$printFile) { 

    my $jid = $jobID;
    my $uid = $userName;
    $jid =~ s/\W//g; #sanity check
    $uid =~ s/\W//g; #sanity check
    my $tmpDir = $ENV{TMPDIR};
    $tmpDir ||= "/tmp";
    $tempFile = "$tmpDir/$jid-$uid-cupsjob$$";

    open (OUT, ">$tempFile") or die "ERROR: Cannot write $tempFile: $!\n";

    while(<STDIN>){
	print OUT "$_";
    }

    close OUT;

    $printFile = $tempFile;

    # Backends should only produce multiple copies if a file name is 
    # supplied (see CUPS Software Programmers Manual)
    $copies = 1;

}

my $uri = $ENV{DEVICE_URI};
$uri =~ m!^$progname:/(\d+)/(\d+)/(\d+)/(\S+)$! or
    die "URI must be \"beh:/<dd>/<att>/<delay>/<original uri>\"!\n";
my $dontdisable = $1;
my $attempts = $2;
my $delay = $3;
$uri = $4;
$uri =~ m!^([^:\s]+):!;
my $backend = $1;
$ENV{DEVICE_URI} = $uri;

# Control by "lpr" command line options, commented out for security
# reasons (user could intendedly make queues being disabled)

#$printOptions =~ m/\bBackendErrorDisableQueue=(\S*)\b/ && 
#    ($dontdisable = ($1 =~ /no/i ? 1 : 0));
#$printOptions =~ m/\bBackendErrorRetries=(\S*)\b/ && ($attempts = $1);
#$printOptions =~ m/\bBackendErrorRetryDelay=(\S*)\b/ && ($delay = $1);
#$printOptions =~ m/\bBackendErrorRetryForever=(\S*)\b/ &&
#    ($delay = ($1 =~ /yes/i ? 0 : $delay));

my $exitvalue;
while($exitvalue = (($uri !~ m!^file:(.*)$!) && ($uri !~ m!^(/.*)$!) ?
		    system {"$progpath/$backend"}
		    ($uri, $jobID, $userName, $jobTitle, $copies,
		     $printOptions, $printFile) :
		    system ("cat $printFile > $1")) >> 8) {
    if ($attempts > 0) {
	$attempts --;
	last if $attempts == 0;
    }
    sleep $delay if $delay > 0;
}

unlink $tempFile if $tempFile;

$exitvalue = 0 if $dontdisable;
exit $exitvalue;