From aa317c135271f4f4e09feb0a8a4d1bb8b9afa7a2 Mon Sep 17 00:00:00 2001 From: Q Date: Sun, 30 Mar 2025 23:13:51 +0300 Subject: [PATCH] new parser --- py-packages/sshtunnelier/setup.py | 4 +- .../sshtunnelier/sshtunnelier/__init__.py | 68 ++++++++++++++++--- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/py-packages/sshtunnelier/setup.py b/py-packages/sshtunnelier/setup.py index 4b5ecbb..507c5b9 100644 --- a/py-packages/sshtunnelier/setup.py +++ b/py-packages/sshtunnelier/setup.py @@ -22,7 +22,5 @@ setup( "ssh-tunnelier = sshtunnelier:main", ] }, - install_requires=[ - "psutil", "pyyaml" - ], + install_requires=["psutil", "pyyaml"], ) diff --git a/py-packages/sshtunnelier/sshtunnelier/__init__.py b/py-packages/sshtunnelier/sshtunnelier/__init__.py index 3687abe..12ed7e7 100644 --- a/py-packages/sshtunnelier/sshtunnelier/__init__.py +++ b/py-packages/sshtunnelier/sshtunnelier/__init__.py @@ -11,7 +11,7 @@ from argparse import ArgumentError, ArgumentParser import psutil import yaml -__version__ = "2024.03.02" +__version__ = "2025.03.30" CONFDIR = os.path.expanduser("~/.config/ssh-tunnelier") CONF_OLD = os.path.join(CONFDIR, "tunnels.json") @@ -76,7 +76,7 @@ def args(): default=None, action="store", type=str, - help="Instant tunnel. Syntax: sshserver:localport:targethost:remoteport", + help="Instant tunnel. Syntax: sshserver:localport[:targethost][:remoteport][:options]", ) parser.add_argument( "--auto", @@ -149,7 +149,7 @@ def config_edit(editor): subprocess.run([editor, CONF]) -def connect(name, config): +def connect(name, config, foreground=False): if name not in config: raise ValueError("No such connection name") @@ -163,10 +163,22 @@ def connect(name, config): conn_id = get_id(name, config) remote_cmd = f"nice /bin/bash -c 'for ((i=1;i<{MAGIC_TIME};i++)); do cut -f4 -d \" \" /proc/$PPID/stat | xargs kill -0 || exit ; sleep 60;done'; echo tunnelier {conn_id}" - cmd = ["ssh", "-f", "-n", *options, *tunnels, host, remote_cmd] - kill_connection(name, config) - subprocess.run(cmd) - list_connections(config, single=name) + f_commands = ( + [ + "-f", + ] + if not foreground + else [] + ) + cmd = ["ssh", *f_commands, "-n", *options, *tunnels, host, remote_cmd] + if not foreground: + kill_connection(name, config) + try: + subprocess.run(cmd) + except KeyboardInterrupt: + pass + if not foreground: + list_connections(config, single=name) def get_id(name, config): @@ -217,6 +229,44 @@ def auto_connect(config): connect(name, config) +def parse_connect(connect_string): + + if not ":" in connect_string: + raise ValueError("See the connection string format") + + element_count = connect_string.count(":") + elements = connect_string.split(":") + name = elements[0] + lport = int(elements[1]) + rport = lport + raddr = "localhost" + options = "" + + if element_count == 2: + rport = int(elements[2]) + if element_count == 3: + raddr = elements[2] + rport = int(elements[3]) + if element_count == 4: + raddr = elements[2] + rport = int(elements[3]) + options = elements[4] + config = { + name: { + "host": name, + "options": options, + "auto-connect": False, + "tunnels": [ + {"local_port": lport, "remote_port": rport, "remote_address": raddr, "reverse": False, "comment": ""} + ], + } + } + url = f"http://localhost:{lport}" + remote = LOCALHOSTSYMBOL if raddr == "localhost" else raddr + print(f" Parsed: {lport} → {remote}:{rport} {url}") + return name, config + + def main(): opts = args() config = load_config() @@ -229,8 +279,8 @@ def main(): if opts.auto: auto_connect(config) if opts.connect: - host, tunnels = parse_connect(opts.connect) - connect_tunnel(host, tunnels) + name, config = parse_connect(opts.connect) + connect(name, config, foreground=True) sys.exit(0) if opts.kill: kill_connection(opts.name, config)