vssh 3.6.2__tar.gz → 3.6.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vssh-3.6.2/vssh.egg-info → vssh-3.6.7}/PKG-INFO +1 -1
- {vssh-3.6.2 → vssh-3.6.7}/pyproject.toml +1 -1
- {vssh-3.6.2 → vssh-3.6.7/vssh.egg-info}/PKG-INFO +1 -1
- {vssh-3.6.2 → vssh-3.6.7}/vssh.py +94 -6
- {vssh-3.6.2 → vssh-3.6.7}/LICENSE +0 -0
- {vssh-3.6.2 → vssh-3.6.7}/README.md +0 -0
- {vssh-3.6.2 → vssh-3.6.7}/setup.cfg +0 -0
- {vssh-3.6.2 → vssh-3.6.7}/vssh.egg-info/SOURCES.txt +0 -0
- {vssh-3.6.2 → vssh-3.6.7}/vssh.egg-info/dependency_links.txt +0 -0
- {vssh-3.6.2 → vssh-3.6.7}/vssh.egg-info/entry_points.txt +0 -0
- {vssh-3.6.2 → vssh-3.6.7}/vssh.egg-info/top_level.txt +0 -0
- {vssh-3.6.2 → vssh-3.6.7}/vssh_mcp_server.py +0 -0
- {vssh-3.6.2 → vssh-3.6.7}/vssh_p2p.py +0 -0
|
@@ -144,14 +144,31 @@ def vssh_connect(host: str, timeout: float = 5.0) -> socket.socket:
|
|
|
144
144
|
del _FAILOVER_ACTIVE[host]
|
|
145
145
|
|
|
146
146
|
# Primary: try Wire VPN IP
|
|
147
|
+
# First attempt: 2s fast path
|
|
148
|
+
# If timeout (cold WireGuard peer re-handshake), retry once with longer timeout
|
|
149
|
+
_wire_refused = False
|
|
147
150
|
try:
|
|
148
151
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
149
|
-
sock.settimeout(min(timeout, 2.0)) # 2s:
|
|
152
|
+
sock.settimeout(min(timeout, 2.0)) # 2s: fast path
|
|
150
153
|
sock.connect((host, PORT))
|
|
151
|
-
sock.settimeout(timeout)
|
|
154
|
+
sock.settimeout(timeout)
|
|
152
155
|
return sock
|
|
153
|
-
except
|
|
154
|
-
|
|
156
|
+
except ConnectionRefusedError:
|
|
157
|
+
_wire_refused = True # Port closed — no point retrying
|
|
158
|
+
except (ConnectionError, OSError, socket.timeout):
|
|
159
|
+
pass # Timeout — WireGuard may be re-handshaking, retry once
|
|
160
|
+
|
|
161
|
+
if not _wire_refused:
|
|
162
|
+
# Retry: wait 1s for WireGuard handshake to complete, then retry
|
|
163
|
+
time.sleep(1.0)
|
|
164
|
+
try:
|
|
165
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
166
|
+
sock.settimeout(min(timeout, 5.0)) # 5s: handshake should be done by now
|
|
167
|
+
sock.connect((host, PORT))
|
|
168
|
+
sock.settimeout(timeout)
|
|
169
|
+
return sock
|
|
170
|
+
except (ConnectionRefusedError, ConnectionError, OSError, socket.timeout):
|
|
171
|
+
pass # Wire truly failed, fall through to Tailscale
|
|
155
172
|
|
|
156
173
|
# Fallback: try Tailscale IP
|
|
157
174
|
ts_ip = TAILSCALE_MAP.get(host)
|
|
@@ -3866,6 +3883,65 @@ Env: VSSH_SECRET
|
|
|
3866
3883
|
print(f' Done: {result["ok"]} ok, {result["skip"]} skip, {result["fail"]} fail')
|
|
3867
3884
|
|
|
3868
3885
|
sys.exit(0 if result['fail'] == 0 else 1)
|
|
3886
|
+
elif cmd == 'up':
|
|
3887
|
+
# Start vssh daemon (systemd or direct)
|
|
3888
|
+
import subprocess as _sp, os as _os
|
|
3889
|
+
if _os.path.exists("/run/systemd/system"):
|
|
3890
|
+
r = _sp.run(["systemctl", "start", "vssh"], capture_output=True)
|
|
3891
|
+
if r.returncode == 0:
|
|
3892
|
+
print("vssh started")
|
|
3893
|
+
else:
|
|
3894
|
+
print(r.stderr.decode().strip() or "Failed to start vssh via systemctl")
|
|
3895
|
+
else:
|
|
3896
|
+
print("Starting vssh daemon...")
|
|
3897
|
+
server()
|
|
3898
|
+
elif cmd == 'down':
|
|
3899
|
+
# Stop vssh daemon
|
|
3900
|
+
import subprocess as _sp, os as _os
|
|
3901
|
+
if _os.path.exists("/run/systemd/system"):
|
|
3902
|
+
r = _sp.run(["systemctl", "stop", "vssh"], capture_output=True)
|
|
3903
|
+
if r.returncode == 0:
|
|
3904
|
+
print("vssh stopped")
|
|
3905
|
+
else:
|
|
3906
|
+
print(r.stderr.decode().strip() or "Failed to stop vssh via systemctl")
|
|
3907
|
+
else:
|
|
3908
|
+
import signal as _sig
|
|
3909
|
+
import glob as _glob
|
|
3910
|
+
killed = 0
|
|
3911
|
+
for _f in _glob.glob("/tmp/vssh_*.pid") + _glob.glob("/var/run/vssh.pid"):
|
|
3912
|
+
try:
|
|
3913
|
+
_pid = int(open(_f).read().strip())
|
|
3914
|
+
_os.kill(_pid, _sig.SIGTERM)
|
|
3915
|
+
_os.unlink(_f)
|
|
3916
|
+
killed += 1
|
|
3917
|
+
except (ValueError, OSError):
|
|
3918
|
+
pass
|
|
3919
|
+
if killed:
|
|
3920
|
+
print(f"vssh stopped ({killed} process(es))")
|
|
3921
|
+
else:
|
|
3922
|
+
import subprocess as _sp2
|
|
3923
|
+
r = _sp2.run(["pkill", "-f", "vssh.*server"], capture_output=True)
|
|
3924
|
+
print("vssh stopped" if r.returncode == 0 else "vssh was not running")
|
|
3925
|
+
elif cmd == 'restart':
|
|
3926
|
+
# Restart vssh daemon
|
|
3927
|
+
import subprocess as _sp, os as _os
|
|
3928
|
+
if _os.path.exists("/run/systemd/system"):
|
|
3929
|
+
r = _sp.run(["systemctl", "restart", "vssh"], capture_output=True)
|
|
3930
|
+
if r.returncode == 0:
|
|
3931
|
+
print("vssh restarted")
|
|
3932
|
+
else:
|
|
3933
|
+
print(r.stderr.decode().strip() or "Failed to restart vssh via systemctl")
|
|
3934
|
+
else:
|
|
3935
|
+
import signal as _sig, glob as _glob
|
|
3936
|
+
for _f in _glob.glob("/tmp/vssh_*.pid") + _glob.glob("/var/run/vssh.pid"):
|
|
3937
|
+
try:
|
|
3938
|
+
_pid = int(open(_f).read().strip())
|
|
3939
|
+
_os.kill(_pid, _sig.SIGTERM)
|
|
3940
|
+
_os.unlink(_f)
|
|
3941
|
+
except (ValueError, OSError):
|
|
3942
|
+
pass
|
|
3943
|
+
print("Restarting vssh daemon...")
|
|
3944
|
+
server()
|
|
3869
3945
|
elif cmd == 'server':
|
|
3870
3946
|
server()
|
|
3871
3947
|
elif cmd == 'install':
|
|
@@ -3894,6 +3970,14 @@ Env: VSSH_SECRET
|
|
|
3894
3970
|
break
|
|
3895
3971
|
except (OSError, ValueError, TimeoutError):
|
|
3896
3972
|
continue
|
|
3973
|
+
# Detect local node by trying to bind to each VPN IP
|
|
3974
|
+
def _is_local_ip(ip):
|
|
3975
|
+
try:
|
|
3976
|
+
_s = _sock.socket(_sock.AF_INET, _sock.SOCK_DGRAM)
|
|
3977
|
+
_s.bind((ip, 0)); _s.close(); return True
|
|
3978
|
+
except OSError:
|
|
3979
|
+
return False
|
|
3980
|
+
_my_vpn_ip = next((ip for ip in _servers.values() if _is_local_ip(ip)), "")
|
|
3897
3981
|
# Also include any config entries not in coordinator (offline/static nodes)
|
|
3898
3982
|
for k, v in VSSH_CONFIG.items():
|
|
3899
3983
|
if k in ('SECRET', 'SSH_PORT', 'SCP_PORT'): continue
|
|
@@ -3902,6 +3986,8 @@ Env: VSSH_SECRET
|
|
|
3902
3986
|
|
|
3903
3987
|
def _check_node(name, ip):
|
|
3904
3988
|
"""Check a single node — connect + optional INFO query (Wire VPN → Tailscale fallback)"""
|
|
3989
|
+
if ip == _my_vpn_ip:
|
|
3990
|
+
return {'name': name, 'ip': ip, 'online': True, 'latency': 0, 'local': True}
|
|
3905
3991
|
result = {'name': name, 'ip': ip, 'online': False, 'latency': 0}
|
|
3906
3992
|
try:
|
|
3907
3993
|
t0 = time.time()
|
|
@@ -3931,7 +4017,7 @@ Env: VSSH_SECRET
|
|
|
3931
4017
|
result['info'] = json.loads(buf.decode())
|
|
3932
4018
|
except (json.JSONDecodeError, ValueError, UnicodeDecodeError) as e:
|
|
3933
4019
|
pass # e silenced
|
|
3934
|
-
s.close()
|
|
4020
|
+
s.close() # Always close — was only in full_mode, causing 13 dangling sockets on regular status
|
|
3935
4021
|
except OSError:
|
|
3936
4022
|
pass # safe to ignore
|
|
3937
4023
|
return result
|
|
@@ -3946,7 +4032,7 @@ Env: VSSH_SECRET
|
|
|
3946
4032
|
total = 0
|
|
3947
4033
|
# Run checks in parallel for speed
|
|
3948
4034
|
results = []
|
|
3949
|
-
with concurrent.futures.ThreadPoolExecutor(max_workers=
|
|
4035
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as pool: # 5 concurrent: limits WireGuard handshake burst that disrupts active sessions
|
|
3950
4036
|
futures = {pool.submit(_check_node, n, ip): n for n, ip in sorted(_servers.items()) if ip}
|
|
3951
4037
|
for f in concurrent.futures.as_completed(futures):
|
|
3952
4038
|
results.append(f.result())
|
|
@@ -4013,6 +4099,8 @@ Env: VSSH_SECRET
|
|
|
4013
4099
|
cores = d.get('cores', '')
|
|
4014
4100
|
cores_str = f"{cores}c" if cores else ""
|
|
4015
4101
|
print(f" {r['name']:6s} ● {r['latency']:4.0f}ms mem={mem_str:>12s} load={load_str:>5s} up={up_str:>7s} {cores_str}")
|
|
4102
|
+
elif r.get('local'):
|
|
4103
|
+
print(f" {r['name']:6s} {r['ip']:18s} ● online (local)")
|
|
4016
4104
|
else:
|
|
4017
4105
|
print(f" {r['name']:6s} {r['ip']:18s} ● online ({r['latency']:.0f}ms)")
|
|
4018
4106
|
else:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|