diff options
author | Maxim Devaev <[email protected]> | 2021-09-23 17:03:10 +0300 |
---|---|---|
committer | Maxim Devaev <[email protected]> | 2021-09-23 17:12:15 +0300 |
commit | ab92a2d7084dea554edb9d5d0032327b302ef9f3 (patch) | |
tree | 209bc6045d9dd3ea1e40a9b0d9559f7ddb67d852 /kvmd/apps/watchdog/__init__.py | |
parent | 8d53c89a6aa3e4b5e9729aeadf8c0a20b985081d (diff) |
rtc watchdog for v3
Diffstat (limited to 'kvmd/apps/watchdog/__init__.py')
-rw-r--r-- | kvmd/apps/watchdog/__init__.py | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/kvmd/apps/watchdog/__init__.py b/kvmd/apps/watchdog/__init__.py new file mode 100644 index 00000000..0caa39a6 --- /dev/null +++ b/kvmd/apps/watchdog/__init__.py @@ -0,0 +1,119 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2021 Maxim Devaev <[email protected]> # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <https://www.gnu.org/licenses/>. # +# # +# ========================================================================== # + + +import argparse +import errno +import time + +from typing import List +from typing import Optional + +from ...logging import get_logger + +from ...yamlconf import Section + +from ... import env + +from .. import init + + +# ===== +class RtcIsNotAvailableError(Exception): + pass + + +# ===== +def _join_rtc(device: int, key: str) -> str: + return f"{env.SYSFS_PREFIX}/sys/class/rtc/rtc{device}/{key}" + + +def _read_int(device: int, key: str) -> int: + with open(_join_rtc(device, key)) as value_file: + return int(value_file.read().strip() or "0") + + +def _write_int(device: int, key: str, value: int) -> None: + with open(_join_rtc(device, key), "w") as value_file: + value_file.write(str(value)) + + +def _reset_alarm(device: int, timeout: int) -> None: + now = _read_int(device, "since_epoch") + if now == 0: + raise RtcIsNotAvailableError("Current UNIX time == 0") + try: + for wake in [0, now + timeout]: + _write_int(device, "wakealarm", wake) + except OSError as err: + if err.errno != errno.EIO: + raise + raise RtcIsNotAvailableError("IO error, probably the supercapacitor is not charged") + + +# ===== +def _cmd_run(config: Section) -> None: + logger = get_logger(0) + logger.info("Running watchdog loop on RTC%d ...", config.device) + fail = False + try: + while True: + try: + _reset_alarm(config.device, config.timeout) + except RtcIsNotAvailableError as err: + if not fail: + logger.error("RTC%d is not available now: %s; waiting ...", config.device, err) + fail = True + else: + if fail: + logger.info("RTC%d is available, working ...", config.device) + fail = False + time.sleep(config.interval) + except (SystemExit, KeyboardInterrupt): + if not fail: + _reset_alarm(config.device, config.timeout) + logger.info("The watchdog remains alarmed. Use 'kvmd-watchdog cancel' to disarm it") + logger.info("Bye-bye") + + +def _cmd_cancel(config: Section) -> None: + _write_int(config.device, "wakealarm", 0) + + +# ===== +def main(argv: Optional[List[str]]=None) -> None: + (parent_parser, argv, config) = init(add_help=False, argv=argv) + parser = argparse.ArgumentParser( + prog="kvmd-watchdog", + description="RTC-based hardware watchdog", + parents=[parent_parser], + ) + parser.set_defaults(cmd=(lambda *_: parser.print_help())) + subparsers = parser.add_subparsers() + + cmd_run_parser = subparsers.add_parser("run", help="Run watchdog loop") + cmd_run_parser.set_defaults(cmd=_cmd_run) + + cmd_cancel_parser = subparsers.add_parser("cancel", help="Cancel armed timeout") + cmd_cancel_parser.set_defaults(cmd=_cmd_cancel) + + options = parser.parse_args(argv[1:]) + options.cmd(config.watchdog) |