189 | | if action not in ["ALLOW", "WARN"] : |
190 | | # if not allowed to print then die, else proceed. |
191 | | # we die without error, so that the job doesn't |
192 | | # stop the print queue. |
193 | | retcode = 0 |
194 | | else : |
195 | | # pass the job untouched to the underlying layer |
196 | | # and starts accounting at the same time |
197 | | thebackend.accounter.beginJob(printer, user) |
198 | | |
199 | | # Now it becomes tricky... |
200 | | |
201 | | # First ensure that we have a file object as input |
202 | | mustclose = 0 |
203 | | if thebackend.inputfile is not None : |
204 | | if hasattr(thebackend.inputfile, "read") : |
205 | | infile = thebackend.inputfile |
| 194 | if action not in ["ALLOW", "WARN"] : |
| 195 | # if not allowed to print then die, else proceed. |
| 196 | # we die without error, so that the job doesn't |
| 197 | # stop the print queue. |
| 198 | retcode = 0 |
| 199 | else : |
| 200 | # pass the job untouched to the underlying layer |
| 201 | # and starts accounting at the same time |
| 202 | thebackend.accounter.beginJob(printer, user) |
| 203 | |
| 204 | # Now it becomes tricky... |
| 205 | |
| 206 | # First ensure that we have a file object as input |
| 207 | mustclose = 0 |
| 208 | if thebackend.inputfile is not None : |
| 209 | if hasattr(thebackend.inputfile, "read") : |
| 210 | infile = thebackend.inputfile |
| 211 | else : |
| 212 | infile = open(thebackend.inputfile, "rb") |
| 213 | mustclose = 1 |
207 | | infile = open(thebackend.inputfile, "rb") |
208 | | mustclose = 1 |
209 | | else : |
210 | | infile = sys.stdin |
211 | | |
212 | | # Find the real backend pathname |
213 | | realbackend = os.path.join(os.path.split(sys.argv[0])[0], thebackend.originalbackend) |
214 | | |
215 | | # And launch it |
216 | | subprocess = PyKotaPopen3([realbackend] + sys.argv[1:], capturestderr=1, bufsize=0, arg0=os.environ["DEVICE_URI"]) |
217 | | |
218 | | # Save file descriptors, we will need them later. |
219 | | infno = infile.fileno() |
220 | | stdoutfno = sys.stdout.fileno() |
221 | | stderrfno = sys.stderr.fileno() |
222 | | fromcfno = subprocess.fromchild.fileno() |
223 | | tocfno = subprocess.tochild.fileno() |
224 | | cerrfno = subprocess.childerr.fileno() |
225 | | |
226 | | # We will have to be careful when dealing with I/O |
227 | | # So we use a poll object to know when to read or write |
228 | | pollster = select.poll() |
229 | | pollster.register(infno, select.POLLIN | select.POLLPRI) |
230 | | pollster.register(fromcfno, select.POLLIN | select.POLLPRI) |
231 | | pollster.register(cerrfno, select.POLLIN | select.POLLPRI) |
232 | | pollster.register(stdoutfno, select.POLLOUT) |
233 | | pollster.register(stderrfno, select.POLLOUT) |
234 | | pollster.register(tocfno, select.POLLOUT) |
235 | | |
236 | | # Initialize our buffers |
237 | | indata = "" |
238 | | outdata = "" |
239 | | errdata = "" |
240 | | endinput = 0 |
241 | | status = -1 |
242 | | while status == -1 : |
243 | | # First check if original backend is still alive |
244 | | status = subprocess.poll() |
245 | | |
246 | | # Now if we got SIGTERM, we have |
247 | | # to kill -TERM the original backend |
248 | | if gotSigTerm : |
249 | | try : |
250 | | os.kill(subprocess.pid, signal.SIGTERM) |
251 | | except : # ignore if process was already killed. |
252 | | pass |
253 | | |
254 | | # In any case, deal with any remaining I/O |
255 | | availablefds = pollster.poll() |
256 | | for (fd, mask) in availablefds : |
257 | | if mask & select.POLLOUT : |
258 | | # We can write |
259 | | if fd == tocfno : |
260 | | if indata : |
261 | | os.write(fd, indata) |
| 215 | infile = sys.stdin |
| 216 | |
| 217 | # Find the real backend pathname |
| 218 | realbackend = os.path.join(os.path.split(sys.argv[0])[0], thebackend.originalbackend) |
| 219 | |
| 220 | # And launch it |
| 221 | subprocess = PyKotaPopen3([realbackend] + sys.argv[1:], capturestderr=1, bufsize=0, arg0=os.environ["DEVICE_URI"]) |
| 222 | |
| 223 | # Save file descriptors, we will need them later. |
| 224 | infno = infile.fileno() |
| 225 | stdoutfno = sys.stdout.fileno() |
| 226 | stderrfno = sys.stderr.fileno() |
| 227 | fromcfno = subprocess.fromchild.fileno() |
| 228 | tocfno = subprocess.tochild.fileno() |
| 229 | cerrfno = subprocess.childerr.fileno() |
| 230 | |
| 231 | # We will have to be careful when dealing with I/O |
| 232 | # So we use a poll object to know when to read or write |
| 233 | pollster = select.poll() |
| 234 | pollster.register(infno, select.POLLIN | select.POLLPRI) |
| 235 | pollster.register(fromcfno, select.POLLIN | select.POLLPRI) |
| 236 | pollster.register(cerrfno, select.POLLIN | select.POLLPRI) |
| 237 | pollster.register(stdoutfno, select.POLLOUT) |
| 238 | pollster.register(stderrfno, select.POLLOUT) |
| 239 | pollster.register(tocfno, select.POLLOUT) |
| 240 | |
| 241 | # Initialize our buffers |
| 242 | indata = "" |
| 243 | outdata = "" |
| 244 | errdata = "" |
| 245 | endinput = 0 |
| 246 | status = -1 |
| 247 | while status == -1 : |
| 248 | # First check if original backend is still alive |
| 249 | status = subprocess.poll() |
| 250 | |
| 251 | # Now if we got SIGTERM, we have |
| 252 | # to kill -TERM the original backend |
| 253 | if gotSigTerm : |
| 254 | try : |
| 255 | os.kill(subprocess.pid, signal.SIGTERM) |
| 256 | except : # ignore if process was already killed. |
| 257 | pass |
| 258 | |
| 259 | # In any case, deal with any remaining I/O |
| 260 | availablefds = pollster.poll() |
| 261 | for (fd, mask) in availablefds : |
| 262 | if mask & select.POLLOUT : |
| 263 | # We can write |
| 264 | if fd == tocfno : |
| 265 | if indata : |
| 266 | os.write(fd, indata) |
| 267 | indata = "" |
| 268 | elif fd == stdoutfno : |
| 269 | if outdata : |
| 270 | os.write(fd, outdata) |
| 271 | outdata = "" |
| 272 | elif fd == stderrfno : |
| 273 | if errdata : |
| 274 | os.write(fd, errdata) |
| 275 | errdata = "" |
| 276 | if (mask & select.POLLIN) or (mask & select.POLLPRI) : |
| 277 | # We have something to read |
| 278 | data = os.read(fd, 256 * 1024) |
| 279 | if fd == infno : |
| 280 | indata += data |
| 281 | if not data : # If yes, then no more input data |
| 282 | endinput = 1 # this happens with real files. |
| 283 | elif fd == fromcfno : |
| 284 | outdata += data |
| 285 | elif fd == cerrfno : |
| 286 | errdata += data |
| 287 | if (mask & select.POLLHUP) or (mask & select.POLLERR) : |
| 288 | # I've never seen POLLERR myself, but this probably |
| 289 | # can't hurt to treat an error condition just like |
| 290 | # an EOF. |
| 291 | # |
| 292 | # Some standard I/O stream has no more datas |
| 293 | if fd == infno : |
| 294 | # Here we are in the case where the input file is stdin. |
| 295 | # which has no more data to be read. |
| 296 | endinput = 1 |
| 297 | elif fd == fromcfno : |
| 298 | # This should never happen, since |
| 299 | # CUPS backends don't send anything on their |
| 300 | # standard output |
| 301 | if outdata : |
| 302 | try : |
| 303 | os.write(stdoutfno, outdata) |
| 304 | outdata = "" |
| 305 | except : |
| 306 | pass |
| 307 | try : |
| 308 | pollster.unregister(fromcfno) |
| 309 | except KeyError : |
| 310 | pass |
| 311 | else : |
| 312 | os.close(fromcfno) |
| 313 | elif fd == cerrfno : |
| 314 | # Original CUPS backend has finished |
| 315 | # to write informations on its standard error |
| 316 | if errdata : |
| 317 | # Try to write remaining info (normally "Ready to print.") |
| 318 | try : |
| 319 | os.write(stderrfno, errdata) |
| 320 | errdata = "" |
| 321 | except : |
| 322 | pass |
| 323 | # We are no more interested in this file descriptor |
| 324 | try : |
| 325 | pollster.unregister(cerrfno) |
| 326 | except KeyError : |
| 327 | pass |
| 328 | else : |
| 329 | os.close(cerrfno) |
| 330 | |
| 331 | if endinput : |
| 332 | # We deal with remaining input datas here |
| 333 | # because EOF can happen in two different |
| 334 | # situations and I don't want to duplicate |
| 335 | # code, nor making functions. |
| 336 | if indata : |
| 337 | try : |
| 338 | os.write(tocfno, indata) |
346 | | # Input file was a real file, we have to close it. |
347 | | if mustclose : |
348 | | infile.close() |
349 | | |
350 | | # Check exit code of original CUPS backend. |
351 | | if os.WIFEXITED(status) : |
352 | | retcode = os.WEXITSTATUS(status) |
353 | | else : |
354 | | thebackend.logger.log_message(_("CUPS backend %s died abnormally.") % realbackend, "error") |
355 | | retcode = -1 |
356 | | |
357 | | # stops accounting. |
358 | | thebackend.accounter.endJob(printer, user) |
359 | | |
360 | | # retrieve the job size |
361 | | jobsize = thebackend.accounter.getJobSize() |
362 | | |
363 | | # update the quota for the current user on this printer |
364 | | if printer.Exists : |
365 | | jobprice = (float(printer.PricePerPage or 0.0) * jobsize) + float(printer.PricePerJob or 0.0) |
366 | | if jobsize : |
367 | | userquota = thebackend.storage.getUserPQuota(user, printer) |
368 | | if userquota.Exists : |
369 | | userquota.increasePagesUsage(jobsize) |
370 | | |
371 | | # adds the current job to history |
372 | | printer.addJobToHistory(thebackend.jobid, user, thebackend.accounter.getLastPageCounter(), action, jobsize, jobprice, thebackend.preserveinputfile, thebackend.title, thebackend.copies, thebackend.options) |
373 | | |
374 | | return retcode # return (retcode or gotSigTerm) shouldn't be needed |
| 355 | # Check exit code of original CUPS backend. |
| 356 | if os.WIFEXITED(status) : |
| 357 | retcode = os.WEXITSTATUS(status) |
| 358 | else : |
| 359 | thebackend.logger.log_message(_("CUPS backend %s died abnormally.") % realbackend, "error") |
| 360 | retcode = -1 |
| 361 | |
| 362 | # stops accounting. |
| 363 | thebackend.accounter.endJob(printer, user) |
| 364 | |
| 365 | # retrieve the job size |
| 366 | jobsize = thebackend.accounter.getJobSize() |
| 367 | |
| 368 | # update the quota for the current user on this printer |
| 369 | if printer.Exists : |
| 370 | jobprice = (float(printer.PricePerPage or 0.0) * jobsize) + float(printer.PricePerJob or 0.0) |
| 371 | if jobsize : |
| 372 | userquota = thebackend.storage.getUserPQuota(user, printer) |
| 373 | if userquota.Exists : |
| 374 | userquota.increasePagesUsage(jobsize) |
| 375 | |
| 376 | # adds the current job to history |
| 377 | printer.addJobToHistory(thebackend.jobid, user, thebackend.accounter.getLastPageCounter(), action, jobsize, jobprice, thebackend.preserveinputfile, thebackend.title, thebackend.copies, thebackend.options) |
| 378 | |
| 379 | return retcode # return (retcode or gotSigTerm) shouldn't be needed |