1 | #!/usr/bin/env python |
||
2 | # coding: utf-8 |
||
3 | import logging |
||
4 | import os |
||
5 | import socket |
||
6 | |||
7 | __version__ = '0.1' |
||
8 | |||
9 | log = logging.getLogger(__name__) |
||
10 | |||
11 | |||
12 | class GrimReaper(object): |
||
13 | def __init__(self, socket_path='/tmp/GrimReaper.socket', process_timeout=30): |
||
14 | self._path = socket_path |
||
15 | self._process_timeout = process_timeout |
||
16 | self._connect() |
||
17 | |||
18 | def _is_connected(self): |
||
19 | try: |
||
20 | self._socket.recv(0) |
||
21 | except socket.error as e: |
||
22 | if e.errno == socket.errno.EAGAIN: |
||
23 | return True |
||
24 | else: |
||
25 | log.debug(e) |
||
26 | return self._connect() |
||
27 | else: |
||
28 | log.debug('Connection is active') |
||
29 | return True |
||
30 | |||
31 | def _connect(self): |
||
32 | self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
||
33 | self._socket.setblocking(0) |
||
34 | |||
35 | try: |
||
36 | self._socket.connect(self._path) |
||
37 | except socket.error as e: |
||
38 | # Transport endpoint is already connected |
||
39 | if e.errno == 106: |
||
40 | return True |
||
41 | |||
42 | log.error('Unable to connect to the socket "%s": %s', self._path, e) |
||
43 | self.on_connection_error(e) |
||
44 | |||
45 | if e.errno == socket.errno.ENOENT: |
||
46 | # No such file or directory |
||
47 | return False |
||
48 | elif e.errno == socket.errno.ECONNREFUSED: |
||
49 | # Connection refused |
||
50 | return False |
||
51 | elif e.errno == socket.errno.EPIPE: |
||
52 | # Broken pipe |
||
53 | return False |
||
54 | |||
55 | raise |
||
56 | return True |
||
57 | |||
58 | def _close(self): |
||
59 | self._socket.close() |
||
60 | |||
61 | View Code Duplication | def register(self, timeout=None, pid=None): |
|
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
62 | if timeout is None: |
||
63 | timeout = self._process_timeout |
||
64 | |||
65 | if pid is None: |
||
66 | pid = os.getpid() |
||
67 | |||
68 | if self._is_connected(): |
||
69 | msg = 'register:%s:%s' % (pid, timeout) |
||
70 | try: |
||
71 | self._socket.sendall(msg.encode('utf-8')) |
||
72 | except socket.error as e: |
||
73 | if e.errno == socket.errno.EPIPE: |
||
74 | self._close() |
||
75 | return |
||
76 | log.debug(e) |
||
77 | |||
78 | log.debug('Registered process (PID=%s; timeout=%s)', pid, timeout) |
||
79 | else: |
||
80 | log.warning('Unable to register the process (PID=%s).', pid) |
||
81 | |||
82 | View Code Duplication | def unregister(self, pid=None): |
|
0 ignored issues
–
show
|
|||
83 | if pid is None: |
||
84 | pid = os.getpid() |
||
85 | |||
86 | if self._is_connected(): |
||
87 | msg = 'unregister:%s' % pid |
||
88 | try: |
||
89 | self._socket.sendall(msg.encode('utf-8')) |
||
90 | except socket.error as e: |
||
91 | if e.errno == socket.errno.EPIPE: |
||
92 | self._close() |
||
93 | return |
||
94 | log.debug(e) |
||
95 | |||
96 | log.debug('Unregistered process (PID=%s)', pid) |
||
97 | else: |
||
98 | log.warning('Unable to unregister the process (PID=%s).', pid) |
||
99 | |||
100 | def on_connection_error(self, exc): |
||
101 | pass |
||
102 |