diff --git a/kvmd/aiogp.py b/kvmd/aiogp.py index 3ee0ea2b..d6fd4a5f 100644 --- a/kvmd/aiogp.py +++ b/kvmd/aiogp.py @@ -24,7 +24,7 @@ import asyncio import threading import dataclasses -import gpiod +#import gpiod from . import aiotools @@ -79,46 +79,8 @@ class AioReader: # pylint: disable=too-many-instance-attributes assert self.__loop pins = sorted(self.__pins) - with gpiod.request_lines( - self.__path, - consumer=self.__consumer, - config={tuple(pins): gpiod.LineSettings(edge_detection=gpiod.line.Edge.BOTH)}, - ) as line_req: + - line_req.wait_edge_events(0.1) - self.__values = { - pin: _DebouncedValue( - initial=bool(value.value), - debounce=self.__pins[pin].debounce, - notifier=self.__notifier, - loop=self.__loop, - ) - for (pin, value) in zip(pins, line_req.get_values(pins)) - } - self.__loop.call_soon_threadsafe(self.__notifier.notify) - - while not self.__stop_event.is_set(): - if line_req.wait_edge_events(1): - new: dict[int, bool] = {} - for event in line_req.read_edge_events(): - (pin, value) = self.__parse_event(event) - new[pin] = value - for (pin, value) in new.items(): - self.__values[pin].set(value) - else: # Timeout - # XXX: Лимит был актуален для 1.6. Надо проверить, поменялось ли это в 2.x. - # Размер буфера ядра - 16 эвентов на линии. При превышении этого числа, - # новые эвенты потеряются. Это не баг, это фича, как мне объяснили в LKML. - # Штош. Будем с этим жить и синхронизировать состояния при таймауте. - for (pin, value) in zip(pins, line_req.get_values(pins)): - self.__values[pin].set(bool(value.value)) # type: ignore - - def __parse_event(self, event: gpiod.EdgeEvent) -> tuple[int, bool]: - if event.event_type == event.Type.RISING_EDGE: - return (event.line_offset, True) - elif event.event_type == event.Type.FALLING_EDGE: - return (event.line_offset, False) - raise RuntimeError(f"Invalid event {event} type: {event.type}") class _DebouncedValue: diff --git a/kvmd/aioproc.py b/kvmd/aioproc.py index 376df004..66c1d43f 100644 --- a/kvmd/aioproc.py +++ b/kvmd/aioproc.py @@ -21,6 +21,7 @@ import os +import platform import signal import asyncio import asyncio.subprocess @@ -38,11 +39,16 @@ async def run_process( env: (dict[str, str] | None)=None, ) -> asyncio.subprocess.Process: # pylint: disable=no-member + if platform.system() != 'Windows': + preexec_fn=os.setpgrp + else: + preexec_fn=None # 或者选择适合 Windows 的其他方式 + return (await asyncio.create_subprocess_exec( *cmd, stdout=asyncio.subprocess.PIPE, stderr=(asyncio.subprocess.DEVNULL if err_to_null else asyncio.subprocess.STDOUT), - preexec_fn=os.setpgrp, + preexec_fn=preexec_fn, env=env, )) @@ -117,6 +123,6 @@ def rename_process(suffix: str, prefix: str="kvmd") -> None: def settle(name: str, suffix: str, prefix: str="kvmd") -> logging.Logger: logger = get_logger(1) logger.info("Started %s pid=%d", name, os.getpid()) - os.setpgrp() + #os.setpgrp() rename_process(suffix, prefix) return logger diff --git a/kvmd/aiotools.py b/kvmd/aiotools.py index a47c94c6..f2776770 100644 --- a/kvmd/aiotools.py +++ b/kvmd/aiotools.py @@ -27,6 +27,7 @@ import ssl import functools import types import typing +import platform from typing import Callable from typing import Awaitable @@ -56,8 +57,9 @@ def run(coro: Coroutine, final: (Coroutine | None)=None) -> None: raise SystemExit() loop = asyncio.get_event_loop() - loop.add_signal_handler(signal.SIGINT, sigint_handler) - loop.add_signal_handler(signal.SIGTERM, sigterm_handler) + if platform.system() != 'Windows': + loop.add_signal_handler(signal.SIGINT, sigint_handler) + loop.add_signal_handler(signal.SIGTERM, sigterm_handler) main_task = loop.create_task(coro) try: diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index d9a2d97a..280f6125 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -181,12 +181,13 @@ def _init_config(config_path: str, override_options: list[str], **load_flags: bo _patch_raw(raw_config) config = make_config(raw_config, scheme) - if _patch_dynamic(raw_config, config, scheme, **load_flags): - config = make_config(raw_config, scheme) - - return config + except (ConfigError, UnknownPluginError) as ex: raise SystemExit(f"ConfigError: {ex}") + if _patch_dynamic(raw_config, config, scheme, **load_flags): + config = make_config(raw_config, scheme) + + return config def _patch_raw(raw_config: dict) -> None: # pylint: disable=too-many-branches diff --git a/kvmd/apps/kvmd/info/extras.py b/kvmd/apps/kvmd/info/extras.py index 1e69748c..6cae6574 100644 --- a/kvmd/apps/kvmd/info/extras.py +++ b/kvmd/apps/kvmd/info/extras.py @@ -23,6 +23,7 @@ import os import re import asyncio +import sys from typing import AsyncGenerator @@ -51,7 +52,7 @@ class ExtrasInfoSubmanager(BaseInfoSubmanager): sui = sysunit.SystemdUnitInfo() await sui.open() except Exception as ex: - if not os.path.exists("/etc/kvmd/.docker_flag"): + if not os.path.exists("/etc/kvmd/.docker_flag") or not sys.platform.startswith('linux'): get_logger(0).error("Can't open systemd bus to get extras state: %s", tools.efmt(ex)) sui = None try: diff --git a/kvmd/apps/kvmd/info/hw.py b/kvmd/apps/kvmd/info/hw.py index 3c444760..db91e586 100644 --- a/kvmd/apps/kvmd/info/hw.py +++ b/kvmd/apps/kvmd/info/hw.py @@ -169,7 +169,7 @@ class HwInfoSubmanager(BaseInfoSubmanager): + (st.steal + st.guest) / total * 100 ) except Exception as ex: - get_logger(0).error("Can't get CPU percent: %s", ex) + #get_logger(0).error("Can't get CPU percent: %s", ex) return None async def __get_mem(self) -> dict: @@ -218,7 +218,7 @@ class HwInfoSubmanager(BaseInfoSubmanager): async def __parse_vcgencmd(self, arg: str, parser: Callable[[str], _RetvalT]) -> (_RetvalT | None): cmd = [*self.__vcgencmd_cmd, arg] try: - text = (await aioproc.read_process(cmd, err_to_null=True))[1] + text = "throttled=0x0" except Exception: get_logger(0).exception("Error while executing: %s", tools.cmdfmt(cmd)) return None diff --git a/kvmd/apps/kvmd/info/system.py b/kvmd/apps/kvmd/info/system.py index d4a450de..de21b824 100644 --- a/kvmd/apps/kvmd/info/system.py +++ b/kvmd/apps/kvmd/info/system.py @@ -76,12 +76,14 @@ class SystemInfoSubmanager(BaseInfoSubmanager): except Exception: get_logger(0).exception("Can't get streamer info") else: - try: - for line in features_text.split("\n"): - (status, name) = map(str.strip, line.split(" ")) - features[name] = (status == "+") - except Exception: - get_logger(0).exception("Can't parse streamer features") + #try: + # print(features_text) + # for line in features_text.split("\n"): + # (status, name) = map(str.strip, line.split(" ")) + # features[name] = (status == "+") + #except Exception: + # get_logger(0).exception("Can't parse streamer features") + pass return { "app": os.path.basename(path), "version": version, diff --git a/kvmd/apps/kvmd/ocr.py b/kvmd/apps/kvmd/ocr.py index 367c0c80..13710d0c 100644 --- a/kvmd/apps/kvmd/ocr.py +++ b/kvmd/apps/kvmd/ocr.py @@ -78,7 +78,7 @@ def _load_libtesseract() -> (ctypes.CDLL | None): setattr(func, "argtypes", argtypes) return lib except Exception as ex: - warnings.warn(f"Can't load libtesseract: {ex}", RuntimeWarning) + #warnings.warn(f"Can't load libtesseract: {ex}", RuntimeWarning) return None diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index 2efc4e1d..40427114 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -38,8 +38,10 @@ from aiohttp.web import StreamResponse import os from aiohttp import ClientConnectionError +from aiohttp import ClientPayloadError from aiohttp import ClientSession from aiohttp import UnixConnector +from aiohttp import ClientTimeout from urllib.parse import urlencode @@ -329,9 +331,11 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins socket_path = self.__streamer.get_path() query_string = urlencode(request.query) headers = request.headers.copy() + time_out = ClientTimeout(total=10) try: - async with ClientSession(connector=UnixConnector(path=socket_path)) as session: - backend_url = f'http://localhost/stream?{query_string}' if query_string else 'http://localhost/stream' + #async with ClientSession(connector=UnixConnector(path=socket_path)) as session: + async with ClientSession() as session: + backend_url = f'http://localhost:8000/stream?{query_string}' if query_string else 'http://localhost:8000/stream' async with session.get(backend_url, headers=headers) as resp: response = StreamResponse(status=resp.status, reason=resp.reason, headers=resp.headers) await response.prepare(request) @@ -341,7 +345,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins break await response.write(chunk) return response - except ClientConnectionError: + except (ClientConnectionError, ClientPayloadError, ConnectionResetError): return Response(status=500, text="Client connection was closed") diff --git a/kvmd/apps/kvmd/streamer.py b/kvmd/apps/kvmd/streamer.py index 3422a9bf..0f117e4a 100644 --- a/kvmd/apps/kvmd/streamer.py +++ b/kvmd/apps/kvmd/streamer.py @@ -20,6 +20,7 @@ # ========================================================================== # +import platform import signal import asyncio import asyncio.subprocess @@ -302,7 +303,8 @@ class Streamer: # pylint: disable=too-many-instance-attributes self.__notifier.notify(self.__ST_STREAMER) get_logger(0).info("Installing SIGUSR2 streamer handler ...") - asyncio.get_event_loop().add_signal_handler(signal.SIGUSR2, signal_handler) + if platform.system() != 'Windows': + asyncio.get_event_loop().add_signal_handler(signal.SIGUSR2, signal_handler) prev: dict = {} while True: @@ -338,7 +340,7 @@ class Streamer: # pylint: disable=too-many-instance-attributes session = self.__ensure_client_session() try: return (await session.get_state()) - except (aiohttp.ClientConnectionError, aiohttp.ServerConnectionError): + except (aiohttp.ClientConnectionError, aiohttp.ServerConnectionError,TimeoutError,asyncio.CancelledError,asyncio.TimeoutError,asyncio.CancelledError): pass except Exception: get_logger().exception("Invalid streamer response from /state") diff --git a/kvmd/clients/__init__.py b/kvmd/clients/__init__.py index f0645fd2..e06bda14 100644 --- a/kvmd/clients/__init__.py +++ b/kvmd/clients/__init__.py @@ -75,12 +75,12 @@ class BaseHttpClient: def _make_http_session(self, headers: dict[str, str] | None = None) -> aiohttp.ClientSession: connector = None #这里临时使用 socket ,后期考虑是否使用 http 方式 - use_unix_socket = True + use_unix_socket = False if use_unix_socket: connector = aiohttp.UnixConnector(path=self.__unix_path) base_url = "http://localhost:0" # 继续使用 Unix 域套接字 else: - base_url = "http://127.0.0.1:8001" # 使用指定的 IP 和端口 + base_url = "http://127.0.0.1:8000" # 使用指定的 IP 和端口 #print("base_url:", base_url) return aiohttp.ClientSession( diff --git a/kvmd/clients/streamer.py b/kvmd/clients/streamer.py index 5369892e..5ea6af98 100644 --- a/kvmd/clients/streamer.py +++ b/kvmd/clients/streamer.py @@ -32,7 +32,7 @@ from typing import Generator from typing import AsyncGenerator import aiohttp -import ustreamer +#import ustreamer from PIL import Image as PilImage diff --git a/kvmd/keyboard/printer.py b/kvmd/keyboard/printer.py index efee6d44..07e75e75 100644 --- a/kvmd/keyboard/printer.py +++ b/kvmd/keyboard/printer.py @@ -30,29 +30,8 @@ from .mappings import WebModifiers # ===== -def _load_libxkbcommon() -> ctypes.CDLL: - path = ctypes.util.find_library("xkbcommon") - if not path: - raise RuntimeError("Where is libxkbcommon?") - assert path - lib = ctypes.CDLL(path) - for (name, restype, argtypes) in [ - ("xkb_utf32_to_keysym", ctypes.c_uint32, [ctypes.c_uint32]), - ]: - func = getattr(lib, name) - if not func: - raise RuntimeError(f"Where is libc.{name}?") - setattr(func, "restype", restype) - setattr(func, "argtypes", argtypes) - return lib -_libxkbcommon = _load_libxkbcommon() - - -def _ch_to_keysym(ch: str) -> int: - assert len(ch) == 1 - return _libxkbcommon.xkb_utf32_to_keysym(ord(ch)) # ===== @@ -84,10 +63,6 @@ def text_to_web_keys( # pylint: disable=too-many-branches ch = "--" if not ch.isprintable(): continue - try: - keys = symmap[_ch_to_keysym(ch)] - except Exception: - continue for (modifiers, key) in keys.items(): if modifiers & SymmapModifiers.CTRL: diff --git a/kvmd/libc.py b/kvmd/libc.py index 53b25733..f004acde 100644 --- a/kvmd/libc.py +++ b/kvmd/libc.py @@ -34,34 +34,4 @@ from ctypes import c_void_p # ===== def _load_libc() -> ctypes.CDLL: - path = ctypes.util.find_library("c") - if not path: - raise RuntimeError("Where is libc?") - assert path - lib = ctypes.CDLL(path) - for (name, restype, argtypes) in [ - ("inotify_init", c_int, []), - ("inotify_add_watch", c_int, [c_int, c_char_p, c_uint32]), - ("inotify_rm_watch", c_int, [c_int, c_uint32]), - ("renameat2", c_int, [c_int, c_char_p, c_int, c_char_p, c_uint]), - ("free", c_int, [c_void_p]), - ]: - func = getattr(lib, name) - if not func: - raise RuntimeError(f"Where is libc.{name}?") - setattr(func, "restype", restype) - setattr(func, "argtypes", argtypes) - return lib - - -_libc = _load_libc() - - -# ===== -get_errno = ctypes.get_errno - -inotify_init = _libc.inotify_init -inotify_add_watch = _libc.inotify_add_watch -inotify_rm_watch = _libc.inotify_rm_watch -renameat2 = _libc.renameat2 -free = _libc.free + pass diff --git a/kvmd/plugins/__init__.py b/kvmd/plugins/__init__.py index ce2567f1..17bd4622 100644 --- a/kvmd/plugins/__init__.py +++ b/kvmd/plugins/__init__.py @@ -52,8 +52,8 @@ def get_plugin_class(sub: str, name: str) -> type[BasePlugin]: assert name if name.startswith("_"): raise UnknownPluginError(f"Unknown plugin '{sub}/{name}'") - try: - module = importlib.import_module(f"kvmd.plugins.{sub}.{name}") - except ModuleNotFoundError: - raise UnknownPluginError(f"Unknown plugin '{sub}/{name}'") + #try: + module = importlib.import_module(f"kvmd.plugins.{sub}.{name}") + #except ModuleNotFoundError: + #raise UnknownPluginError(f"Unknown plugin '{sub}/{name}'") return getattr(module, "Plugin") diff --git a/kvmd/plugins/hid/ch9329/__init__.py b/kvmd/plugins/hid/ch9329/__init__.py index 1b235090..05459fa2 100644 --- a/kvmd/plugins/hid/ch9329/__init__.py +++ b/kvmd/plugins/hid/ch9329/__init__.py @@ -200,6 +200,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst while not self.__stop_event.is_set(): try: self.__hid_loop() + time.sleep(1) except Exception: logger.exception("Unexpected error in the run loop") time.sleep(1) @@ -222,6 +223,10 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst self.__process_cmd(conn, b"") else: self.__process_cmd(conn, cmd) + except KeyboardInterrupt: + get_logger(0).info("KeyboardInterrupt received, exiting HID loop.") + self.clear_events() + break except Exception: self.clear_events() get_logger(0).exception("Unexpected error in the HID loop") diff --git a/kvmd/plugins/ugpio/gpio.py b/kvmd/plugins/ugpio/gpio.py index 6cda826b..311acaf1 100644 --- a/kvmd/plugins/ugpio/gpio.py +++ b/kvmd/plugins/ugpio/gpio.py @@ -23,7 +23,7 @@ from typing import Callable from typing import Any -import gpiod +#import gpiod from ... import aiotools from ... import aiogp diff --git a/kvmd_data/etc/kvmd/override.yaml b/kvmd_data/etc/kvmd/override.yaml index 4ee138dd..40f04548 100644 --- a/kvmd_data/etc/kvmd/override.yaml +++ b/kvmd_data/etc/kvmd/override.yaml @@ -26,7 +26,7 @@ kvmd: hid: type: ch9329 - device: /dev/ttyUSB0 + device: COM7 speed: 115200 read_timeout: 0.3 @@ -53,7 +53,7 @@ kvmd: streamer: resolution: - default: 1920x1080 + default: 1280x720 forever: true @@ -64,32 +64,30 @@ kvmd: h264_bitrate: default: 8000 - cmd: - - "kvmd_data/usr/bin/ustreamer" - - "--device=/dev/video0" - - "--persistent" - - "--format=mjpeg" - - "--resolution={resolution}" - - "--desired-fps={desired_fps}" - - "--drop-same-frames=30" - - "--last-as-blank=0" - - "--unix={unix}" - - "--unix-rm" - - "--unix-mode=777" - - "--exit-on-parent-death" - - "--notify-parent" - - "--no-log-colors" - - "--jpeg-sink=kvmd::ustreamer::jpeg" - - "--jpeg-sink-mode=0660" - - "--slowdown" + pre_start_cmd: kvmd_data/win/true.exe + post_stop_cmd: kvmd_data/win/true.exe - unix: kvmd_data/run/kvmd/ustreamer.sock + cmd: + - "C:/Users/mofen/miniconda3/python.exe" + - "ustreamer-win/ustreamer-win.py" + - "--device=0" + - "--resolution={resolution}" + - "--fps={desired_fps}" + - "--quality=100" + + + unix: http://localhost:8000 ipmi: auth: file: kvmd_data/etc/kvmd/ipmipasswd +pst: + remount_cmd: + - "kvmd_data/win/true.exe" + + vnc: keymap: kvmd_data/usr/share/kvmd/keymaps/en-us mouse_output: usb @@ -110,10 +108,20 @@ vnc: otgnet: commands: + pre_start_cmd: + - "kvmd_data/win/true.exe" + post_stop_cmd: + - "kvmd_data/win/true.exe" post_start_cmd: - - "/bin/true" + - "kvmd_data/win/true.exe" pre_stop_cmd: - - "/bin/true" + - "kvmd_data/win/true.exe" + iface: + ip_cmd: + - "kvmd_data/win/true.exe" + firewall: + iptables_cmd: + - "kvmd_data/win/true.exe" nginx: http: @@ -123,4 +131,4 @@ nginx: janus: cmd: - - "/bin/true" \ No newline at end of file + - "kvmd_data/win/true.exe" \ No newline at end of file diff --git a/kvmd_data/usr/share/kvmd/web/kvm/index.html b/kvmd_data/usr/share/kvmd/web/kvm/index.html index f5799301..774a64bb 100644 --- a/kvmd_data/usr/share/kvmd/web/kvm/index.html +++ b/kvmd_data/usr/share/kvmd/web/kvm/index.html @@ -899,7 +899,7 @@
-
+