""" openvpn supervisor """ import asyncio import tempfile import os import time import openvpn_api import signal def generate_config(in_fp, cfg): def change_item(config, item_key, item_content): output_lines = [] changed = False for i in config.splitlines(): if i.startswith(item_key+" ") or i == item_key: output_lines.append(f"{item_key} {item_content}") changed = True else: output_lines.append(i) if not changed: output_lines.append(f"{item_key} {item_content}") return "\n".join(output_lines) # _, path = tempfile.mkstemp() with open(in_fp, "r") as in_f: config = in_f.read() for key, value in cfg.items(): config = change_item(config, key, value) return config IDLE = "idle" RUNNING = "running" class Openvpn: def __init__(self, cfg_fp, interface, folder_path, management_port, name=None, additional_cfg={}, loop=None): self.cfg_fp = cfg_fp self.interface = interface self.folder_path = folder_path self.management_port = management_port self.PID = 0 self.status = IDLE self.proc = None self.exit_future = None self.additional_cfg = additional_cfg self.run_task = None self.openvpn_api = None self.name = name self.pid_fp = None if loop: self.loop = loop else: self.loop = asyncio.get_event_loop() def get_cfg(self): self.log_fp = os.path.join(self.folder_path, "log.txt") cfg = { "dev": self.interface, "dev-type": "tun", # TODO: add code to read dev-type "management": f"localhost {self.management_port}", "log-append": self.log_fp } cfg.update(self.additional_cfg) return cfg def generate_config_file(self): cfg = self.get_cfg() self.config_fp = os.path.join(self.folder_path, "cfg.txt") config = generate_config(self.cfg_fp, cfg) with open(self.config_fp, "w") as config_f: config_f.write(config) return self.config_fp def start(self): if self.status == IDLE: self.status = RUNNING config_fp = self.generate_config_file() cmd = " ".join(["openvpn", "--config", config_fp]) self.run_task = self.loop.create_task(self.run(cmd)) def get_log(self): # regenerate log_fp _ = self.get_cfg() try: with open(self.log_fp, "r") as log_f: return log_f.read() except: return "" def clear_log(self): os.remove(self.log_fp) async def stop(self): if self.status == RUNNING: try: os.kill(self.PID, signal.SIGKILL) except Exception as err: print("kill failed:", err) self.status = IDLE self.run_task.cancel() async def restart(self): await self.stop() self.start() async def run(self, cmd, group="openvpn"): print(f"run: {cmd}") self.exit_future = asyncio.Future() while self.status == RUNNING: print("create proc") print(self.status) self.pid_fp = os.path.join(self.folder_path, "pid.txt") try: os.remove(self.pid_fp) except: pass shell = f"sg {group} -c \"echo \\$\\$ > {self.pid_fp}; {cmd}\"" print(shell) proc = await asyncio.create_subprocess_shell( shell, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) print("started") self.proc = proc for i in range(3): await asyncio.sleep(1) got_pid = False try: self.PID = open(self.pid_fp, "r").read() got_pid = True except: got_pid = False if got_pid: break if not got_pid: print("error, cannot get pid") break self.openvpn_api = openvpn_api.VPN( 'localhost', int(self.management_port)) print(f"pid: {self.PID}") stdout, stderr = await proc.communicate() print(f'[{cmd!r} exited with {proc.returncode}]') if stdout: print(f'[stdout]\n{stdout.decode()}') if stderr: print(f'[stderr]\n{stderr.decode()}') await asyncio.sleep(5) self.exit_future.set_result(True) self.status = IDLE if __name__ == "__main__": folder_fp = "/home/mantao/Desktop/t/" cfg_fp = "/home/mantao/Desktop/t/TCP_Files/UK2-TCP.ovpn" o1 = Openvpn(cfg_fp, "tun0", folder_fp, 8001, additional_cfg={ "auth-user-pass": "/home/mantao/Desktop/t/fast.txt"}) o1.start()