diff options
author | David Timber <dxdt@dev.snart.me> | 2025-04-14 19:37:42 +0200 |
---|---|---|
committer | David Timber <dxdt@dev.snart.me> | 2025-04-14 19:37:42 +0200 |
commit | c2178aa9e7af2bff38af7f62df07b0858b66abeb (patch) | |
tree | 88c7b87ef7d6268419f25ca06ce5c28a487958a4 /src/palhm | |
parent | c2e41c5baca02e15c1b9d282280314d3e13d4814 (diff) |
Change command invocation semantics to ...
`python -m palhm`
Diffstat (limited to 'src/palhm')
-rwxr-xr-x | src/palhm/__main__.py | 220 | ||||
-rw-r--r-- | src/palhm/conf/crontab | 4 | ||||
-rw-r--r-- | src/palhm/conf/palhm-boot-report.service | 2 |
3 files changed, 223 insertions, 3 deletions
diff --git a/src/palhm/__main__.py b/src/palhm/__main__.py new file mode 100755 index 0000000..cdb21b7 --- /dev/null +++ b/src/palhm/__main__.py @@ -0,0 +1,220 @@ +# Copyright (c) 2022 David Timber <dxdt@dev.snart.me> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +import importlib +import logging +import os +import sys +from abc import ABC, abstractmethod +from getopt import getopt + +import palhm +from palhm.exceptions import InvalidConfigError + + +class ProgConf: + conf = "/etc/palhm/palhm.jsonc" + cmd = None + override_vl = None + ctx = None + + def alloc_ctx (): + ProgConf.ctx = palhm.setup_conf(palhm.load_conf(ProgConf.conf)) + if not ProgConf.override_vl is None: + ProgConf.ctx.l.setLevel(ProgConf.override_vl) + +def err_unknown_cmd (): + sys.stderr.write( + "Unknown command. Run '" + sys.executable + " -m palhm help' for usage.\n") + exit(2) + +class Cmd (ABC): + @abstractmethod + def do_cmd (self): + ... + +class ConfigCmd (Cmd): + def __init__ (self, *args, **kwargs): + pass + + def do_cmd (self): + ProgConf.alloc_ctx() + print(ProgConf.ctx) + return 0 + + def print_help (): + print( +"Usage: " + sys.executable + " -m palhm config" + ''' +Load and parse config. Print the structure to stdout.''') + +class RunCmd (Cmd): + def __init__ (self, optlist, args): + self.optlist = optlist + self.args = args + + def do_cmd (self): + ProgConf.alloc_ctx() + + if self.args and self.args[0]: # empty string as "default" + task = self.args[0] + else: + task = palhm.DEFAULT.RUN_TASK.value + + ProgConf.ctx.task_map[task].run(ProgConf.ctx) + + return 0 + + def print_help (): + print( +"Usage: " + sys.executable + " -m palhm run [TASK]" + ''' +Run a task in config. Run the "''' + palhm.DEFAULT.RUN_TASK.value + +'''" task if [TASK] is not specified.''') + +class ModsCmd (Cmd): + def __init__ (self, *args, **kwargs): + pass + + def _walk_mods (self, path: str): + def is_mod_dir (path: str) -> bool: + try: + for i in os.scandir(path): + if i.name.startswith("__init__.py"): + return True + except NotADirectoryError: + pass + return False + + def is_mod_file (path: str) -> str: + if not os.path.isfile(path): + return None + + try: + pos = path.rindex(".") + if path[pos + 1:].startswith("py"): + return os.path.basename(path[:pos]) + except ValueError: + pass + + for i in os.scandir(path): + if i.name.startswith("_"): + continue + elif is_mod_dir(i.path): + print(i.name) + self._walk_mods(i.path) + else: + name = is_mod_file(i.path) + if name: + print(name) + + def do_cmd (self): + for i in importlib.util.find_spec("palhm.mod").submodule_search_locations: + self._walk_mods(i) + + return 0 + + def print_help (): + print( +"Usage: " + sys.executable + " -m palhm mods" + ''' +Prints the available modules to stdout.''') + +class BootReportCmd (Cmd): + def __init__ (self, *args, **kwargs): + pass + + def do_cmd (self): + ProgConf.alloc_ctx() + + if ProgConf.ctx.boot_report is None: + raise InvalidConfigError("'boot-report' not configured") + + return ProgConf.ctx.boot_report.do_send(ProgConf.ctx) + + def print_help (): + print( +"Usage: " + sys.executable + " -m palhm boot-report" + ''' +Send mail of boot report to recipients configured.''') + +class HelpCmd (Cmd): + def __init__ (self, optlist, args): + self.optlist = optlist + self.args = args + + def do_cmd (self): + if len(self.args) >= 2: + if not args[0] in CmdMap: + err_unknown_cmd() + else: + CmdMap[self.args[0]].print_help() + else: + HelpCmd.print_help() + + return 0 + + def print_help (): + print( +"Usage: " + sys.executable + " -m palhm [options] CMD [command options ...]" + ''' +Options: + -q Set the verbosity level to 0(CRITIAL). Overrides config + -v Increase the verbosity level by 1. Overrides config + -f FILE Load config from FILE instead of the hard-coded default +Config: ''' + ProgConf.conf + ''' +Commands: + run run a task + config load config and print the contents + help [CMD] print this message and exit normally if [CMD] is not specified. + Print usage of [CMD] otherwise + mods list available modules + boot-report mail boot report''') + + return 0 + +CmdMap = { + "config": ConfigCmd, + "run": RunCmd, + "help": HelpCmd, + "mods": ModsCmd, + "boot-report": BootReportCmd +} + +optlist, args = getopt(sys.argv[1:], "qvf:") +optkset = set() +for p in optlist: + optkset.add(p[0]) + +if "-v" in optkset and "-q" in optkset: + sys.stderr.write("Options -v and -q cannot not used together.\n") + exit(2) + +if not args or not args[0] in CmdMap: + err_unknown_cmd() + +for p in optlist: + if p[0] == "-q": ProgConf.override_vl = logging.ERROR + elif p[0] == "-v": + if ProgConf.override_vl is None: + ProgConf.override_vl = palhm.DEFAULT.VL.value - 10 + else: + ProgConf.override_vl -= 10 + elif p[0] == "-f": ProgConf.conf = p[1] + +logging.basicConfig(format = "%(name)s %(message)s") + +ProgConf.cmd = CmdMap[args[0]](optlist, args) +del args[0] +exit(ProgConf.cmd.do_cmd()) diff --git a/src/palhm/conf/crontab b/src/palhm/conf/crontab index d0eeda6..9e59a16 100644 --- a/src/palhm/conf/crontab +++ b/src/palhm/conf/crontab @@ -1,6 +1,6 @@ # PALHM will produce stderr on exception. Mail the output to root MAILTO="root" # Run default task every Sunday at midnight -0 0 * * sun root /var/lib/PALHM/src/palhm.py -q run +0 0 * * sun root python -m palhm -q run # Check dnssec validity every hour -# 0 * * * * root systemd-run -qP -p User=palhm -p Nice=15 -p ProtectSystem=strict -p ReadOnlyPaths=/ -p PrivateDevices=true --wait /var/lib/PALHM/src/palhm.py -q run check-dnssec +# 0 * * * * root systemd-run -qP -p User=palhm -p Nice=15 -p ProtectSystem=strict -p ReadOnlyPaths=/ -p PrivateDevices=true --wait python -m palhm -q run check-dnssec diff --git a/src/palhm/conf/palhm-boot-report.service b/src/palhm/conf/palhm-boot-report.service index 0b4ba67..16a7c6d 100644 --- a/src/palhm/conf/palhm-boot-report.service +++ b/src/palhm/conf/palhm-boot-report.service @@ -4,7 +4,7 @@ After=postfix.service sendmail.service exim.service dovecot.service network-onli [Service] Type=oneshot -ExecStart=/var/lib/PALHM/src/palhm.py -q boot-report +ExecStart=/usr/bin/python -m palhm -q boot-report Nice=10 [Install] |