ser2netweb 3.0.0__py3-none-any.whl
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.
- ser2netweb/__init__.py +20 -0
- ser2netweb/__main__.py +6 -0
- ser2netweb/accounts.py +503 -0
- ser2netweb/cli.py +489 -0
- ser2netweb/clock.py +128 -0
- ser2netweb/config.py +4491 -0
- ser2netweb/console.py +181 -0
- ser2netweb/datalock.py +181 -0
- ser2netweb/engine/__init__.py +0 -0
- ser2netweb/engine/access.py +173 -0
- ser2netweb/engine/bindhint.py +115 -0
- ser2netweb/engine/bridge.py +1928 -0
- ser2netweb/engine/cluster.py +1140 -0
- ser2netweb/engine/modbus.py +530 -0
- ser2netweb/engine/modbus_gateway.py +1465 -0
- ser2netweb/engine/mqtt_pub.py +632 -0
- ser2netweb/engine/netguard.py +805 -0
- ser2netweb/engine/netinfo.py +199 -0
- ser2netweb/engine/portdiag.py +414 -0
- ser2netweb/engine/portlist.py +471 -0
- ser2netweb/engine/protocols/__init__.py +28 -0
- ser2netweb/engine/protocols/base.py +117 -0
- ser2netweb/engine/protocols/raw.py +12 -0
- ser2netweb/engine/protocols/rfc2217.py +586 -0
- ser2netweb/engine/protocols/telnet.py +172 -0
- ser2netweb/engine/serial_compat.py +598 -0
- ser2netweb/engine/serial_io.py +210 -0
- ser2netweb/engine/serial_link.py +414 -0
- ser2netweb/engine/sockopts.py +103 -0
- ser2netweb/engine/sparkplug.py +538 -0
- ser2netweb/engine/sparkplug_node.py +550 -0
- ser2netweb/engine/supervisor.py +533 -0
- ser2netweb/engine/tls.py +232 -0
- ser2netweb/envvars.py +57 -0
- ser2netweb/explain.py +117 -0
- ser2netweb/healthcheck.py +78 -0
- ser2netweb/i18n.py +110 -0
- ser2netweb/importer.py +550 -0
- ser2netweb/layout.py +118 -0
- ser2netweb/licences.py +86 -0
- ser2netweb/locale/tr.json +1569 -0
- ser2netweb/logsetup.py +253 -0
- ser2netweb/logtext.py +23 -0
- ser2netweb/notify.py +761 -0
- ser2netweb/optional.py +76 -0
- ser2netweb/reload.py +295 -0
- ser2netweb/runtime.py +762 -0
- ser2netweb/sdnotify.py +85 -0
- ser2netweb/ser2net_import.py +2001 -0
- ser2netweb/signins.py +98 -0
- ser2netweb/standalone.py +154 -0
- ser2netweb/state.py +1457 -0
- ser2netweb/storage.py +130 -0
- ser2netweb/textfold.py +51 -0
- ser2netweb/tlsreload.py +37 -0
- ser2netweb/watchdog.py +201 -0
- ser2netweb/web/__init__.py +0 -0
- ser2netweb/web/api.py +793 -0
- ser2netweb/web/auth.py +699 -0
- ser2netweb/web/certinfo.py +144 -0
- ser2netweb/web/httpguard.py +413 -0
- ser2netweb/web/ldap_auth.py +556 -0
- ser2netweb/web/oidc_auth.py +330 -0
- ser2netweb/web/proxy.py +398 -0
- ser2netweb/web/routes/__init__.py +78 -0
- ser2netweb/web/routes/cluster.py +669 -0
- ser2netweb/web/routes/common.py +359 -0
- ser2netweb/web/routes/console.py +451 -0
- ser2netweb/web/routes/forms.py +853 -0
- ser2netweb/web/routes/login.py +573 -0
- ser2netweb/web/routes/mappings.py +470 -0
- ser2netweb/web/routes/ops.py +374 -0
- ser2netweb/web/routes/settings.py +2135 -0
- ser2netweb/web/routes/system.py +308 -0
- ser2netweb/web/routes/totp.py +282 -0
- ser2netweb/web/selfsigned.py +129 -0
- ser2netweb/web/server.py +483 -0
- ser2netweb/web/static/app.css +327 -0
- ser2netweb/web/static/app.js +1102 -0
- ser2netweb/web/static/htmx.min.js +1 -0
- ser2netweb/web/static/points.js +218 -0
- ser2netweb/web/static/theme.js +7 -0
- ser2netweb/web/static/xterm-addon-fit.js +2 -0
- ser2netweb/web/static/xterm.css +218 -0
- ser2netweb/web/static/xterm.js +2 -0
- ser2netweb/web/templates/_cluster_body.html +109 -0
- ser2netweb/web/templates/_console.html +21 -0
- ser2netweb/web/templates/_footer.html +13 -0
- ser2netweb/web/templates/_login_notice.html +8 -0
- ser2netweb/web/templates/_macros.html +21 -0
- ser2netweb/web/templates/_mapping_form.html +477 -0
- ser2netweb/web/templates/_mapping_log.html +28 -0
- ser2netweb/web/templates/_mapping_traces.html +44 -0
- ser2netweb/web/templates/_mappings_body.html +149 -0
- ser2netweb/web/templates/_modbus_read.html +18 -0
- ser2netweb/web/templates/_ports_table.html +37 -0
- ser2netweb/web/templates/_serial_port_select.html +48 -0
- ser2netweb/web/templates/_shell.html +41 -0
- ser2netweb/web/templates/audit.html +55 -0
- ser2netweb/web/templates/base.html +77 -0
- ser2netweb/web/templates/dashboard.html +72 -0
- ser2netweb/web/templates/licenses.html +18 -0
- ser2netweb/web/templates/login.html +32 -0
- ser2netweb/web/templates/login_totp.html +23 -0
- ser2netweb/web/templates/settings.html +830 -0
- ser2netweb/web/templates/setup.html +43 -0
- ser2netweb/web/templates/totp.html +41 -0
- ser2netweb/web/totp.py +94 -0
- ser2netweb/winservice.py +1793 -0
- ser2netweb-3.0.0.dist-info/METADATA +120 -0
- ser2netweb-3.0.0.dist-info/RECORD +121 -0
- ser2netweb-3.0.0.dist-info/WHEEL +5 -0
- ser2netweb-3.0.0.dist-info/entry_points.txt +2 -0
- ser2netweb-3.0.0.dist-info/licenses/COMMERCIAL_LICENSE.md +94 -0
- ser2netweb-3.0.0.dist-info/licenses/LICENSE +662 -0
- ser2netweb-3.0.0.dist-info/licenses/NOTICE +20 -0
- ser2netweb-3.0.0.dist-info/licenses/THIRD-PARTY-NOTICES.md +105 -0
- ser2netweb-3.0.0.dist-info/licenses/tools/licenses/npm/htmx.org.txt +13 -0
- ser2netweb-3.0.0.dist-info/licenses/tools/licenses/npm/xterm-addon-fit.txt +19 -0
- ser2netweb-3.0.0.dist-info/licenses/tools/licenses/npm/xterm.txt +21 -0
- ser2netweb-3.0.0.dist-info/top_level.txt +1 -0
ser2netweb/sdnotify.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""systemd's notification protocol (sd_notify), standard library only.
|
|
2
|
+
|
|
3
|
+
Under a unit with Type=notify (the optional drop-in systemd/ser2netweb.service.d/notify.conf)
|
|
4
|
+
systemd waits for READY=1 before it counts ser2netweb as started, and with WatchdogSec= it
|
|
5
|
+
restarts ser2netweb when the WATCHDOG=1 pings stop (they come from the event loop, so a
|
|
6
|
+
loop that is stuck stops them). Everything here does nothing without $NOTIFY_SOCKET, so
|
|
7
|
+
the default unit (Type=simple), Docker, Windows and a start by hand are not affected.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import socket
|
|
13
|
+
import sys
|
|
14
|
+
import time
|
|
15
|
+
from typing import Mapping, Optional
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _address(env: Mapping[str, str]) -> Optional[str]:
|
|
19
|
+
addr = env.get("NOTIFY_SOCKET") or ""
|
|
20
|
+
if not addr or not hasattr(socket, "AF_UNIX"):
|
|
21
|
+
return None
|
|
22
|
+
if addr.startswith("@"): # an abstract socket (Linux)
|
|
23
|
+
return "\0" + addr[1:]
|
|
24
|
+
return addr if addr.startswith("/") else None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def notify(*fields: str, env: Optional[Mapping[str, str]] = None) -> bool:
|
|
28
|
+
"""Send ``fields`` ("READY=1", "STATUS=..."), one per line, to systemd. True when sent;
|
|
29
|
+
False without $NOTIFY_SOCKET or when it can't be (never raises). It never waits: this
|
|
30
|
+
runs on the event loop, and a socket whose queue is full (nobody reads it) would hold
|
|
31
|
+
it up; that message is lost instead."""
|
|
32
|
+
addr = _address(os.environ if env is None else env)
|
|
33
|
+
if addr is None or sys.platform == "win32": # (no systemd there)
|
|
34
|
+
return False
|
|
35
|
+
try:
|
|
36
|
+
with socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) as s:
|
|
37
|
+
s.setblocking(False)
|
|
38
|
+
s.connect(addr)
|
|
39
|
+
s.sendall("\n".join(fields).encode("utf-8", "replace"))
|
|
40
|
+
except OSError:
|
|
41
|
+
return False
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def ready(status: str = "", env: Optional[Mapping[str, str]] = None) -> bool:
|
|
46
|
+
"""ser2netweb has started: its mappings run and its admin UI listens."""
|
|
47
|
+
return notify("READY=1", *([f"STATUS={status}"] if status else []), env=env)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def reloading(env: Optional[Mapping[str, str]] = None) -> bool:
|
|
51
|
+
"""ser2netweb reads config.json again (systemctl reload, SIGHUP); ready() follows when it
|
|
52
|
+
is done (also what Type=notify-reload waits for)."""
|
|
53
|
+
fields = ["RELOADING=1"]
|
|
54
|
+
if sys.platform != "win32": # (no CLOCK_MONOTONIC on Windows, which has no systemd)
|
|
55
|
+
try:
|
|
56
|
+
fields.append(f"MONOTONIC_USEC={time.clock_gettime_ns(time.CLOCK_MONOTONIC) // 1000}")
|
|
57
|
+
except (AttributeError, OSError):
|
|
58
|
+
pass
|
|
59
|
+
return notify(*fields, env=env)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def stopping(env: Optional[Mapping[str, str]] = None) -> bool:
|
|
63
|
+
return notify("STOPPING=1", env=env)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def watchdog(env: Optional[Mapping[str, str]] = None) -> bool:
|
|
67
|
+
return notify("WATCHDOG=1", env=env)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def watchdog_interval(env: Optional[Mapping[str, str]] = None) -> Optional[float]:
|
|
71
|
+
"""How often to send WATCHDOG=1 (half of WatchdogSec=, as systemd advises), or None
|
|
72
|
+
when systemd doesn't watch this process."""
|
|
73
|
+
env = os.environ if env is None else env
|
|
74
|
+
if _address(env) is None:
|
|
75
|
+
return None
|
|
76
|
+
# meant for this process: its pid, or its parent's when that is the standalone
|
|
77
|
+
# binary's launcher, which runs ser2netweb in a child process (NotifyAccess=all)
|
|
78
|
+
pid = (env.get("WATCHDOG_PID") or "").strip()
|
|
79
|
+
if pid and pid not in (str(os.getpid()), str(os.getppid())):
|
|
80
|
+
return None
|
|
81
|
+
try:
|
|
82
|
+
usec = int((env.get("WATCHDOG_USEC") or "").strip())
|
|
83
|
+
except ValueError:
|
|
84
|
+
return None
|
|
85
|
+
return usec / 2e6 if usec > 0 else None
|