root / pykota / trunk / pykota / accounters / hardware.py @ 1703

Revision 1703, 7.1 kB (checked in by jalet, 20 years ago)

Fix missing import statement for the signal module

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# PyKota
2# -*- coding: ISO-8859-15 -*-
3#
4# PyKota - Print Quotas for CUPS and LPRng
5#
6# (c) 2003-2004 Jerome Alet <alet@librelogiciel.com>
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20#
21# $Id$
22#
23# $Log$
24# Revision 1.12  2004/09/06 15:42:34  jalet
25# Fix missing import statement for the signal module
26#
27# Revision 1.11  2004/08/31 23:29:53  jalet
28# Introduction of the new 'onaccountererror' configuration directive.
29# Small fix for software accounter's return code which can't be None anymore.
30# Make software and hardware accounting code look similar : will be factorized
31# later.
32#
33# Revision 1.10  2004/08/27 22:49:04  jalet
34# No answer from subprocess now is really a fatal error. Waiting for some
35# time to make this configurable...
36#
37# Revision 1.9  2004/08/25 22:34:39  jalet
38# Now both software and hardware accounting raise an exception when no valid
39# result can be extracted from the subprocess' output.
40# Hardware accounting now reads subprocess' output until an integer is read
41# or data is exhausted : it now behaves just like software accounting in this
42# aspect.
43#
44# Revision 1.8  2004/07/22 22:41:48  jalet
45# Hardware accounting for LPRng should be OK now. UNTESTED.
46#
47# Revision 1.7  2004/07/16 12:22:47  jalet
48# LPRng support early version
49#
50# Revision 1.6  2004/07/01 19:56:42  jalet
51# Better dispatching of error messages
52#
53# Revision 1.5  2004/06/10 22:42:06  jalet
54# Better messages in logs
55#
56# Revision 1.4  2004/05/24 22:45:49  jalet
57# New 'enforcement' directive added
58# Polling loop improvements
59#
60# Revision 1.3  2004/05/24 14:36:40  jalet
61# Revert to old polling loop. Will need optimisations
62#
63# Revision 1.2  2004/05/18 14:49:22  jalet
64# Big code changes to completely remove the need for "requester" directives,
65# jsut use "hardware(... your previous requester directive's content ...)"
66#
67# Revision 1.1  2004/05/13 13:59:30  jalet
68# Code simplifications
69#
70#
71#
72
73import sys
74import os
75import signal
76import popen2
77from pykota.accounter import AccounterBase, PyKotaAccounterError
78
79class Accounter(AccounterBase) :
80    def __init__(self, kotabackend, arguments) :
81        """Initializes querying accounter."""
82        AccounterBase.__init__(self, kotabackend, arguments)
83        self.isSoftware = 0
84       
85    def getPrinterInternalPageCounter(self) :   
86        """Returns the printer's internal page counter."""
87        self.filter.logdebug("Reading printer's internal page counter...")
88        counter = self.askPrinterPageCounter(self.filter.printerhostname)
89        self.filter.logdebug("Printer's internal page counter value is : %s" % str(counter))
90        return counter   
91       
92    def beginJob(self, printer) :   
93        """Saves printer internal page counter at start of job."""
94        # save page counter before job
95        self.LastPageCounter = self.getPrinterInternalPageCounter()
96        self.fakeBeginJob()
97       
98    def fakeBeginJob(self) :   
99        """Fakes a begining of a job."""
100        self.counterbefore = self.getLastPageCounter()
101       
102    def endJob(self, printer) :   
103        """Saves printer internal page counter at end of job."""
104        # save page counter after job
105        self.LastPageCounter = self.counterafter = self.getPrinterInternalPageCounter()
106       
107    def getJobSize(self) :   
108        """Returns the actual job size."""
109        try :
110            jobsize = (self.counterafter - self.counterbefore)   
111            if jobsize < 0 :
112                # Try to take care of HP printers
113                # Their internal page counter is saved to NVRAM
114                # only every 10 pages. If the printer was switched
115                # off then back on during the job, and that the
116                # counters difference is negative, we know
117                # the formula (we can't know if more than eleven
118                # pages were printed though) :
119                if jobsize > -10 :
120                    jobsize += 10
121                else :   
122                    # here we may have got a printer being replaced
123                    # DURING the job. This is HIGHLY improbable !
124                    jobsize = 0
125        except :   
126            # takes care of the case where one counter (or both) was never set.
127            jobsize = 0
128        return jobsize
129       
130    def askPrinterPageCounter(self, printer) :
131        """Returns the page counter from the printer via an external command.
132       
133           The external command must report the life time page number of the printer on stdout.
134        """
135        commandline = self.arguments.strip() % locals()
136        if printer is None :
137            raise PyKotaAccounterError, _("Unknown printer address in HARDWARE(%s) for printer %s") % (commandline, self.filter.printername)
138        self.filter.printInfo(_("Launching HARDWARE(%s)...") % commandline)
139        pagecounter = None
140        child = popen2.Popen4(commandline)   
141        try :
142            answer = child.fromchild.read()
143        except IOError :   
144            # we were interrupted by a signal, certainely a SIGTERM
145            # caused by the user cancelling the current job
146            try :
147                os.kill(child.pid, signal.SIGTERM)
148            except :   
149                pass # already killed ?
150            self.filter.printInfo(_("SIGTERM was sent to hardware accounter %s (pid: %s)") % (commandline, child.pid))
151        else :   
152            lines = [l.strip() for l in answer.split("\n")]
153            for i in range(len(lines)) : 
154                try :
155                    pagecounter = int(lines[i])
156                except (AttributeError, ValueError) :
157                    self.filter.printInfo(_("Line [%s] skipped in accounter's output. Trying again...") % lines[i])
158                else :   
159                    break
160        child.fromchild.close()   
161        child.tochild.close()
162        try :
163            status = child.wait()
164        except OSError, msg :   
165            self.filter.logdebug("Error while waiting for hardware accounter pid %s : %s" % (child.pid, msg))
166        else :   
167            if os.WIFEXITED(status) :
168                status = os.WEXITSTATUS(status)
169            self.filter.printInfo(_("Hardware accounter %s exit code is %s") % (self.arguments, str(status)))
170           
171        if pagecounter is None :
172            message = _("Unable to query printer %s via HARDWARE(%s)") % (printer, commandline)
173            if self.onerror == "CONTINUE" :
174                self.filter.printInfo(message, "error")
175            else :
176                raise PyKotaAccounterError, message 
177        return pagecounter       
Note: See TracBrowser for help on using the browser.