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