1# -*- coding: UTF-8 -*-
2
3import re
4import sys
5import shutil
6
7from gi.repository import GObject
8
9from pychess.compat import create_task
10from pychess.System.Log import log
11from pychess.System.SubProcess import SubProcess
12
13
14class Pinger(GObject.GObject):
15    """ The received signal contains the time it took to get response from the
16        server in millisecconds. -1 means that some error occurred """
17
18    __gsignals__ = {
19        "received": (GObject.SignalFlags.RUN_FIRST, None, (float, )),
20        "error": (GObject.SignalFlags.RUN_FIRST, None, (str, ))
21    }
22
23    def __init__(self, host):
24        GObject.GObject.__init__(self)
25        self.host = host
26        self.subproc = None
27
28        self.expression = re.compile(r"=([\d\.]+) (m?s)")
29
30        # We need untranslated error messages in regexp search
31        # below, so have to use deferred translation here
32        def _(msg):
33            return msg
34
35        error = _("Destination Host Unreachable")
36        self.errorExprs = (re.compile("(%s)" % error), )
37        del _
38
39        self.restartsOnDead = 3
40        self.deadCount = 0
41
42    def start(self):
43        assert not self.subproc
44        if sys.platform == "win32":
45            args = ["-t", self.host]
46        else:
47            args = ["-i10", self.host]
48        self.subproc = SubProcess(shutil.which("ping"), args, env={"LANG": "en"})
49        create_task(self.subproc.start())
50        self.conid1 = self.subproc.connect("line", self.__handleLines)
51        self.conid2 = self.subproc.connect("died", self.__handleDead)
52
53    def __handleLines(self, subprocess, line):
54        match = self.expression.search(line)
55        if match:
56            time, unit = match.groups()
57            time = float(time)
58            if unit == "s":
59                time *= 1000
60            self.emit("received", time)
61        else:
62            for expr in self.errorExprs:
63                match = expr.search(line)
64                if match:
65                    msg = match.groups()[0]
66                    self.emit("error", _(msg))
67
68    def __handleDead(self, subprocess):
69        if self.deadCount < self.restartsOnDead:
70            log.warning("Pinger died and restarted (%d/%d)" %
71                        (self.deadCount + 1, self.restartsOnDead),
72                        extra={"task": self.subproc.defname})
73            self.stop()
74            self.start()
75            self.deadCount += 1
76        else:
77            self.emit("error", _("Died"))
78            self.stop()
79
80    def stop(self):
81        if not self.subproc:
82            return
83        # exitCode = self.subproc.gentleKill()
84        self.subproc.disconnect(self.conid1)
85        self.subproc.disconnect(self.conid2)
86        self.subproc.terminate()
87        self.subproc = None
88
89
90if __name__ == "__main__":
91    pinger = Pinger("google.com")
92
93    def callback(pinger, time):
94        print(time)
95
96    pinger.connect("received", callback)
97    pinger.start()
98    import time
99    time.sleep(5)
100    pinger.stop()
101    time.sleep(3)
102