#!/usr/bin/env python3 # # lokinet runtime wrapper # from ctypes import * import configparser import signal import time import threading import os import sys import requests from pylokinet import rc lib_file = os.path.join(os.path.realpath('.'), 'liblokinet-shared.so') def log(msg): sys.stderr.write("lokinet: {}\n".format(msg)) sys.stderr.flush() class LokiNET(threading.Thread): lib = None ctx = 0 failed = False up = False asRouter = True def configure(self, lib, conf, ip=None, port=None, ifname=None, seedfile=None, lokid_host=None, lokid_port=None): log("configure lib={} conf={}".format(lib, conf)) if not os.path.exists(os.path.dirname(conf)): os.mkdir(os.path.dirname(conf)) try: self.lib = CDLL(lib) except OSError as ex: log("failed to load library: {}".format(ex)) return False if self.lib.llarp_ensure_config(conf.encode('utf-8'), os.path.dirname(conf).encode('utf-8'), True, self.asRouter): config = configparser.ConfigParser() config.read(conf) log('overwrite ip="{}" port="{}" ifname="{}" seedfile="{}" lokid=("{}", "{}")'.format( ip, port, ifname, seedfile, lokid_host, lokid_port)) if seedfile and lokid_host and lokid_port: if not os.path.exists(seedfile): log('cannot access service node seed at "{}"'.format(seedfile)) return False config['lokid'] = { 'service-node-seed': seedfile, 'enabled': "true", 'jsonrpc': "{}:{}".format(lokid_host, lokid_port) } if ip: config['router']['public-address'] = '{}'.format(ip) if port: config['router']['public-port'] = '{}'.format(port) if ifname and port: config['bind'] = { ifname: '{}'.format(port) } with open(conf, "w") as f: config.write(f) self.ctx = self.lib.llarp_main_init(conf.encode('utf-8')) else: return False return self.lib.llarp_main_setup(self.ctx, False) == 0 def inform_fail(self): """ inform lokinet crashed """ self.failed = True self._inform() def inform_up(self): self.up = True self._inform() def _inform(self): """ inform waiter """ def wait_for_up(self, timeout): """ wait for lokinet to go up for :timeout: seconds :return True if we are up and running otherwise False: """ # return self._up.wait(timeout) def signal(self, sig): if self.ctx and self.lib: self.lib.llarp_main_signal(self.ctx, int(sig)) def run(self): # self._up.acquire() self.up = True code = self.lib.llarp_main_run(self.ctx) log("llarp_main_run exited with status {}".format(code)) if code: self.inform_fail() self.up = False # self._up.release() def close(self): if self.lib and self.ctx: self.lib.llarp_main_free(self.ctx) def getconf(name, fallback=None): return name in os.environ and os.environ[name] or fallback def run_main(args): seedfile = getconf("LOKI_SEED_FILE") if seedfile is None: print("LOKI_SEED_FILE was not set") return lokid_host = getconf("LOKI_RPC_HOST", "127.0.0.1") lokid_port = getconf("LOKI_RPC_PORT", "22023") root = getconf("LOKINET_ROOT") if root is None: print("LOKINET_ROOT was not set") return rc_callback = getconf("LOKINET_SUBMIT_URL") if rc_callback is None: print("LOKINET_SUBMIT_URL was not set") return bootstrap = getconf("LOKINET_BOOTSTRAP_URL") if bootstrap is None: print("LOKINET_BOOTSTRAP_URL was not set") lib = getconf("LOKINET_LIB", lib_file) if not os.path.exists(lib): lib = "liblokinet-shared.so" timeout = int(getconf("LOKINET_TIMEOUT", "5")) ping_interval = int(getconf("LOKINET_PING_INTERVAL", "60")) ping_callback = getconf("LOKINET_PING_URL") ip = getconf("LOKINET_IP") port = getconf("LOKINET_PORT") ifname = getconf("LOKINET_IFNAME") if ping_callback is None: print("LOKINET_PING_URL was not set") return conf = os.path.join(root, "daemon.ini") log("going up") loki = LokiNET() log("bootstrapping...") try: r = requests.get(bootstrap) if r.status_code == 404: log("bootstrap gave no RCs, we are probably the seed node") elif r.status_code != 200: raise Exception("http {}".format(r.status_code)) else: data = r.content if rc.validate(data): log("valid RC obtained") with open(os.path.join(root, "bootstrap.signed"), "wb") as f: f.write(data) else: raise Exception("invalid RC") except Exception as ex: log("failed to bootstrap: {}".format(ex)) loki.close() return if loki.configure(lib, conf, ip, port, ifname, seedfile, lokid_host, lokid_port): log("configured") loki.start() try: log("waiting for spawn") while timeout > 0: time.sleep(1) if loki.failed: log("failed") break log("waiting {}".format(timeout)) timeout -= 1 if loki.up: log("submitting rc") try: with open(os.path.join(root, 'self.signed'), 'rb') as f: r = requests.put(rc_callback, data=f.read(), headers={ "content-type": "application/octect-stream"}) log('submit rc reply: HTTP {}'.format(r.status_code)) except Exception as ex: log("failed to submit rc: {}".format(ex)) loki.signal(signal.SIGINT) time.sleep(2) else: while loki.up: time.sleep(ping_interval) try: r = requests.get(ping_callback) log("ping reply: HTTP {}".format(r.status_code)) except Exception as ex: log("failed to submit ping: {}".format(ex)) else: log("failed to go up") loki.signal(signal.SIGINT) except KeyboardInterrupt: loki.signal(signal.SIGINT) time.sleep(2) finally: loki.close() else: loki.close() def main(): run_main(sys.argv[1:]) if __name__ == "__main__": main()