582 | | self.pipes = { 0: os.pipe() } |
583 | | for branchtype in ["prehook", "tee", "posthook"] : |
584 | | if branchtype == "posthook" : |
585 | | os.close(self.pipes[0][1]) |
586 | | branches = self.enumBranches(self.PrinterName, branchtype) |
587 | | for b in branches : |
588 | | bname = b.split("_", 1)[1] |
589 | | if branchtype != "posthook" and bname not in self.pipes : |
590 | | self.pipes[bname] = os.pipe() |
591 | | else : |
592 | | if bname in self.pipes : |
593 | | os.close(self.pipes[bname][1]) |
594 | | status = self.runCommands(branchtype, branches, serialize) |
595 | | if status : |
596 | | if branchtype != "posthook" : |
597 | | exitcode = status |
598 | | else : |
599 | | # we just ignore error in posthooks |
| 559 | self.pipes = { 0: (0,1) } |
| 560 | branches = self.enumBranches(self.PrinterName, "prehook") |
| 561 | for b in branches : |
| 562 | self.pipes[b.split("_", 1)[1]] = os.pipe() |
| 563 | retcode = self.runCommands("prehook", branches, serialize) |
| 564 | for p in self.pipes.items()[1:] : |
| 565 | os.close(p[1][1]) |
| 566 | if not self.isCancelled and not self.gotSigTerm : |
| 567 | if self.RealBackend : |
| 568 | retcode = self.runOriginalBackend() |
| 569 | if not self.gotSigTerm : |
| 570 | os.environ["TEASTATUS"] = str(retcode) |
| 571 | branches = self.enumBranches(self.PrinterName, "posthook") |
| 572 | if self.runCommands("posthook", branches, serialize) : |
718 | | |
719 | | def unregisterFileNo(self, pollobj, fileno) : |
720 | | """Removes a file handle from the polling object.""" |
721 | | try : |
722 | | pollobj.unregister(fileno) |
723 | | except KeyError : |
724 | | self.logInfo("File number %s unregistered twice from polling object, ignored." % fileno, "warn") |
725 | | except : |
726 | | self.logDebug("Error while unregistering file number %s from polling object." % fileno) |
727 | | else : |
728 | | self.logDebug("File number %s unregistered from polling object." % fileno) |
729 | | |
730 | | def formatFileEvent(self, fd, mask) : |
731 | | """Formats file debug info.""" |
732 | | maskval = [] |
733 | | if mask & select.POLLIN : |
734 | | maskval.append("POLLIN") |
735 | | if mask & select.POLLOUT : |
736 | | maskval.append("POLLOUT") |
737 | | if mask & select.POLLPRI : |
738 | | maskval.append("POLLPRI") |
739 | | if mask & select.POLLERR : |
740 | | maskval.append("POLLERR") |
741 | | if mask & select.POLLHUP : |
742 | | maskval.append("POLLHUP") |
743 | | if mask & select.POLLNVAL : |
744 | | maskval.append("POLLNVAL") |
745 | | return "%s (%s)" % (fd, " | ".join(maskval)) |
750 | | arguments = sys.argv |
751 | | self.logDebug("Starting original backend %s with args %s" % (originalbackend, " ".join(['"%s"' % a for a in ([os.environ["DEVICE_URI"]] + arguments[1:])]))) |
752 | | subprocess = Popen4ForCUPS([originalbackend] + arguments[1:], bufsize=0, arg0=os.environ["DEVICE_URI"]) |
753 | | |
754 | | # Save file descriptors, we will need them later. |
755 | | stderrfno = sys.stderr.fileno() |
756 | | fromcfno = subprocess.fromchild.fileno() |
757 | | tocfno = subprocess.tochild.fileno() |
758 | | |
759 | | # We will have to be careful when dealing with I/O |
760 | | # So we use a poll object to know when to read or write |
761 | | pollster = select.poll() |
762 | | pollster.register(fromcfno, select.POLLIN | select.POLLPRI) |
763 | | pollster.register(stderrfno, select.POLLOUT) |
764 | | pollster.register(tocfno, select.POLLOUT) |
765 | | |
766 | | # Initialize our buffers |
767 | | indata = "" |
768 | | outdata = "" |
769 | | endinput = endoutput = 0 |
770 | | inputclosed = outputclosed = 0 |
771 | | totaltochild = totalfromcups = 0 |
772 | | totalfromchild = totaltocups = 0 |
773 | | |
774 | | if self.InputFile is None : |
775 | | # this is not a real file, we read the job's data |
776 | | # from our temporary file which is a copy of stdin |
777 | | inf = open(self.DataFile, "rb") |
778 | | infno = inf.fileno() |
779 | | pollster.register(infno, select.POLLIN | select.POLLPRI) |
780 | | else : |
781 | | # job's data is in a file, no need to pass the data |
782 | | # to the original backend |
783 | | self.logDebug("Job's data is in %s" % self.InputFile) |
784 | | infno = None |
785 | | endinput = 1 |
786 | | |
787 | | self.logDebug("Entering streams polling loop...") |
788 | | MEGABYTE = 1024*1024 |
| 669 | arguments = [os.environ["DEVICE_URI"]] + sys.argv[1:] |
| 670 | self.logDebug("Starting original backend %s with args %s" % (originalbackend, " ".join(['"%s"' % a for a in arguments]))) |
| 671 | |
| 672 | pid = os.fork() |
| 673 | if pid == 0 : |
| 674 | if self.InputFile is None : |
| 675 | f=open(self.dataFile, "rb") |
| 676 | os.dup2(f.fileno(), 0) |
| 677 | f.close() |
| 678 | try : |
| 679 | os.execve(originalbackend,arguments,os.environ) |
| 680 | except OSError, msg : |
| 681 | self.logDebug("execve() failed: %s" % msg) |
| 682 | os._exit(-1) |
791 | | while (status == -1) and (not killed) and not (inputclosed and outputclosed) : |
792 | | # First check if original backend is still alive |
793 | | status = subprocess.poll() |
794 | | |
795 | | # Now if we got SIGTERM, we have |
796 | | # to kill -TERM the original backend |
797 | | if self.gotSigTerm and not killed : |
798 | | try : |
799 | | os.kill(subprocess.pid, signal.SIGTERM) |
800 | | except OSError, msg : # ignore but logs if process was already killed. |
801 | | self.logDebug("Error while sending signal to pid %s : %s" % (subprocess.pid, msg)) |
802 | | else : |
803 | | self.logInfo(_("SIGTERM was sent to original backend %s (PID %s)") % (originalbackend, subprocess.pid)) |
| 685 | while status == -1 : |
| 686 | try : |
| 687 | status = os.waitpid(pid, 0)[1] |
| 688 | except OSError, (err, msg) : |
| 689 | if err == 4 and self.gotSigTerm : |
| 690 | os.kill(pid, signal.SIGTERM) |
805 | | |
806 | | # In any case, deal with any remaining I/O |
807 | | try : |
808 | | availablefds = pollster.poll(5000) |
809 | | except select.error, msg : |
810 | | self.logDebug("Interrupted poll : %s" % msg) |
811 | | availablefds = [] |
812 | | if not availablefds : |
813 | | self.logDebug("Nothing to do, sleeping a bit...") |
814 | | time.sleep(0.01) # give some time to the system |
815 | | else : |
816 | | for (fd, mask) in availablefds : |
817 | | try : |
818 | | if mask & select.POLLOUT : |
819 | | # We can write |
820 | | if fd == tocfno : |
821 | | if indata : |
822 | | try : |
823 | | nbwritten = os.write(fd, indata) |
824 | | except (OSError, IOError), msg : |
825 | | self.logDebug("Error while writing to original backend's stdin %s : %s" % (fd, msg)) |
826 | | else : |
827 | | if len(indata) != nbwritten : |
828 | | self.logDebug("Short write to original backend's input !") |
829 | | totaltochild += nbwritten |
830 | | self.logDebug("%s bytes sent to original backend so far..." % totaltochild) |
831 | | indata = indata[nbwritten:] |
832 | | else : |
833 | | self.logDebug("No data to send to original backend yet, sleeping a bit...") |
834 | | time.sleep(0.01) |
835 | | |
836 | | if endinput : |
837 | | self.unregisterFileNo(pollster, tocfno) |
838 | | self.logDebug("Closing original backend's stdin.") |
839 | | os.close(tocfno) |
840 | | inputclosed = 1 |
841 | | elif fd == stderrfno : |
842 | | if outdata : |
843 | | try : |
844 | | nbwritten = os.write(fd, outdata) |
845 | | except (OSError, IOError), msg : |
846 | | self.logDebug("Error while writing to CUPS back channel (stderr) %s : %s" % (fd, msg)) |
847 | | else : |
848 | | if len(outdata) != nbwritten : |
849 | | self.logDebug("Short write to stderr (CUPS) !") |
850 | | totaltocups += nbwritten |
851 | | self.logDebug("%s bytes sent back to CUPS so far..." % totaltocups) |
852 | | outdata = outdata[nbwritten:] |
853 | | else : |
854 | | # self.logDebug("No data to send back to CUPS yet, sleeping a bit...") # Uncommenting this fills your logs |
855 | | time.sleep(0.01) # Give some time to the system, stderr is ALWAYS writeable it seems. |
856 | | |
857 | | if endoutput : |
858 | | self.unregisterFileNo(pollster, stderrfno) |
859 | | outputclosed = 1 |
860 | | else : |
861 | | self.logDebug("Unexpected : %s - Sleeping a bit..." % self.formatFileEvent(fd, mask)) |
862 | | time.sleep(0.01) |
863 | | |
864 | | if mask & (select.POLLIN | select.POLLPRI) : |
865 | | # We have something to read |
866 | | try : |
867 | | data = os.read(fd, MEGABYTE) |
868 | | except (IOError, OSError), msg : |
869 | | self.logDebug("Error while reading file %s : %s" % (fd, msg)) |
870 | | else : |
871 | | if fd == infno : |
872 | | if not data : # If yes, then no more input data |
873 | | self.unregisterFileNo(pollster, infno) |
874 | | self.logDebug("Input data ends.") |
875 | | endinput = 1 # this happens with real files. |
876 | | else : |
877 | | indata += data |
878 | | totalfromcups += len(data) |
879 | | self.logDebug("%s bytes read from CUPS so far..." % totalfromcups) |
880 | | elif fd == fromcfno : |
881 | | if not data : |
882 | | self.logDebug("No back channel data to read from original backend yet, sleeping a bit...") |
883 | | time.sleep(0.01) |
884 | | else : |
885 | | outdata += data |
886 | | totalfromchild += len(data) |
887 | | self.logDebug("%s bytes read from original backend so far..." % totalfromchild) |
888 | | else : |
889 | | self.logDebug("Unexpected : %s - Sleeping a bit..." % self.formatFileEvent(fd, mask)) |
890 | | time.sleep(0.01) |
891 | | |
892 | | if mask & (select.POLLHUP | select.POLLERR) : |
893 | | # Treat POLLERR as an EOF. |
894 | | # Some standard I/O stream has no more datas |
895 | | self.unregisterFileNo(pollster, fd) |
896 | | if fd == infno : |
897 | | # Here we are in the case where the input file is stdin. |
898 | | # which has no more data to be read. |
899 | | self.logDebug("Input data ends.") |
900 | | endinput = 1 |
901 | | elif fd == fromcfno : |
902 | | # We are no more interested in this file descriptor |
903 | | self.logDebug("Closing original backend's stdout+stderr.") |
904 | | os.close(fromcfno) |
905 | | endoutput = 1 |
906 | | else : |
907 | | self.logDebug("Unexpected : %s - Sleeping a bit..." % self.formatFileEvent(fd, mask)) |
908 | | time.sleep(0.01) |
909 | | |
910 | | if mask & select.POLLNVAL : |
911 | | self.logDebug("File %s was closed. Unregistering from polling object." % fd) |
912 | | self.unregisterFileNo(pollster, fd) |
913 | | except IOError, msg : |
914 | | self.logDebug("Got an IOError : %s" % msg) # we got signalled during an I/O |
915 | | |
916 | | # We must close the original backend's input stream |
917 | | if killed and not inputclosed : |
918 | | self.logDebug("Forcing close of original backend's stdin.") |
919 | | os.close(tocfno) |
920 | | |
921 | | self.logDebug("Exiting streams polling loop...") |
922 | | |
923 | | self.logDebug("input data's final length : %s" % len(indata)) |
924 | | self.logDebug("back-channel data's final length : %s" % len(outdata)) |
925 | | |
926 | | self.logDebug("Total bytes read from CUPS (job's datas) : %s" % totalfromcups) |
927 | | self.logDebug("Total bytes sent to original backend (job's datas) : %s" % totaltochild) |
928 | | |
929 | | self.logDebug("Total bytes read from original backend (back-channel datas) : %s" % totalfromchild) |
930 | | self.logDebug("Total bytes sent back to CUPS (back-channel datas) : %s" % totaltocups) |
931 | | |
932 | | # Check exit code of original CUPS backend. |
933 | | if status == -1 : |
934 | | # we exited the loop before the original backend exited |
935 | | # now we have to wait for it to finish and get its status |
936 | | self.logDebug("Waiting for original backend to exit...") |
937 | | try : |
938 | | status = subprocess.wait() |
939 | | except OSError : # already dead : TODO : detect when abnormal |
940 | | status = 0 |