pipetool.py revision 59a7b9f69b5b745e58c01288cb4f85fb535250b0
153e354dd0edd0d111fb7512f366c4764ffc2e681Phil#! /usr/bin/env python
253e354dd0edd0d111fb7512f366c4764ffc2e681Phil
353e354dd0edd0d111fb7512f366c4764ffc2e681Phil## This file is part of Scapy
453e354dd0edd0d111fb7512f366c4764ffc2e681Phil## See http://www.secdev.org/projects/scapy for more informations
553e354dd0edd0d111fb7512f366c4764ffc2e681Phil## Copyright (C) Philippe Biondi <phil@secdev.org>
653e354dd0edd0d111fb7512f366c4764ffc2e681Phil## This program is published under a GPLv2 license
753e354dd0edd0d111fb7512f366c4764ffc2e681Phil
859a7b9f69b5b745e58c01288cb4f85fb535250b0gpotterimport os
953e354dd0edd0d111fb7512f366c4764ffc2e681Philimport subprocess
1053e354dd0edd0d111fb7512f366c4764ffc2e681Philimport itertools
1153e354dd0edd0d111fb7512f366c4764ffc2e681Philimport collections
1253e354dd0edd0d111fb7512f366c4764ffc2e681Philimport time
1353e354dd0edd0d111fb7512f366c4764ffc2e681Philimport Queue
1459a7b9f69b5b745e58c01288cb4f85fb535250b0gpotterfrom threading import Lock, Thread
156057906368d55634d11e1d19a5cca1f127595b11Robin Jarry
16c8aedce97942cf1963c137902d50d14cd5c28bf0gpotterfrom scapy.automaton import Message, select_objects
17c8aedce97942cf1963c137902d50d14cd5c28bf0gpotterfrom scapy.consts import WINDOWS
182a54fe68cadcb2b75c9c31244499c2b21f385eb4gpotterfrom scapy.error import log_interactive, warning
196057906368d55634d11e1d19a5cca1f127595b11Robin Jarryfrom scapy.config import conf
20c8aedce97942cf1963c137902d50d14cd5c28bf0gpotterfrom scapy.utils import get_temp_file, do_graph
21c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter
22c8aedce97942cf1963c137902d50d14cd5c28bf0gpotterimport scapy.arch
2353e354dd0edd0d111fb7512f366c4764ffc2e681Phil
2453e354dd0edd0d111fb7512f366c4764ffc2e681Philclass PipeEngine:
2553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    pipes = {}
2653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    @classmethod
2753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def list_pipes(cls):
2853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        for pn,pc in sorted(cls.pipes.items()):
2953e354dd0edd0d111fb7512f366c4764ffc2e681Phil            doc = pc.__doc__ or ""
3053e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if doc:
3153e354dd0edd0d111fb7512f366c4764ffc2e681Phil                doc = doc.splitlines()[0]
3253e354dd0edd0d111fb7512f366c4764ffc2e681Phil            print "%20s: %s" % (pn, doc)
3353e354dd0edd0d111fb7512f366c4764ffc2e681Phil    @classmethod
3453e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def list_pipes_detailed(cls):
3553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        for pn,pc in sorted(cls.pipes.items()):
3653e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if pc.__doc__:
3753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                print "###### %s\n %s" % (pn ,pc.__doc__)
3853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            else:
3953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                print "###### %s" % pn
4053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
4153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, *pipes):
4253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.active_pipes = set()
4353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.active_sources = set()
4453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.active_drains = set()
4553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.active_sinks = set()
4653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._add_pipes(*pipes)
4759a7b9f69b5b745e58c01288cb4f85fb535250b0gpotter        self.thread_lock = Lock()
4859a7b9f69b5b745e58c01288cb4f85fb535250b0gpotter        self.command_lock = Lock()
49c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        self.__fd_queue = []
5053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.__fdr,self.__fdw = os.pipe()
5153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.threadid = None
5253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __getattr__(self, attr):
5353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if attr.startswith("spawn_"):
5453e354dd0edd0d111fb7512f366c4764ffc2e681Phil            dname = attr[6:]
5553e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if dname in self.pipes:
5653e354dd0edd0d111fb7512f366c4764ffc2e681Phil                def f(*args, **kargs):
5753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    k = self.pipes[dname]
5853e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    p = k(*args, **kargs)
5953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    self.add(p)
6053e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    return p
6153e354dd0edd0d111fb7512f366c4764ffc2e681Phil                return f
6253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        raise AttributeError(attr)
6353e354dd0edd0d111fb7512f366c4764ffc2e681Phil
64c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter    def checkRecv(self):
65c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        return len(self.__fd_queue) > 0
66c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter
67c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter    def fileno(self):
68c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        return self.__fdr
69c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter
70c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter    def _read_cmd(self):
71c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        self.__fd_queue.pop()
72c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        return os.read(self.__fdr,1)
73c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter
74c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter    def _write_cmd(self, _cmd):
75c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        os.write(self.__fdw, _cmd)
76c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        self.__fd_queue.append("X")
77c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter
7853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def add_one_pipe(self, pipe):
7953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.active_pipes.add(pipe)
8053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if isinstance(pipe, Source):
8153e354dd0edd0d111fb7512f366c4764ffc2e681Phil            self.active_sources.add(pipe)
8253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if isinstance(pipe, Drain):
8353e354dd0edd0d111fb7512f366c4764ffc2e681Phil            self.active_drains.add(pipe)
8453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if isinstance(pipe, Sink):
8553e354dd0edd0d111fb7512f366c4764ffc2e681Phil            self.active_sinks.add(pipe)
8653e354dd0edd0d111fb7512f366c4764ffc2e681Phil
8753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def get_pipe_list(self, pipe):
8853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        def flatten(p, l):
8953e354dd0edd0d111fb7512f366c4764ffc2e681Phil            l.add(p)
9053e354dd0edd0d111fb7512f366c4764ffc2e681Phil            for q in p.sources|p.sinks|p.high_sources|p.high_sinks:
9153e354dd0edd0d111fb7512f366c4764ffc2e681Phil                if q not in l:
9253e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    flatten(q, l)
93c277f8948afe9d19703168fca385d2fa6df67657Phil        pl = set()
9453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        flatten(pipe, pl)
9553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return pl
9653e354dd0edd0d111fb7512f366c4764ffc2e681Phil
9753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def _add_pipes(self, *pipes):
98c277f8948afe9d19703168fca385d2fa6df67657Phil        pl = set()
9953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        for p in pipes:
100c277f8948afe9d19703168fca385d2fa6df67657Phil            pl |= self.get_pipe_list(p)
101c277f8948afe9d19703168fca385d2fa6df67657Phil        pl -= self.active_pipes
102c277f8948afe9d19703168fca385d2fa6df67657Phil        for q in pl:
103c277f8948afe9d19703168fca385d2fa6df67657Phil            self.add_one_pipe(q)
104c277f8948afe9d19703168fca385d2fa6df67657Phil        return pl
10553e354dd0edd0d111fb7512f366c4764ffc2e681Phil
10653e354dd0edd0d111fb7512f366c4764ffc2e681Phil
10753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def run(self):
10853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        log_interactive.info("Pipe engine thread started.")
10953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        try:
11053e354dd0edd0d111fb7512f366c4764ffc2e681Phil            for p in self.active_pipes:
11153e354dd0edd0d111fb7512f366c4764ffc2e681Phil                p.start()
11253e354dd0edd0d111fb7512f366c4764ffc2e681Phil            sources = self.active_sources
113c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            sources.add(self)
11453e354dd0edd0d111fb7512f366c4764ffc2e681Phil            exhausted = set([])
11553e354dd0edd0d111fb7512f366c4764ffc2e681Phil            RUN=True
11653e354dd0edd0d111fb7512f366c4764ffc2e681Phil            STOP_IF_EXHAUSTED = False
11753e354dd0edd0d111fb7512f366c4764ffc2e681Phil            while RUN and (not STOP_IF_EXHAUSTED or len(sources) > 1):
118c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                fds = select_objects(sources, 2, customTypes=(AutoSource, PipeEngine))
11953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                for fd in fds:
120c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                    if fd is self:
121c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                        cmd = self._read_cmd()
12253e354dd0edd0d111fb7512f366c4764ffc2e681Phil                        if cmd == "X":
12353e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            RUN=False
12453e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            break
12553e354dd0edd0d111fb7512f366c4764ffc2e681Phil                        elif cmd == "B":
12653e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            STOP_IF_EXHAUSTED = True
12753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                        elif cmd == "A":
12853e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            sources = self.active_sources-exhausted
12953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            sources.add(self.__fdr)
13053e354dd0edd0d111fb7512f366c4764ffc2e681Phil                        else:
13153e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            warning("Unknown internal pipe engine command: %r. Ignoring." % cmd)
13253e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    elif fd in sources:
13353e354dd0edd0d111fb7512f366c4764ffc2e681Phil                        try:
13453e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            fd.deliver()
13553e354dd0edd0d111fb7512f366c4764ffc2e681Phil                        except Exception,e:
13653e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            log_interactive.exception("piping from %s failed: %s" % (fd.name, e))
13753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                        else:
13853e354dd0edd0d111fb7512f366c4764ffc2e681Phil                            if fd.exhausted():
13953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                                exhausted.add(fd)
14053e354dd0edd0d111fb7512f366c4764ffc2e681Phil                                sources.remove(fd)
14153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        except KeyboardInterrupt:
14253e354dd0edd0d111fb7512f366c4764ffc2e681Phil            pass
14353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        finally:
14453e354dd0edd0d111fb7512f366c4764ffc2e681Phil            try:
14553e354dd0edd0d111fb7512f366c4764ffc2e681Phil                for p in self.active_pipes:
14653e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    p.stop()
14753e354dd0edd0d111fb7512f366c4764ffc2e681Phil            finally:
14853e354dd0edd0d111fb7512f366c4764ffc2e681Phil                self.thread_lock.release()
14953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                log_interactive.info("Pipe engine thread stopped.")
15053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
15153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def start(self):
15253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if self.thread_lock.acquire(0):
15359a7b9f69b5b745e58c01288cb4f85fb535250b0gpotter            _t = Thread(target=self.run)
15459a7b9f69b5b745e58c01288cb4f85fb535250b0gpotter            _t.start()
15559a7b9f69b5b745e58c01288cb4f85fb535250b0gpotter            self.threadid = _t.ident
15653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        else:
15753e354dd0edd0d111fb7512f366c4764ffc2e681Phil            warning("Pipe engine already running")
15853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def wait_and_stop(self):
15953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.stop(_cmd="B")
16053e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def stop(self, _cmd="X"):
16153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        try:
16253e354dd0edd0d111fb7512f366c4764ffc2e681Phil            with self.command_lock:
16353e354dd0edd0d111fb7512f366c4764ffc2e681Phil                if self.threadid is not None:
164c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                    self._write_cmd(_cmd)
16553e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    while not self.thread_lock.acquire(0):
16653e354dd0edd0d111fb7512f366c4764ffc2e681Phil                        time.sleep(0.01) # interruptible wait for thread to terminate
16753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    self.thread_lock.release() # (not using .join() because it needs 'threading' module)
16853e354dd0edd0d111fb7512f366c4764ffc2e681Phil                else:
16953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    warning("Pipe engine thread not running")
17053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        except KeyboardInterrupt:
17153e354dd0edd0d111fb7512f366c4764ffc2e681Phil            print "Interrupted by user."
17253e354dd0edd0d111fb7512f366c4764ffc2e681Phil
17353e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def add(self, *pipes):
174c277f8948afe9d19703168fca385d2fa6df67657Phil        pipes = self._add_pipes(*pipes)
17553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        with self.command_lock:
17653e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if self.threadid is not None:
17753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                for p in pipes:
17853e354dd0edd0d111fb7512f366c4764ffc2e681Phil                    p.start()
179c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                self._write_cmd("A")
18053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
18153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def graph(self,**kargs):
18253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        g=['digraph "pipe" {',"\tnode [shape=rectangle];",]
18353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        for p in self.active_pipes:
18453e354dd0edd0d111fb7512f366c4764ffc2e681Phil            g.append('\t"%i" [label="%s"];' % (id(p), p.name))
18553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        g.append("")
18653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        g.append("\tedge [color=blue, arrowhead=vee];")
18753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        for p in self.active_pipes:
18853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            for q in p.sinks:
18953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                g.append('\t"%i" -> "%i";' % (id(p), id(q)))
19053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        g.append("")
191dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        g.append("\tedge [color=purple, arrowhead=veevee];")
19253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        for p in self.active_pipes:
19353e354dd0edd0d111fb7512f366c4764ffc2e681Phil            for q in p.high_sinks:
194dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil                g.append('\t"%i" -> "%i";' % (id(p), id(q)))
195dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        g.append("")
196dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        g.append("\tedge [color=red, arrowhead=diamond];")
197dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        for p in self.active_pipes:
198dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil            for q in p.trigger_sinks:
199dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil                g.append('\t"%i" -> "%i";' % (id(p), id(q)))
20053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        g.append('}')
20153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        graph = "\n".join(g)
202c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        do_graph(graph, **kargs)
20353e354dd0edd0d111fb7512f366c4764ffc2e681Phil
20453e354dd0edd0d111fb7512f366c4764ffc2e681Phil
20553e354dd0edd0d111fb7512f366c4764ffc2e681Philclass _ConnectorLogic(object):
20653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self):
20753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.sources = set()
20853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.sinks = set()
20953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.high_sources = set()
21053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.high_sinks = set()
211dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        self.trigger_sources = set()
212dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        self.trigger_sinks = set()
21353e354dd0edd0d111fb7512f366c4764ffc2e681Phil
21453e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __lt__(self, other):
21553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        other.sinks.add(self)
21653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.sources.add(other)
21753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return other
21853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __gt__(self, other):
21953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.sinks.add(other)
22053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        other.sources.add(self)
22153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return other
22253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __eq__(self, other):
22353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self > other
22453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        other > self
22553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return other
22653e354dd0edd0d111fb7512f366c4764ffc2e681Phil
22753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __lshift__(self, other):
22853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.high_sources.add(other)
22953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        other.high_sinks.add(self)
23053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return other
23153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __rshift__(self, other):
23253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.high_sinks.add(other)
23353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        other.high_sources.add(self)
23453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return other
23553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __floordiv__(self, other):
23653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self >> other
23753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        other >> self
23853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return other
23953e354dd0edd0d111fb7512f366c4764ffc2e681Phil
240dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil    def __xor__(self, other):
241dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        self.trigger_sinks.add(other)
242dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        other.trigger_sources.add(self)
243dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        return other
24453e354dd0edd0d111fb7512f366c4764ffc2e681Phil
24553e354dd0edd0d111fb7512f366c4764ffc2e681Philclass Pipe(_ConnectorLogic):
24653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    class __metaclass__(type):
24753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        def __new__(cls, name, bases, dct):
24853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            c = type.__new__(cls, name, bases, dct)
24953e354dd0edd0d111fb7512f366c4764ffc2e681Phil            PipeEngine.pipes[name] = c
25053e354dd0edd0d111fb7512f366c4764ffc2e681Phil            return c
25153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, name=None):
25253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        _ConnectorLogic.__init__(self)
25353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if name is None:
25453e354dd0edd0d111fb7512f366c4764ffc2e681Phil            name = "%s" % (self.__class__.__name__)
25553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.name = name
25653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def _send(self, msg):
25753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        for s in self.sinks:
25853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s.push(msg)
25953e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def _high_send(self, msg):
26053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        for s in self.high_sinks:
26153e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s.high_push(msg)
262f1288d41516059cab24e840f0d8cd2d1bf7735e0phil    def _trigger(self, msg=None):
263dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        for s in self.trigger_sinks:
264dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil            s.on_trigger(msg)
26553e354dd0edd0d111fb7512f366c4764ffc2e681Phil
26653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __repr__(self):
26753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        ct = conf.color_theme
26853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        s = "%s%s" % (ct.punct("<"), ct.layer_name(self.name))
26953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if self.sources or self.sinks:
27053e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s+= " %s" % ct.punct("[")
27153e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if self.sources:
27253e354dd0edd0d111fb7512f366c4764ffc2e681Phil                s+="%s%s" %  (ct.punct(",").join(ct.field_name(s.name) for s in self.sources),
27353e354dd0edd0d111fb7512f366c4764ffc2e681Phil                              ct.field_value(">"))
27453e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s += ct.layer_name("#")
27553e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if self.sinks:
27653e354dd0edd0d111fb7512f366c4764ffc2e681Phil                s+="%s%s" % (ct.field_value(">"),
27753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                             ct.punct(",").join(ct.field_name(s.name) for s in self.sinks))
27853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s += ct.punct("]")
27953e354dd0edd0d111fb7512f366c4764ffc2e681Phil
28053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if self.high_sources or self.high_sinks:
28153e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s+= " %s" % ct.punct("[")
28253e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if self.high_sources:
28353e354dd0edd0d111fb7512f366c4764ffc2e681Phil                s+="%s%s" %  (ct.punct(",").join(ct.field_name(s.name) for s in self.high_sources),
28453e354dd0edd0d111fb7512f366c4764ffc2e681Phil                              ct.field_value(">>"))
28553e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s += ct.layer_name("#")
28653e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if self.high_sinks:
28753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                s+="%s%s" % (ct.field_value(">>"),
28853e354dd0edd0d111fb7512f366c4764ffc2e681Phil                             ct.punct(",").join(ct.field_name(s.name) for s in self.high_sinks))
28953e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s += ct.punct("]")
29053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
291dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil        if self.trigger_sources or self.trigger_sinks:
292dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil            s+= " %s" % ct.punct("[")
293dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil            if self.trigger_sources:
294dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil                s+="%s%s" %  (ct.punct(",").join(ct.field_name(s.name) for s in self.trigger_sources),
295dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil                              ct.field_value("^"))
296dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil            s += ct.layer_name("#")
297dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil            if self.trigger_sinks:
298dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil                s+="%s%s" % (ct.field_value("^"),
299dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil                             ct.punct(",").join(ct.field_name(s.name) for s in self.trigger_sinks))
300dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil            s += ct.punct("]")
301dbabf910cf2ddd7e420190267dd0c2a7abc2dfb1phil
30253e354dd0edd0d111fb7512f366c4764ffc2e681Phil
30353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        s += ct.punct(">")
30453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return s
30553e354dd0edd0d111fb7512f366c4764ffc2e681Phil
30653e354dd0edd0d111fb7512f366c4764ffc2e681Philclass Source(Pipe):
30753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, name=None):
30853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        Pipe.__init__(self, name=name)
30953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.is_exhausted = False
31053e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def _read_message(self):
31153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return Message()
31253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def deliver(self):
31353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        msg = self._read_message
31453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._send(msg)
31553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def fileno(self):
31653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return None
31753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def exhausted(self):
31853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return self.is_exhausted
31953e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def start(self):
32053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
32153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def stop(self):
32253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
32353e354dd0edd0d111fb7512f366c4764ffc2e681Phil
32453e354dd0edd0d111fb7512f366c4764ffc2e681Philclass Drain(Pipe):
32553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Repeat messages from low/high entries to (resp.) low/high exits
32653e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
32753e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|-------|->>
32853e354dd0edd0d111fb7512f366c4764ffc2e681Phil     |       |
32953e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|-------|->
33053e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
33153e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
33253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
33353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._send(msg)
33453e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
33553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._high_send(msg)
33653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def start(self):
33753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
33853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def stop(self):
33953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
34053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
34153e354dd0edd0d111fb7512f366c4764ffc2e681Philclass Sink(Pipe):
34253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
34353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
34453e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
34553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
34653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def start(self):
34753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
34853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def stop(self):
34953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
35053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
35153e354dd0edd0d111fb7512f366c4764ffc2e681Phil
35253e354dd0edd0d111fb7512f366c4764ffc2e681Philclass AutoSource(Source):
35353e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, name=None):
35453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        Source.__init__(self, name=name)
35553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.__fdr,self.__fdw = os.pipe()
35653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._queue = collections.deque()
35753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def fileno(self):
35853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        return self.__fdr
359c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter    def checkRecv(self):
360c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        return len(self._queue) > 0
36153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def _gen_data(self, msg):
36253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._queue.append((msg,False))
36353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._wake_up()
36453e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def _gen_high_data(self, msg):
36553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._queue.append((msg,True))
36653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._wake_up()
36753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def _wake_up(self):
36853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        os.write(self.__fdw,"x")
36953e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def deliver(self):
37053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        os.read(self.__fdr,1)
37153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        try:
37253e354dd0edd0d111fb7512f366c4764ffc2e681Phil            msg,high = self._queue.popleft()
37353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        except IndexError: #empty queue. Exhausted source
37453e354dd0edd0d111fb7512f366c4764ffc2e681Phil            pass
37553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        else:
37653e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if high:
37753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                self._high_send(msg)
37853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            else:
37953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                self._send(msg)
38053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
38153e354dd0edd0d111fb7512f366c4764ffc2e681Philclass ThreadGenSource(AutoSource):
38253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, name=None):
38353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        AutoSource.__init__(self, name=name)
38453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.RUN = False
38553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def generate(self):
38653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
38753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def start(self):
38853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.RUN = True
38959a7b9f69b5b745e58c01288cb4f85fb535250b0gpotter        Thread(target=self.generate).start()
39053e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def stop(self):
39153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.RUN = False
39253e354dd0edd0d111fb7512f366c4764ffc2e681Phil
39353e354dd0edd0d111fb7512f366c4764ffc2e681Phil
39453e354dd0edd0d111fb7512f366c4764ffc2e681Phil
39553e354dd0edd0d111fb7512f366c4764ffc2e681Philclass ConsoleSink(Sink):
39653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Print messages on low and high entries
39753e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
39853e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|--.    |->>
39953e354dd0edd0d111fb7512f366c4764ffc2e681Phil     | print |
40053e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|--'    |->
40153e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
40253e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
40353e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
40453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        print ">%r" % msg
40553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
40653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        print ">>%r" % msg
40753e354dd0edd0d111fb7512f366c4764ffc2e681Phil
40853e354dd0edd0d111fb7512f366c4764ffc2e681Philclass RawConsoleSink(Sink):
40953e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Print messages on low and high entries
41053e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
41153e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|--.    |->>
41253e354dd0edd0d111fb7512f366c4764ffc2e681Phil     | write |
41353e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|--'    |->
41453e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
41553e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
41653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, name=None, newlines=True):
41753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        Sink.__init__(self, name=name)
41853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.newlines = newlines
41953e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
42053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if self.newlines:
42153e354dd0edd0d111fb7512f366c4764ffc2e681Phil            msg += "\n"
42253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        os.write(1, str(msg))
42353e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
42453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if self.newlines:
42553e354dd0edd0d111fb7512f366c4764ffc2e681Phil            msg += "\n"
42653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        os.write(1, str(msg))
42753e354dd0edd0d111fb7512f366c4764ffc2e681Phil
42853e354dd0edd0d111fb7512f366c4764ffc2e681Philclass CLIFeeder(AutoSource):
42953e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Send messages from python command line
43053e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +--------+
43153e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|        |->>
43253e354dd0edd0d111fb7512f366c4764ffc2e681Phil     | send() |
43353e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|   `----|->
43453e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +--------+
43553e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
43653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def send(self, msg):
43753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._gen_data(msg)
43853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def close(self):
43953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.is_exhausted = True
44053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
44153e354dd0edd0d111fb7512f366c4764ffc2e681Philclass CLIHighFeeder(CLIFeeder):
44253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Send messages from python command line to high output
44353e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +--------+
44453e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|   .----|->>
44553e354dd0edd0d111fb7512f366c4764ffc2e681Phil     | send() |
44653e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|        |->
44753e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +--------+
44853e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
44953e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def send(self, msg):
45053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._gen_high_data(msg)
45153e354dd0edd0d111fb7512f366c4764ffc2e681Phil
45253e354dd0edd0d111fb7512f366c4764ffc2e681Phil
45353e354dd0edd0d111fb7512f366c4764ffc2e681Philclass PeriodicSource(ThreadGenSource):
45453e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Generage messages periodically on low exit
45553e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
45653e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|       |->>
45753e354dd0edd0d111fb7512f366c4764ffc2e681Phil     | msg,T |
45853e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|  `----|->
45953e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
46053e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
46153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, msg, period, period2=0, name=None):
46253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        ThreadGenSource.__init__(self,name=name)
46353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if not hasattr(msg, "__iter__"):
46453e354dd0edd0d111fb7512f366c4764ffc2e681Phil            msg=[msg]
46553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.msg = msg
46653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.period = period
46753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.period2 = period2
46853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def generate(self):
46953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        while self.RUN:
47053e354dd0edd0d111fb7512f366c4764ffc2e681Phil            empty_gen = True
47153e354dd0edd0d111fb7512f366c4764ffc2e681Phil            for m in self.msg:
47253e354dd0edd0d111fb7512f366c4764ffc2e681Phil                empty_gen = False
47353e354dd0edd0d111fb7512f366c4764ffc2e681Phil                self._gen_data(m)
47453e354dd0edd0d111fb7512f366c4764ffc2e681Phil                time.sleep(self.period)
47553e354dd0edd0d111fb7512f366c4764ffc2e681Phil            if empty_gen:
47653e354dd0edd0d111fb7512f366c4764ffc2e681Phil                self.is_exhausted = True
47753e354dd0edd0d111fb7512f366c4764ffc2e681Phil                self._wake_up()
47853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            time.sleep(self.period2)
47953e354dd0edd0d111fb7512f366c4764ffc2e681Phil
48053e354dd0edd0d111fb7512f366c4764ffc2e681Philclass TermSink(Sink):
48153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Print messages on low and high entries on a separate terminal
48253e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
48353e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|--.    |->>
48453e354dd0edd0d111fb7512f366c4764ffc2e681Phil     | print |
48553e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|--'    |->
48653e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
48753e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
48853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, name=None, keepterm=True, newlines=True, openearly=True):
48953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        Sink.__init__(self, name=name)
49053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.keepterm = keepterm
49153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.newlines = newlines
49253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.openearly = openearly
49353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.opened = False
49453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if self.openearly:
49553e354dd0edd0d111fb7512f366c4764ffc2e681Phil            self.start()
49653e354dd0edd0d111fb7512f366c4764ffc2e681Phil
49753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def start(self):
49853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if not self.opened:
49953e354dd0edd0d111fb7512f366c4764ffc2e681Phil            self.opened = True
500c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            if WINDOWS:
501c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                self.__f = get_temp_file()
502c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                self.name = "Scapy" if self.name is None else self.name
503c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                # Start a powershell in a new window and print the PID
504c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                cmd = "$app = Start-Process PowerShell -ArgumentList '-command &{$host.ui.RawUI.WindowTitle=\\\"%s\\\";Get-Content \\\"%s\\\" -wait}' -passthru; echo $app.Id" % (self.name, self.__f.replace("\\", "\\\\"))
505c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                print([conf.prog.powershell, cmd])
506c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                _p = subprocess.Popen([conf.prog.powershell, cmd], stdout=subprocess.PIPE)
507c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                _output, _stderr = _p.communicate()
508c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                # This is the process PID
509c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                self.__p = int(_output)
510c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                print("PID:" + str(self.__p))
511c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            else:
512c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                self.__r,self.__w = os.pipe()
513c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                cmd = ["xterm"]
514c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                if self.name is not None:
515c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                    cmd.extend(["-title",self.name])
516c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                if self.keepterm:
517c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                    cmd.append("-hold")
518c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                cmd.extend(["-e", "cat 0<&%i" % self.__r])
519c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                self.__p = subprocess.Popen(cmd, shell=True, executable="/bin/bash")
520c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                os.close(self.__r)
52153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def stop(self):
52253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if not self.keepterm:
52353e354dd0edd0d111fb7512f366c4764ffc2e681Phil            self.opened = False
524c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            if not WINDOWS:
525c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                os.close(self.__w)
526c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                self.__p.kill()
527c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                self.__p.wait()
528c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            else:
529c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                # Recipe to kill process with PID
530c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                # http://code.activestate.com/recipes/347462-terminating-a-subprocess-on-windows/
531c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                import ctypes
532c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                PROCESS_TERMINATE = 1
533c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.__p)
534c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                ctypes.windll.kernel32.TerminateProcess(handle, -1)
535c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter                ctypes.windll.kernel32.CloseHandle(handle)
53653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def _print(self, s):
53753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        if self.newlines:
53853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            s+="\n"
539c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        if WINDOWS:
540c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            self.__w = open(self.__f, "a")
541c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            self.__w.write(s)
542c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            self.__w.close()
543c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter        else:
544c8aedce97942cf1963c137902d50d14cd5c28bf0gpotter            os.write(self.__w, s)
54553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
54653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._print(str(msg))
54753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
54853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._print(str(msg))
54953e354dd0edd0d111fb7512f366c4764ffc2e681Phil
55053e354dd0edd0d111fb7512f366c4764ffc2e681Phil
55153e354dd0edd0d111fb7512f366c4764ffc2e681Philclass QueueSink(Sink):
55253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Collect messages from high and low entries and queue them. Messages are unqueued with the .recv() method.
55353e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
55453e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|--.    |->>
55553e354dd0edd0d111fb7512f366c4764ffc2e681Phil     | queue |
55653e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|--'    |->
55753e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
55853e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
55953e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, name=None):
56053e354dd0edd0d111fb7512f366c4764ffc2e681Phil        Sink.__init__(self, name=name)
56153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.q = Queue.Queue()
56253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
56353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.q.put(msg)
56453e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
56553e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.q.put(msg)
56653e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def recv(self):
56753e354dd0edd0d111fb7512f366c4764ffc2e681Phil        while True:
56853e354dd0edd0d111fb7512f366c4764ffc2e681Phil            try:
56953e354dd0edd0d111fb7512f366c4764ffc2e681Phil                return self.q.get(True, timeout=0.1)
57053e354dd0edd0d111fb7512f366c4764ffc2e681Phil            except Queue.Empty:
57153e354dd0edd0d111fb7512f366c4764ffc2e681Phil                pass
57253e354dd0edd0d111fb7512f366c4764ffc2e681Phil
57353e354dd0edd0d111fb7512f366c4764ffc2e681Phil
57453e354dd0edd0d111fb7512f366c4764ffc2e681Philclass TransformDrain(Drain):
57553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Apply a function to messages on low and high entry
57653e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
57753e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|--[f]--|->>
57853e354dd0edd0d111fb7512f366c4764ffc2e681Phil     |       |
57953e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|--[f]--|->
58053e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
58153e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
58253e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def __init__(self, f, name=None):
58353e354dd0edd0d111fb7512f366c4764ffc2e681Phil        Drain.__init__(self, name=name)
58453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self.f = f
58553e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
58653e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._send(self.f(msg))
58753e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
58853e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._high_send(self.f(msg))
58953e354dd0edd0d111fb7512f366c4764ffc2e681Phil
59053e354dd0edd0d111fb7512f366c4764ffc2e681Philclass UpDrain(Drain):
59153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Repeat messages from low entry to high exit
59253e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
59353e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|    ,--|->>
59453e354dd0edd0d111fb7512f366c4764ffc2e681Phil     |   /   |
59553e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|--'    |->
59653e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
59753e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
59853e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
59953e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._high_send(msg)
60053e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
60153e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
60253e354dd0edd0d111fb7512f366c4764ffc2e681Phil
60353e354dd0edd0d111fb7512f366c4764ffc2e681Philclass DownDrain(Drain):
60453e354dd0edd0d111fb7512f366c4764ffc2e681Phil    """Repeat messages from high entry to low exit
60553e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
60653e354dd0edd0d111fb7512f366c4764ffc2e681Phil  >>-|--.    |->>
60753e354dd0edd0d111fb7512f366c4764ffc2e681Phil     |   \   |
60853e354dd0edd0d111fb7512f366c4764ffc2e681Phil   >-|    `--|->
60953e354dd0edd0d111fb7512f366c4764ffc2e681Phil     +-------+
61053e354dd0edd0d111fb7512f366c4764ffc2e681Phil"""
61153e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def push(self, msg):
61253e354dd0edd0d111fb7512f366c4764ffc2e681Phil        pass
61353e354dd0edd0d111fb7512f366c4764ffc2e681Phil    def high_push(self, msg):
61453e354dd0edd0d111fb7512f366c4764ffc2e681Phil        self._send(msg)
615