|
|
@ -24,140 +24,72 @@ import threading |
|
|
|
import traceback |
|
|
|
import json |
|
|
|
import Queue |
|
|
|
|
|
|
|
import util |
|
|
|
from network import Network |
|
|
|
from util import print_error, print_stderr, parse_json |
|
|
|
from simple_config import SimpleConfig |
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
|
The Network object is not aware of clients/subscribers |
|
|
|
It only does subscribe/unsubscribe to addresses |
|
|
|
Which client has wich address is managed by the daemon |
|
|
|
Network also reports status changes |
|
|
|
""" |
|
|
|
|
|
|
|
DAEMON_PORT=8001 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ClientThread(threading.Thread): |
|
|
|
# read messages from client (socket), and sends them to Network |
|
|
|
# responses are sent back on the same socket |
|
|
|
|
|
|
|
def __init__(self, server, network, s): |
|
|
|
def __init__(self, server, s): |
|
|
|
threading.Thread.__init__(self) |
|
|
|
self.server = server |
|
|
|
self.daemon = True |
|
|
|
self.s = s |
|
|
|
self.s.settimeout(0.1) |
|
|
|
self.network = network |
|
|
|
self.queue = Queue.Queue() |
|
|
|
self.unanswered_requests = {} |
|
|
|
self.debug = False |
|
|
|
self.client_pipe = util.SocketPipe(s) |
|
|
|
self.daemon_pipe = util.QueuePipe(send_queue = self.server.network.requests_queue) |
|
|
|
self.server.add_client(self) |
|
|
|
|
|
|
|
|
|
|
|
def run(self): |
|
|
|
|
|
|
|
message = '' |
|
|
|
while True: |
|
|
|
try: |
|
|
|
self.send_responses() |
|
|
|
except socket.error: |
|
|
|
break |
|
|
|
|
|
|
|
def reading_thread(self): |
|
|
|
while self.running: |
|
|
|
try: |
|
|
|
data = self.s.recv(1024) |
|
|
|
except socket.timeout: |
|
|
|
request = self.client_pipe.get() |
|
|
|
except util.timeout: |
|
|
|
continue |
|
|
|
except: |
|
|
|
data = '' |
|
|
|
if not data: |
|
|
|
if request is None: |
|
|
|
self.running = False |
|
|
|
break |
|
|
|
message += data |
|
|
|
while True: |
|
|
|
cmd, message = parse_json(message) |
|
|
|
if not cmd: |
|
|
|
break |
|
|
|
self.process(cmd) |
|
|
|
|
|
|
|
self.server.remove_client(self) |
|
|
|
|
|
|
|
if request.get('method') == 'daemon.stop': |
|
|
|
self.server.stop() |
|
|
|
continue |
|
|
|
|
|
|
|
self.daemon_pipe.send(request) |
|
|
|
|
|
|
|
|
|
|
|
def process(self, request): |
|
|
|
if self.debug: |
|
|
|
print_error("<--", request) |
|
|
|
method = request['method'] |
|
|
|
params = request['params'] |
|
|
|
_id = request['id'] |
|
|
|
|
|
|
|
if method == ('daemon.stop'): |
|
|
|
self.server.stop() |
|
|
|
return |
|
|
|
|
|
|
|
if method.startswith('network.'): |
|
|
|
out = {'id':_id} |
|
|
|
def run(self): |
|
|
|
self.running = True |
|
|
|
threading.Thread(target=self.reading_thread).start() |
|
|
|
while self.running: |
|
|
|
try: |
|
|
|
f = getattr(self.network, method[8:]) |
|
|
|
except AttributeError: |
|
|
|
out['error'] = "unknown method" |
|
|
|
response = self.daemon_pipe.get() |
|
|
|
except util.timeout: |
|
|
|
continue |
|
|
|
try: |
|
|
|
out['result'] = f(*params) |
|
|
|
except BaseException as e: |
|
|
|
out['error'] = str(e) |
|
|
|
print_error("network error", str(e)) |
|
|
|
|
|
|
|
self.queue.put(out) |
|
|
|
return |
|
|
|
|
|
|
|
def cb(i,r): |
|
|
|
_id = r.get('id') |
|
|
|
if _id is not None: |
|
|
|
my_id = self.unanswered_requests.pop(_id) |
|
|
|
r['id'] = my_id |
|
|
|
self.queue.put(r) |
|
|
|
|
|
|
|
try: |
|
|
|
new_id = self.network.interface.send([(method, params)], cb) [0] |
|
|
|
except Exception as e: |
|
|
|
self.queue.put({'id':_id, 'error':str(e)}) |
|
|
|
print_error("network interface error", str(e)) |
|
|
|
return |
|
|
|
|
|
|
|
self.unanswered_requests[new_id] = _id |
|
|
|
self.client_pipe.send(response) |
|
|
|
except socket.error: |
|
|
|
self.running = False |
|
|
|
break |
|
|
|
self.server.remove_client(self) |
|
|
|
|
|
|
|
|
|
|
|
def send_responses(self): |
|
|
|
while True: |
|
|
|
try: |
|
|
|
r = self.queue.get_nowait() |
|
|
|
except Queue.Empty: |
|
|
|
break |
|
|
|
out = json.dumps(r) + '\n' |
|
|
|
while out: |
|
|
|
n = self.s.send(out) |
|
|
|
out = out[n:] |
|
|
|
if self.debug: |
|
|
|
print_error("-->", r) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NetworkServer: |
|
|
|
|
|
|
|
def __init__(self, config): |
|
|
|
self.config = config |
|
|
|
self.network = Network(config) |
|
|
|
self.network.trigger_callback = self.trigger_callback |
|
|
|
self.network.start() |
|
|
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
|
|
|
self.daemon_port = config.get('daemon_port', DAEMON_PORT) |
|
|
|
self.socket.bind(('', self.daemon_port)) |
|
|
|
self.socket.listen(5) |
|
|
|
self.socket.settimeout(1) |
|
|
|
# network sends responses on that queue |
|
|
|
self.network_queue = Queue.Queue() |
|
|
|
self.network.start(self.network_queue) |
|
|
|
|
|
|
|
self.running = False |
|
|
|
# daemon terminates after period of inactivity |
|
|
|
self.timeout = config.get('daemon_timeout', 5*60) |
|
|
@ -165,16 +97,21 @@ class NetworkServer: |
|
|
|
|
|
|
|
# each GUI is a client of the daemon |
|
|
|
self.clients = [] |
|
|
|
# daemon needs to know which client subscribed to which address |
|
|
|
# todo: the daemon needs to know which client subscribed to which address |
|
|
|
|
|
|
|
def is_running(self): |
|
|
|
with self.lock: |
|
|
|
return self.running |
|
|
|
|
|
|
|
def stop(self): |
|
|
|
self.network.stop() |
|
|
|
with self.lock: |
|
|
|
self.running = False |
|
|
|
|
|
|
|
def add_client(self, client): |
|
|
|
for key in ['status','banner','updated','servers','interfaces']: |
|
|
|
value = self.get_status_value(key) |
|
|
|
client.queue.put({'method':'network.status', 'params':[key, value]}) |
|
|
|
value = self.network.get_status_value(key) |
|
|
|
client.daemon_pipe.get_queue.put({'method':'network.status', 'params':[key, value]}) |
|
|
|
with self.lock: |
|
|
|
self.clients.append(client) |
|
|
|
|
|
|
@ -184,27 +121,28 @@ class NetworkServer: |
|
|
|
self.clients.remove(client) |
|
|
|
print_error("client quit:", len(self.clients)) |
|
|
|
|
|
|
|
def get_status_value(self, key): |
|
|
|
if key == 'status': |
|
|
|
value = self.network.connection_status |
|
|
|
elif key == 'banner': |
|
|
|
value = self.network.banner |
|
|
|
elif key == 'updated': |
|
|
|
value = (self.network.get_local_height(), self.network.get_server_height()) |
|
|
|
elif key == 'servers': |
|
|
|
value = self.network.get_servers() |
|
|
|
elif key == 'interfaces': |
|
|
|
value = self.network.get_interfaces() |
|
|
|
return value |
|
|
|
|
|
|
|
def trigger_callback(self, key): |
|
|
|
value = self.get_status_value(key) |
|
|
|
print_error("daemon trigger callback", key, len(self.clients)) |
|
|
|
for client in self.clients: |
|
|
|
client.queue.put({'method':'network.status', 'params':[key, value]}) |
|
|
|
|
|
|
|
|
|
|
|
def main_loop(self): |
|
|
|
self.running = True |
|
|
|
threading.Thread(target=self.listen_thread).start() |
|
|
|
while self.is_running(): |
|
|
|
try: |
|
|
|
response = self.network_queue.get(timeout=0.1) |
|
|
|
except Queue.Empty: |
|
|
|
continue |
|
|
|
for client in self.clients: |
|
|
|
client.daemon_pipe.get_queue.put(response) |
|
|
|
|
|
|
|
print_error("Daemon exiting") |
|
|
|
|
|
|
|
def listen_thread(self): |
|
|
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
|
|
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
|
|
|
self.daemon_port = self.config.get('daemon_port', DAEMON_PORT) |
|
|
|
self.socket.bind(('', self.daemon_port)) |
|
|
|
self.socket.listen(5) |
|
|
|
self.socket.settimeout(1) |
|
|
|
t = time.time() |
|
|
|
while self.running: |
|
|
|
try: |
|
|
@ -218,11 +156,10 @@ class NetworkServer: |
|
|
|
t = time.time() |
|
|
|
continue |
|
|
|
t = time.time() |
|
|
|
client = ClientThread(self, self.network, connection) |
|
|
|
client = ClientThread(self, connection) |
|
|
|
client.start() |
|
|
|
print_error("Daemon exiting") |
|
|
|
|
|
|
|
|
|
|
|
self.stop() |
|
|
|
print_error("listen thread exiting") |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|