diff --git a/.gitignore b/.gitignore index 67da934..f5a0dd9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /nilrt-snac-*.tar.gz /src/nilrt-snac-conflicts/*.tar.gz /src/nilrt-snac-conflicts/*.ipk + +__pycache__/ \ No newline at end of file diff --git a/Makefile b/Makefile index fc58641..b8e8ab8 100644 --- a/Makefile +++ b/Makefile @@ -22,16 +22,18 @@ libdir ?= $(exec_prefix)/lib sbindir ?= $(exec_prefix)/sbin sysconfdir ?= $(prefix)/etc +PYTHON_FILES = \ + $(shell find nilrt_snac -name \*.py -or -name \*.txt) + SRC_FILES = \ - src/configure-nilrt-snac \ src/nilrt-snac-conflicts/control \ src/ni-wireguard-labview/ni-wireguard-labview.initd \ src/ni-wireguard-labview/wglv0.conf \ src/nilrt-snac \ - src/util.sh \ DIST_FILES = \ $(SRC_FILES) \ + $(PYTHON_FILES) \ LICENSE \ README.md \ Makefile \ @@ -52,7 +54,7 @@ src/nilrt-snac-conflicts/nilrt-snac-conflicts.ipk : # PHONY TARGETS # ################# -.PHONY : all clean dist install uninstall +.PHONY : all clean dist install uninstall test all : src/nilrt-snac-conflicts/nilrt-snac-conflicts.ipk @@ -70,10 +72,14 @@ install : all $(DIST_FILES) install -o 0 -g 0 --mode=0755 -t "$(DESTDIR)$(sbindir)" \ src/nilrt-snac - mkdir -p $(DESTDIR)$(libdir)/$(PACKAGE) - install --mode=0444 -t "$(DESTDIR)$(libdir)/$(PACKAGE)" \ - src/configure-nilrt-snac \ - src/util.sh \ + mkdir -p $(DESTDIR)$(libdir)/$(PACKAGE)/nilrt_snac + install --mode=0444 -t "$(DESTDIR)$(libdir)/$(PACKAGE)/nilrt_snac" \ + $(shell find nilrt_snac -name \*.py -or -name \*.txt -maxdepth 1) \ + + # install doesn't support recursive copy + mkdir -p $(DESTDIR)$(libdir)/$(PACKAGE)/nilrt_snac/_configs + install --mode=0444 -t "$(DESTDIR)$(libdir)/$(PACKAGE)/nilrt_snac/_configs" \ + $(shell find nilrt_snac/_configs -name \*.py -maxdepth 1) \ mkdir -p $(DESTDIR)$(docdir)/$(PACKAGE) install --mode=0444 -t "$(DESTDIR)$(docdir)/$(PACKAGE)" \ diff --git a/nilrt_snac/OpkgHelper.py b/nilrt_snac/OpkgHelper.py new file mode 100644 index 0000000..c27ba90 --- /dev/null +++ b/nilrt_snac/OpkgHelper.py @@ -0,0 +1,89 @@ +"""Class to help with managing opkg install/uninstall.""" + +import subprocess +from typing import List + +from nilrt_snac import logger +from nilrt_snac._common import get_distro + +OPKG_SNAC_CONF = "/etc/opkg/snac.conf" + + +class OpkgHelper: # noqa: D101 - Missing docstring in public class (auto-generated noqa) + def __init__(self) -> None: # noqa: D107 - Missing docstring in __init__ (auto-generated noqa) + self._installed_packages: List[str] = [] + # This runs before the prereqs are checked + if get_distro() != "nilrt": + logger.warning("Not running on nilrt, can't get list of installed packages.") + return + + self.update() + packages = self._run(["list-installed"]) + + for line in packages.split("\n"): + pkg = line.split(" - ") + if len(pkg) > 1: + self._installed_packages.append(pkg[0]) + + def _run( # noqa: D102 - Missing docstring in public method (auto-generated noqa) + self, command: List[str] + ) -> str: + result = subprocess.run(["opkg"] + command, stdout=subprocess.PIPE) + + if result.returncode != 0: + raise RuntimeError( + f"Command 'opkg {' '.join(command)}' failed with return code {result.returncode}" + ) + + return result.stdout.decode() + + def set_dry_run( # noqa: D102 - Missing docstring in public method (auto-generated noqa) + self, dry_run: bool + ) -> None: + self._dry_run = dry_run + + def install( # noqa: D102 - Missing docstring in public method (auto-generated noqa) + self, package: str, force_reinstall: bool = False + ) -> None: + if not self.is_installed(package): + cmd = ["opkg", "install"] + if force_reinstall: + cmd.append("--force-reinstall") + cmd.append(package) + if not self._dry_run: + subprocess.run(cmd, check=True) + self._installed_packages.append(package) + else: + logger.debug(f"{package} already installed") + + def remove( # noqa: D102 - Missing docstring in public method (auto-generated noqa) + self, package: str, ignore_installed=False, force_essential: bool = False, force_depends: bool = False + ) -> None: + + if ignore_installed or self.is_installed(package): + check = False if ignore_installed else True + cmd = ["opkg", "remove"] + if force_essential: + cmd.append("--force-removal-of-essential-packages") + if force_depends: + cmd.append("--force-depends") + cmd.append(package) + if not self._dry_run: + subprocess.run(cmd, check=check) + if not ignore_installed: + self._installed_packages.remove(package) + else: + logger.debug(f"{package} already uninstalled") + + def is_installed( # noqa: D102 - Missing docstring in public method (auto-generated noqa) + self, package: str + ) -> bool: + return package in self._installed_packages + + def update( # noqa: D102 - Missing docstring in public method (auto-generated noqa) + self, + ) -> None: + self._run(["update"]) + + +opkg_helper = OpkgHelper() diff --git a/nilrt_snac/__init__.py b/nilrt_snac/__init__.py new file mode 100644 index 0000000..42c2563 --- /dev/null +++ b/nilrt_snac/__init__.py @@ -0,0 +1,28 @@ +"""nilrt-snac.""" + +import logging +import pathlib +from enum import IntEnum + +__version__ = "0.9.0" + +SNAC_DATA_DIR = pathlib.Path(__file__).parents[3] / "share" / "nilrt-snac" + +logger = logging.getLogger(__package__) +logger.addHandler(logging.NullHandler()) + + +class Errors(IntEnum): # noqa: D101 - Missing docstring in public class (auto-generated noqa) + EX_OK = 0 + EX_ERROR = 1 + EX_USAGE = 2 + EX_BAD_ENVIRONMENT = 128 + EX_CHECK_FAILURE = 129 + + +class SNACError(Exception): # noqa: D101 - Missing docstring in public class (auto-generated noqa) + def __init__( # noqa: D107 - Missing docstring in __init__ (auto-generated noqa) + self, message, return_code=Errors.EX_ERROR + ): + super().__init__(message) + self.return_code = return_code diff --git a/nilrt_snac/__main__.py b/nilrt_snac/__main__.py new file mode 100644 index 0000000..7dfa4cc --- /dev/null +++ b/nilrt_snac/__main__.py @@ -0,0 +1,145 @@ +"""nilrt-snac entry points.""" + +import argparse +import logging +import sys +from typing import List, Optional + +from nilrt_snac._pre_reqs import verify_prereqs +from nilrt_snac.OpkgHelper import opkg_helper +from nilrt_snac._configs import CONFIGS + + +from nilrt_snac import Errors, logger, SNACError, __version__ + +VERSION_DESCRIPTION = \ +f""" +nilrt-snac {__version__} +Copyright (C) 2024 NI (Emerson Electric) +License MIT: MIT License +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +""" + + +def _configure(args: argparse.Namespace) -> int: + """Configure SNAC mode.""" + logger.warning("!! THIS TOOL IS IN-DEVELOPMENT AND APPROPRIATE ONLY FOR DEVELOPER TESTING !!") + logger.warning("!! Running this tool will irreversibly alter the state of your system. !!") + logger.warning("!! If you are accessing your system using WiFi, you will lose connection. !!") + + if args.yes: + consent = "y" + else: + consent = input("Do you want to continue with SNAC configuration? [y/N] ") + + if consent.lower() not in ["y", "yes"]: + return Errors.EX_OK + + print("Configuring SNAC mode.") + for config in CONFIGS: + config.configure(args) + + print("!! A reboot is now required to affect your system configuration. !!") + print("!! Login with user 'root' and no password. !!") + + return Errors.EX_OK + + +def _verify(args: argparse.Namespace) -> int: + """Configure SNAC mode.""" + print("Validating SNAC mode.") + valid = True + for config in CONFIGS: + new_valid = config.verify(args) + valid = valid and new_valid + + if not valid: + raise SNACError("SNAC mode is not configured correctly.", Errors.EX_CHECK_FAILURE) + return Errors.EX_OK + + +def _parse_args(argv: List[str]) -> argparse.Namespace: + """Top level entry point for the command line interface.""" + parser = argparse.ArgumentParser(description="Utility for enabling SNAC mode on NI Linux RT.") + + subparsers = parser.add_subparsers(help="Commands for SNAC mode.", dest="cmd") + subparsers.required = False + + configure_parser = subparsers.add_parser("configure", help="Set SNAC mode") + configure_parser.add_argument( + "-y", + "--yes", + action="store_true", + help="Consent to changes", + ) + configure_parser.set_defaults(func=_configure) + + verify_parser = subparsers.add_parser("verify", help="Verify SNAC mode configured correctly") + verify_parser.set_defaults(func=_verify) + + debug_group = parser.add_argument_group("Debug") + debug_group.add_argument( + "-v", + "--verbose", + action="store_true", + default=0, + help="Print debug information. Can be repeated for more detailed output.", + ) + debug_group.add_argument( + "-n", + "--dry-run", + action="store_true", + ) + + parser.add_argument( + "-V", + "--version", + action="store_true", + default=0, + help="Print version.", + ) + + args = parser.parse_args(argv) + + return args + + +def main( # noqa: D103 - Missing docstring in public function (auto-generated noqa) + argv: Optional[List[str]] = None, +): + if argv is None: + argv = sys.argv + args = _parse_args(argv[1:]) + + log_level = logging.INFO + if args.verbose: + log_level = logging.DEBUG + args.logging_level = log_level + + log_format = "(%(relativeCreated)5d) %(levelname)-5s %(name)s.%(funcName)s: %(message)s" + logging.basicConfig(level=args.logging_level, format=log_format) + + logger.debug("Arguments: %s", args) + opkg_helper.set_dry_run(args.dry_run) + + if args.version: + print(VERSION_DESCRIPTION) + return Errors.EX_OK + + if args.cmd is None: + logger.error("Command required: {configure, verify}, see --help for more information.") + return Errors.EX_USAGE + + try: + if not args.dry_run: + verify_prereqs() + ret_val = args.func(args) + except SNACError as e: + logger.error(e) + return e.return_code + + return ret_val + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/nilrt_snac/_common.py b/nilrt_snac/_common.py new file mode 100644 index 0000000..a52e21c --- /dev/null +++ b/nilrt_snac/_common.py @@ -0,0 +1,12 @@ +import pathlib + +def get_distro(): + try: + os_release = pathlib.Path("/etc/os-release") + if os_release.exists(): + with open(os_release, "r") as f: + for line in f: + if line.startswith("ID="): + return line.split("=")[1].strip() + except NameError: + return None diff --git a/nilrt_snac/_configs/__init__.py b/nilrt_snac/_configs/__init__.py new file mode 100644 index 0000000..08700f2 --- /dev/null +++ b/nilrt_snac/_configs/__init__.py @@ -0,0 +1,29 @@ +from typing import List + +from nilrt_snac._configs._base_config import _BaseConfig +from nilrt_snac._configs._console_config import _ConsoleConfig +from nilrt_snac._configs._cryptsetup_config import _CryptSetupConfig +from nilrt_snac._configs._faillock_config import _FaillockConfig +from nilrt_snac._configs._niauth_config import _NIAuthConfig +from nilrt_snac._configs._ntp_config import _NTPConfig +from nilrt_snac._configs._opkg_config import _OPKGConfig +from nilrt_snac._configs._pwquality_config import _PWQualityConfig +from nilrt_snac._configs._wifi_config import _WIFIConfig +from nilrt_snac._configs._wireguard_config import _WireguardConfig +from nilrt_snac._configs._x11_config import _X11Config + +# USBGuard is not supported for 1.0, but may be added in the future +# from nilrt_snac._configs._usbguard_config import _USBGuardConfig + +CONFIGS: List[_BaseConfig] = [ + _NTPConfig(), + _OPKGConfig(), + _WireguardConfig(), + _CryptSetupConfig(), + _NIAuthConfig(), + _WIFIConfig(), + _FaillockConfig(), + _X11Config(), + _ConsoleConfig(), + _PWQualityConfig(), +] diff --git a/nilrt_snac/_configs/_base_config.py b/nilrt_snac/_configs/_base_config.py new file mode 100644 index 0000000..2adb61a --- /dev/null +++ b/nilrt_snac/_configs/_base_config.py @@ -0,0 +1,13 @@ +import argparse +from abc import ABC, abstractmethod + + +class _BaseConfig(ABC): + + @abstractmethod + def configure(self, args: argparse.Namespace) -> None: + raise NotImplementedError + + @abstractmethod + def verify(self, args: argparse.Namespace) -> bool: + raise NotImplementedError diff --git a/nilrt_snac/_configs/_config_file.py b/nilrt_snac/_configs/_config_file.py new file mode 100644 index 0000000..97bec16 --- /dev/null +++ b/nilrt_snac/_configs/_config_file.py @@ -0,0 +1,67 @@ +"""Helper class to read/write and update configuration files.""" + +import pathlib +import re +from typing import Union + +from nilrt_snac import logger + + +class _ConfigFile: + """Helper class to read/write and update configuration files.""" + + def __init__(self, path: Union[pathlib.Path, str]) -> None: + """Initialize the ConfigFile object. + + Args: path: The path to the configuration file. + """ + if type(path) is str: + path = pathlib.Path(path) + + self.path = path + self._config = path.read_text() if path.exists() else "" + self._mode = path.stat().st_mode if path.exists() else 0o700 + + def save(self, dry_run: bool) -> None: + """Save the configuration file.""" + if dry_run: + print("dry-run: Not saved") + else: + self.path.write_text(self._config) + self.path.chmod(self._mode) + logger.debug(f"Contents of {self.path}:") + logger.debug(self._config) + + def update(self, key: str, value: str) -> None: + """Update the configuration file with the given key and value. + + Args: + key: Search RE pattern to find the key. + value: The value to replace the key with. + + Uses the re.sub() method to replace the key with the value. + """ + self._config = re.sub(key, value, self._config, flags=re.MULTILINE) + + def add(self, value: str) -> None: + """Add the value string to the config file. + + Args: + value: String to add + """ + self._config += value + + def exists(self) -> bool: + return self.path.exists() + + def chmod(self, mode: int) -> None: + self._mode = mode + + def contains(self, key: str) -> bool: + """Check if the configuration file contains the given key. + + Args: key: RE pattern to search for in the configuration file. + + Returns: True if the key is found, False otherwise. + """ + return bool(re.search(key, self._config)) diff --git a/nilrt_snac/_configs/_console_config.py b/nilrt_snac/_configs/_console_config.py new file mode 100644 index 0000000..632cf7e --- /dev/null +++ b/nilrt_snac/_configs/_console_config.py @@ -0,0 +1,34 @@ +import argparse +import subprocess + +from nilrt_snac._configs._base_config import _BaseConfig + +from nilrt_snac import logger + + +class _ConsoleConfig(_BaseConfig): + def __init__(self): + pass # Nothing to do for now + + def configure(self, args: argparse.Namespace) -> None: + print("Configuring console access...") + dry_run: bool = args.dry_run + if not dry_run: + logger.debug("Disabling console access") + subprocess.run( + ["nirtcfg", "--set", "section=systemsettings,token=consoleout.enabled,value=False"], + check=True, + ) + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying console access configuration...") + valid = True + result = subprocess.run( + ["nirtcfg", "--get", "section=systemsettings,token=consoleout.enabled"], + check=True, + stdout=subprocess.PIPE, + ) + if result.stdout.decode().strip() != "False": + valid = False + logger.error("FOUND: console access not diabled") + return valid diff --git a/nilrt_snac/_configs/_cryptsetup_config.py b/nilrt_snac/_configs/_cryptsetup_config.py new file mode 100644 index 0000000..7b0fafc --- /dev/null +++ b/nilrt_snac/_configs/_cryptsetup_config.py @@ -0,0 +1,24 @@ +import argparse + +from nilrt_snac._configs._base_config import _BaseConfig + +from nilrt_snac import logger +from nilrt_snac.OpkgHelper import opkg_helper + + +class _CryptSetupConfig(_BaseConfig): + def __init__(self): + self._opkg_helper = opkg_helper + + def configure(self, args: argparse.Namespace) -> None: + print("Configuring cryptsetup...") + + self._opkg_helper.install("cryptsetup") + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying cryptsetup configuration...") + valid = True + if not self._opkg_helper.is_installed("cryptsetup"): + valid = False + logger.error(f"MISSING: cryptsetup not installed") + return valid diff --git a/nilrt_snac/_configs/_faillock_config.py b/nilrt_snac/_configs/_faillock_config.py new file mode 100644 index 0000000..6d19ff9 --- /dev/null +++ b/nilrt_snac/_configs/_faillock_config.py @@ -0,0 +1,23 @@ +import argparse + +from nilrt_snac._configs._base_config import _BaseConfig + +from nilrt_snac import logger +from nilrt_snac.OpkgHelper import opkg_helper + + +class _FaillockConfig(_BaseConfig): + def __init__(self): + self._opkg_helper = opkg_helper + + def configure(self, args: argparse.Namespace) -> None: + print("Configuring PAM faillock...") + self._opkg_helper.install("pam-plugin-faillock") + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying PAM faillock...") + valid = True + if not self._opkg_helper.is_installed("pam-plugin-faillock"): + valid = False + logger.error("MISSING: pam-plugin-faillock not installed") + return valid diff --git a/nilrt_snac/_configs/_niauth_config.py b/nilrt_snac/_configs/_niauth_config.py new file mode 100644 index 0000000..3f39da0 --- /dev/null +++ b/nilrt_snac/_configs/_niauth_config.py @@ -0,0 +1,35 @@ +import argparse +import subprocess + +from nilrt_snac._configs._base_config import _BaseConfig +from nilrt_snac._configs._config_file import _ConfigFile + +from nilrt_snac import logger, SNAC_DATA_DIR +from nilrt_snac.OpkgHelper import opkg_helper + + +class _NIAuthConfig(_BaseConfig): + def __init__(self): + self._opkg_helper = opkg_helper + + def configure(self, args: argparse.Namespace) -> None: + print("Removing NIAuth...") + dry_run: bool = args.dry_run + self._opkg_helper.remove("ni-auth", force_essential=True, force_depends=True) + if not self._opkg_helper.is_installed("nilrt-snac-conflicts"): + self._opkg_helper.install(str(SNAC_DATA_DIR / "nilrt-snac-conflicts.ipk")) + + if not dry_run: + logger.debug("Removing root password") + subprocess.run(["passwd", "-d", "root"], check=True) + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying NIAuth...") + valid = True + if self._opkg_helper.is_installed("ni-auth"): + valid = False + logger.error("FOUND: ni-auth installed") + if not self._opkg_helper.is_installed("nilrt-snac-conflicts"): + valid = False + logger.error("MISSING: nilrt-snac-conflicts not installed") + return valid diff --git a/nilrt_snac/_configs/_ntp_config.py b/nilrt_snac/_configs/_ntp_config.py new file mode 100644 index 0000000..ce4c864 --- /dev/null +++ b/nilrt_snac/_configs/_ntp_config.py @@ -0,0 +1,46 @@ +import argparse +import subprocess + +from nilrt_snac._configs._base_config import _BaseConfig +from nilrt_snac._configs._config_file import _ConfigFile + +from nilrt_snac import logger +from nilrt_snac.OpkgHelper import opkg_helper + + +class _NTPConfig(_BaseConfig): + def __init__(self): + self._opkg_helper = opkg_helper + + def configure(self, args: argparse.Namespace) -> None: + print("Configuring NTP...") + config_file = _ConfigFile("/etc/ntp.conf") + dry_run: bool = args.dry_run + self._opkg_helper.install("ntp") + + logger.debug("Switching ntp servers to US mil.") + if config_file.contains("natinst.pool.ntp.org"): + config_file.update("^.*natinst.pool.ntp.org.*$", "") + + if not config_file.contains("server 0.us.pool.ntp.mil iburst maxpoll 16"): + config_file.add("server 0.us.pool.ntp.mil iburst maxpoll 16") + + config_file.save(dry_run) + if not dry_run: + logger.debug("Restarting ntp service") + subprocess.run(["/etc/init.d/ntpd", "restart"]) + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying NTP configuration...") + config_file = _ConfigFile("/etc/ntp.conf") + valid = True + if not self._opkg_helper.is_installed("ntp"): + valid = False + logger.error("MISSING: ntp not installed") + if not config_file.contains("0.us.pool.ntp.mil iburst maxpoll 16"): + valid = False + logger.error("MISSING: designated ntp server and settings not found in config file") + if config_file.contains("natinst.pool.ntp.org"): + valid = False + logger.error("FOUND: NI ntp server in config file") + return valid diff --git a/nilrt_snac/_configs/_opkg_config.py b/nilrt_snac/_configs/_opkg_config.py new file mode 100644 index 0000000..6b94172 --- /dev/null +++ b/nilrt_snac/_configs/_opkg_config.py @@ -0,0 +1,62 @@ +import argparse +import pathlib +import subprocess +import textwrap + +from nilrt_snac._configs._base_config import _BaseConfig +from nilrt_snac._configs._config_file import _ConfigFile + +from nilrt_snac import logger +from nilrt_snac.OpkgHelper import OPKG_SNAC_CONF, opkg_helper + + +class _OPKGConfig(_BaseConfig): + def __init__(self): + self._opkg_helper = opkg_helper + + def configure(self, args: argparse.Namespace) -> None: + print("Configuring opkg...") + snac_config_file = _ConfigFile(OPKG_SNAC_CONF) + base_feeds_config_file = _ConfigFile("/etc/opkg/base-feeds.conf") + dry_run: bool = args.dry_run + + if not snac_config_file.contains("option autoremove 1"): + snac_config_file.add( + textwrap.dedent( + """ + # NILRT SNAC configuration opkg runparts. Do not hand-edit. + option autoremove 1 + """ + ) + ) + + if pathlib.Path("/etc/opkg/NI-dist.conf").exists(): + logger.debug("Removing unsupported package feeds...") + if not dry_run: + subprocess.run(["rm", "-fv", "/etc/opkg/NI-dist.conf"], check=True) + + if base_feeds_config_file.contains("src.*/extra/.*"): + base_feeds_config_file.update("^src.*/extra/.*", "") + + snac_config_file.save(dry_run) + base_feeds_config_file.save(dry_run) + self._opkg_helper.update() + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying opkg configuration...") + snac_config_file = _ConfigFile(OPKG_SNAC_CONF) + base_feeds_config_file = _ConfigFile("/etc/opkg/base-feeds.conf") + valid = True + if not snac_config_file.exists(): + valid = False + logger.error(f"MISSING: {OPKG_SNAC_CONF} not found") + if not snac_config_file.contains("option autoremove 1"): + valid = False + logger.error( + f"MISSING: 'option autoremove 1' not found in {snac_config_file.path}" + ) + if base_feeds_config_file.contains("src.*/extra/.*"): + valid = False + logger.error(f"FOUND: 'src.*/extra/.*' found in {base_feeds_config_file.path}") + + return valid diff --git a/nilrt_snac/_configs/_pwquality_config.py b/nilrt_snac/_configs/_pwquality_config.py new file mode 100644 index 0000000..dd81fa7 --- /dev/null +++ b/nilrt_snac/_configs/_pwquality_config.py @@ -0,0 +1,43 @@ +import argparse +import textwrap + +from nilrt_snac._configs._base_config import _BaseConfig +from nilrt_snac._configs._config_file import _ConfigFile + +from nilrt_snac import logger + + +class _PWQualityConfig(_BaseConfig): + def __init__(self): + pass + + def configure(self, args: argparse.Namespace) -> None: + print("Configuring Password quality...") + config_file = _ConfigFile("/etc/pam.d/common-password") + dry_run: bool = args.dry_run + + if not config_file.contains("remember=5"): + config_file.update(r"(password.*pam_unix.so.*)", r"\1 remember=5") + if not config_file.contains("password.*requisite.*pam_pwquality.so.*retry=3"): + config_file.add( + textwrap.dedent( + """ + # Additional check for password complexity + password requisite pam_pwquality.so retry=3 + """ + ) + ) + + config_file.save(dry_run) + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying Password quality...") + config_file = _ConfigFile("/etc/pam.d/common-password") + valid = True + if not config_file.contains("remember=5"): + valid = False + logger.error("MISSING: 'remember=5' for pam_unix.so configuration") + if not config_file.contains("password.*requisite.*pam_pwquality.so.*retry=3"): + valid = False + logger.error("MISSING: entry to add quality check") + return valid diff --git a/nilrt_snac/_configs/_usbguard_config.py b/nilrt_snac/_configs/_usbguard_config.py new file mode 100644 index 0000000..c90ddc8 --- /dev/null +++ b/nilrt_snac/_configs/_usbguard_config.py @@ -0,0 +1,61 @@ +import argparse +import pathlib +import shutil +import subprocess +import tempfile + +from nilrt_snac._configs._base_config import _BaseConfig + +from nilrt_snac import logger +from nilrt_snac.OpkgHelper import opkg_helper + +USBGUARD_SRC_URL = ( + "https://github.com/USBGuard/usbguard/releases/download/usbguard-1.1.2/usbguard-1.1.2.tar.gz" +) + + +class _USBGuardConfig(_BaseConfig): + def __init__(self): + self._src_path = pathlib.Path("/usr/local/src") + self._opkg_helper = opkg_helper + + def configure(self, args: argparse.Namespace) -> None: + print("Installing USBGuard...") + dry_run: bool = args.dry_run + + logger.debug(f"Ensure {self._src_path} exists") + self._src_path.mkdir(parents=True, exist_ok=True) + + logger.debug("Clean up any previous copy of USB Guard") + installer_path = self._src_path / "usbguard" + shutil.rmtree(installer_path) + + logger.debug(f"Download and extract {USBGUARD_SRC_URL}") + # There is not proper typing support for NamedTemporaryFile + with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: # type: ignore + subprocess.run(["wget", USBGUARD_SRC_URL, "-O", fp.name], check=True) + subprocess.run(["tar", "xz", "-f", fp.name, "-C", self._src_path], check=True) + + logger.debug("Install prereq") + self._opkg_helper.install("libqb-dev") + + logger.debug("Configure and install USBGuard") + cmd = [ + "./configure", + "--with-crypto-library=openssl", + "--with-bundled-catch", + "--with-bundled-pegtl", + "--without-dbus", + "--without-polkit", + "--prefix=/", + ] + if not dry_run: + subprocess.run(cmd, check=True, cwd=installer_path) + subprocess.run(["make", "install"], check=True, cwd=installer_path) + # TODO: make initscript + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying USBGuard configuration...") + valid = True + # TODO: figure out what needs to be verified + return valid diff --git a/nilrt_snac/_configs/_wifi_config.py b/nilrt_snac/_configs/_wifi_config.py new file mode 100644 index 0000000..9a1882f --- /dev/null +++ b/nilrt_snac/_configs/_wifi_config.py @@ -0,0 +1,49 @@ +import argparse +import subprocess +import textwrap + +from nilrt_snac._configs._base_config import _BaseConfig +from nilrt_snac._configs._config_file import _ConfigFile + +from nilrt_snac import logger + + +class _WIFIConfig(_BaseConfig): + def __init__(self): + pass + + def configure(self, args: argparse.Namespace) -> None: + print("Configuring WiFi configuration...") + config_file = _ConfigFile("/etc/modprobe.d/snac_blacklist.conf") + dry_run: bool = args.dry_run + if not config_file.contains("install cfg80211 /bin/true"): + config_file.add( + textwrap.dedent( + """ + # Do not allow WiFi connections + # We cannot use the blacklist keyword because they will still be loaded + # when another module depends on them. This will prevent the modules from + # being loaded along with any modules that depends on them. + install cfg80211 /bin/true + install mac80211 /bin/true + """ + ) + ) + + config_file.save(dry_run) + if not dry_run: + logger.debug("Removing any WiFi modules in memory") + # We do not check for success. If the modules are not loaded, this will return an error + subprocess.run(["rmmod", "cfg80211", "mac80211"], check=False) + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying WiFi configuration...") + config_file = _ConfigFile("/etc/modprobe.d/snac_blacklist.conf") + valid = True + if not config_file.exists(): + valid = False + logger.error(f"MISSING: {config_file.path} not found") + if not config_file.contains("install cfg80211 /bin/true"): + valid = False + logger.error("MISSING: commands to fail install of WiFi modules") + return valid diff --git a/nilrt_snac/_configs/_wireguard_config.py b/nilrt_snac/_configs/_wireguard_config.py new file mode 100644 index 0000000..4f4c86a --- /dev/null +++ b/nilrt_snac/_configs/_wireguard_config.py @@ -0,0 +1,114 @@ +import argparse +import pathlib +import subprocess +import textwrap + +from nilrt_snac._configs._base_config import _BaseConfig +from nilrt_snac._configs._config_file import _ConfigFile + +from nilrt_snac import logger +from nilrt_snac.OpkgHelper import OPKG_SNAC_CONF, opkg_helper + +WIREGUARD_TOOLS_DEB = "http://ftp.us.debian.org/debian/pool/main/w/wireguard/wireguard-tools_1.0.20210914-1+b1_amd64.deb" + + +class _WireguardConfig(_BaseConfig): + def __init__(self): + self._sysconnf_path = pathlib.Path("/etc/wireguard") + self._opkg_helper = opkg_helper + + def configure(self, args: argparse.Namespace) -> None: + print("Installing wireguard-tools...") + config_file = _ConfigFile(self._sysconnf_path / "wglv0.conf") + private_key = _ConfigFile(self._sysconnf_path / "wglv0.privatekey") + public_key = _ConfigFile(self._sysconnf_path / "wglv0.publickey") + opkg_conf = _ConfigFile(OPKG_SNAC_CONF) + ifplug_conf = _ConfigFile("/etc/ifplugd/ifplugd.conf") + dry_run: bool = args.dry_run + subprocess.run(["wget", WIREGUARD_TOOLS_DEB, "-O", "./wireguard-tools.deb"], check=True) + if not opkg_conf.contains("arch amd64 15"): + opkg_conf.add("arch amd64 15\n") + # We need to save the file before installing the package so that amd64 is + # a valid architecture + opkg_conf.save(dry_run) + self._opkg_helper.install("./wireguard-tools.deb", force_reinstall=True) + + if not ifplug_conf.contains("^ARGS_wglv0.*"): + ifplug_conf.add( + textwrap.dedent( + """ + + # This assignment block is managed by the nilrt-snac package. + ARGS_wglv0="$ARGS --no-auto" + # endblock + """ + ) + ) + + if not config_file.contains("^PrivateKey = .+") and not private_key.exists(): + logger.debug("Generating wireguard keypair....") + result = subprocess.run(["wg", "genkey"], stdout=subprocess.PIPE, check=True) + priv_key = result.stdout.decode().strip() + private_key.add(priv_key) + private_key.chmod(0o600) + result = subprocess.run( + ["wg", "pubkey"], input=priv_key, text=True, stdout=subprocess.PIPE, check=True + ) + pub_key = result.stdout.strip() + public_key.add(pub_key) + public_key.chmod(0o600) + + config_file.save(dry_run) + private_key.save(dry_run) + public_key.save(dry_run) + opkg_conf.save(dry_run) + ifplug_conf.save(dry_run) + if not dry_run: + logger.debug("Restating wireguard service") + subprocess.run( + [ + "update-rc.d", + "ni-wireguard-labview", + "start", + "03", + "3", + "4", + "5", + ".", + "stop", + "05", + "0", + "6", + ".", + ], + check=True, + ) + subprocess.run(["/etc/init.d/ni-wireguard-labview", "restart"], check=True) + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying wireguard configuration...") + config_file = _ConfigFile(self._sysconnf_path / "wglv0.conf") + private_key = _ConfigFile(self._sysconnf_path / "wglv0.privatekey") + public_key = _ConfigFile(self._sysconnf_path / "wglv0.publickey") + opkg_conf = _ConfigFile(OPKG_SNAC_CONF) + ifplug_conf = _ConfigFile("/etc/ifplugd/ifplugd.conf") + valid = True + if not self._opkg_helper.is_installed("wireguard-tools"): + valid = False + logger.error("MISSING: wireguard-tools not installed") + if not config_file.exists(): + valid = False + logger.error(f"MISSING: {config_file.path}") + if not private_key.exists(): + valid = False + logger.error(f"MISSING: {private_key.path}") + if not public_key.exists(): + valid = False + logger.error(f"MISSING: {public_key.path}") + if not opkg_conf.contains("arch amd64 15"): + valid = False + logger.error(f"MISSING: 'arch amd64 15' in {opkg_conf.path}") + if not ifplug_conf.contains("ARGS_wglv0=.*"): + valid = False + logger.error(f"MISSING: 'ARGS_wglv0=.*' in {ifplug_conf.path}") + return valid diff --git a/nilrt_snac/_configs/_x11_config.py b/nilrt_snac/_configs/_x11_config.py new file mode 100644 index 0000000..c727ff8 --- /dev/null +++ b/nilrt_snac/_configs/_x11_config.py @@ -0,0 +1,28 @@ +import argparse + +from nilrt_snac._configs._base_config import _BaseConfig + +from nilrt_snac import logger +from nilrt_snac.OpkgHelper import opkg_helper + + +class _X11Config(_BaseConfig): + def __init__(self): + self._opkg_helper = opkg_helper + + def configure(self, args: argparse.Namespace) -> None: + print("Removing X11 stack...") + self._opkg_helper.remove("packagegroup-core-x11") + self._opkg_helper.remove("packagegroup-ni-xfce") + self._opkg_helper.remove("*xfce4*-locale-ja*", ignore_installed=True) # where do these come from !?! + + def verify(self, args: argparse.Namespace) -> bool: + print("Verifying X11 stack removal...") + valid = True + if self._opkg_helper.is_installed("packagegroup-core-x11"): + valid = False + logger.error("FOUND: packagegroup-core-x11 installed") + if self._opkg_helper.is_installed("packagegroup-ni-xfce"): + valid = False + logger.error("FOUND: packagegroup-ni-xfce installed") + return valid diff --git a/nilrt_snac/_pre_reqs.py b/nilrt_snac/_pre_reqs.py new file mode 100644 index 0000000..0cffcdd --- /dev/null +++ b/nilrt_snac/_pre_reqs.py @@ -0,0 +1,55 @@ +import os +import pathlib +import subprocess + +from nilrt_snac._common import get_distro +from nilrt_snac.OpkgHelper import opkg_helper + +from nilrt_snac import Errors, SNACError, logger + +# Check that the effective UID of this bash script is root (UID 0) +def _check_euid_root(): + print("Checking EUID") + if os.geteuid() != 0: + raise SNACError("This script must be run as root.", Errors.EX_BAD_ENVIRONEMNT) + + +# Check that iptables is available. +# NOTE: The ip_tables kernel module is only loaded once the first call to iptables has +# been made, (inlcuding rule creation). +def _check_iptables(): + print("Checking iptables") + if not opkg_helper.is_installed("iptables"): + logger.debug(" Installing iptables") + opkg_helper.install("iptables") + + logger.debug(" Ensuring iptables is loaded") + try: + subprocess.run(["iptables", "-L"]) + except Exception: + raise SNACError("Failed to load iptables.", Errors.EX_CHECK_FAILURE) + + logger.debug(" Ensuring iptables is installed") + result = subprocess.run(["lsmod"], stdout=subprocess.PIPE) + if "ip_tables" not in result.stdout.decode(): + raise SNACError("Failed to find ip_tables module.", Errors.EX_CHECK_FAILURE) + + +# Check that the script isn't executing within a safemode context +def _check_runmode(): + safe_mode = pathlib.Path("/etc/natinst/safemode") + if safe_mode.exists(): + raise SNACError("This script cannot be run in safe mode.", Errors.EX_BAD_ENVIRONEMNT) + + +# Check that the script is running on NI LinuxRT +def _check_nilrt(): + if get_distro() != "nilrt": + raise SNACError("This script must be run on a NILRT system.", Errors.EX_BAD_ENVIRONEMNT) + + +def verify_prereqs(): # noqa: D103 - Missing docstring in public function (auto-generated noqa) + _check_euid_root() + _check_iptables() + _check_runmode() + _check_nilrt() diff --git a/src/configure-nilrt-snac b/src/configure-nilrt-snac deleted file mode 100644 index e2bc8fd..0000000 --- a/src/configure-nilrt-snac +++ /dev/null @@ -1,256 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: MIT -set -euo pipefail - -SCRIPT_ROOT="$(realpath $(dirname ${BASH_SOURCE}))" -PREFIX_ROOT="$(realpath ${SCRIPT_ROOT}/../../)" -source "${SCRIPT_ROOT}/util.sh" - -## CONSTANTS -OPKG_CONF=/etc/opkg/snac.conf -WIREGUARD_TOOLS_DEB='http://ftp.us.debian.org/debian/pool/main/w/wireguard/wireguard-tools_1.0.20210914-1+b1_amd64.deb' -USBGUARD_SRC_URL="https://github.com/USBGuard/usbguard/releases/download/usbguard-1.1.2/usbguard-1.1.2.tar.gz" - - -## FUNCTIONS - -check_euid_root() { - # Check that the effective UID of this bash script is root (UID 0). If not, - # exit with error 2. - if [ $(id -u) -ne 0 ]; then - log ERROR "this script must be run as root (uid 0)." - exit $EX_BAD_ENVIRONEMNT - fi -} - -# Check that iptables is available. -# NOTE: The ip_tables kernel module is only loaded once the first call to iptables has been made, (inlcuding rule creation). -check_iptables() { - log INFO Checking iptables configuration... - - log DEBUG Installing iptables... - opkg install iptables - - # This call also ensures that the module gets loaded - log DEBUG Checking iptables user tools... - if ! iptables -L; then - echo ERROR iptables binary - exit $EX_CHECK_FAILURE - fi >/dev/null - - # test that ip_tables is in lsmod - local modules=($(lsmod | tail --lines=+2 | awk '{print($1)}')) - if grep -qE 'ip_tables'; then - log DEBUG "check_iptables(): ip_tables module loaded." - else - log ERROR ip_tables module is not loaded. - exit $EX_CHECK_FAILURE - fi <<<${modules[@]} -} - -# Check that the script isn't executing within a safemode context -check_runmode() { - if test -e /etc/natinst/safemode; then - log ERROR Script is executing in a safemode context. - exit $EX_BAD_ENVIRONEMNT - fi -} - -# Check that the script is running on NI LinuxRT -check_nilrt() { - if grep -qE '^ID=nilrt$' /etc/os-release; then - return - else - log ERROR os-release:ID does not match NILRT. This script must only be run on NILRT systems. - exit $EX_BAD_ENVIRONEMNT - fi -} - -configure_ntp() { - log INFO Configuring NTP... - # As of NILRT 24.3, all the NTP packages are in the extra/ feed. :( - opkg update >/dev/null && opkg install ntp - # remove default server configurations - log DEBUG Switching ntp servers to US mil. - sed -i '/^server .*/d' /etc/ntp.conf - echo "server 0.us.pool.ntp.mil iburst maxpoll 16" >>/etc/ntp.conf - /etc/init.d/ntpd restart -} - -configure_opkg() { - log INFO Configuring opkg... - - echo "# NILRT SNAC configuration opkg runparts. Do not hand-edit." >"${OPKG_CONF}" - echo "option autoremove 1" >>"${OPKG_CONF}" - - log DEBUG Removing unsupported package feeds... - rm -fv /etc/opkg/NI-dist.conf - # TODO Uncomment this once we have moved all necessary packages into the core feeds. - #sed -i '/^src.*\/extra\/.*/d' /etc/opkg/base-feeds.conf - - opkg update -} - -configure_usbguard() { - log INFO Installing USBGuard... - mkdir -p /usr/local/src - rm -rf /usr/local/src/usbguard* - wget "${USBGUARD_SRC_URL}" -O - | tar xz -f - -C /usr/local/src - pushd /usr/local/src/usbguard* - - opkg update >/dev/null && opkg install libqb-dev - ./configure --with-crypto-library=openssl --with-bundled-catch --with-bundled-pegtl --without-dbus --without-polkit --prefix=/ - make install - # TODO: make initscript - popd -} - -configure_wireguard() { - # pre-installed by nilrt-snac - local wireguard_sysconf="/etc/wireguard" - local conffile="${wireguard_sysconf}/wglv0.conf" - # - # generated by this function - local privatekey="${wireguard_sysconf}/wglv0.privatekey" - local publickey="${wireguard_sysconf}/wglv0.publickey" - # - - log INFO Installing wireguard-tools... - opkg update >/dev/null && opkg install busybox # contains wget - wget "${WIREGUARD_TOOLS_DEB}" -O ./wireguard-tools.deb - echo "arch amd64 15" >>"${OPKG_CONF}" - opkg install --force-reinstall ./wireguard-tools.deb - log INFO DONE. wireguard-tools "(wg)" is now installed. - - # Exclude interface wglv0 from ifplugd rules. - if ! grep -q -E '^ARGS_wglv0.*' /etc/ifplugd/ifplugd.conf; then - cat >>/etc/ifplugd/ifplugd.conf <<-EOF - - # This assignment block is managed by the nilrt-snac package. - ARGS_wglv0="\$ARGS --no-auto" - # endblock - EOF - fi - - # Only generate a new keypair if a private key is not already configured. - if ! grep -q -E '^PrivateKey = .+' "${conffile}"; then - log INFO Generating wireguard keypair. - - local old_umask=$(umask) - umask 177 # mode=0600 - test -f "${privatekey}" || \ - wg genkey | \ - tee "${privatekey}" | \ - wg pubkey >"${publickey}" - umask $old_umask - fi - - # Initialize the wglv0 interface on startup. - update-rc.d ni-wireguard-labview start 03 3 4 5 . stop 05 0 6 . - /etc/init.d/ni-wireguard-labview restart -} - -install_cryptsetup() { - log INFO Installing cryptsetup... - opkg update >/dev/null && opkg install cryptsetup -} - -# Rips niauth out of the system. -remove_niauth() { - log INFO Removing NIAuth... - - # Manually remove the 'Essential' mark on NI-Auth and its siblings, so that they can be removed. - #trap "opkg update >/dev/null" EXIT - - #sed -i '/Essential: yes/d' /var/lib/opkg/status - opkg remove --force-removal-of-essential-packages --force-depends ni-auth - opkg install "${PREFIX_ROOT}/share/nilrt-snac/nilrt-snac-conflicts.ipk" - passwd -d root - log DEBUG root account password deleted. - - #opkg update >/dev/null # undoes the remarking we just did - trap - EXIT -} - -enable_pwquality() { - log INFO Enabling password quality checks... - log INFO Enabling password history... - sed -i 's/^\(password.*pam_unix.so.*\)$/\1 remember=5/' /etc/pam.d/common-password - log INFO Enabling password complexity using libpwquality... - cat <<-EOF >>/etc/pam.d/common-password - # Additional check for password complexity - password requisite pam_pwquality.so retry=3 - EOF -} - -disable_wifi() { - log INFO Disabling WiFi... - cat </etc/modprobe.d/snac_blacklist.conf -# Do not allow WiFi connections -# We cannot use the blacklist keyword because they will still be loaded -# when another module depends on them. This will prevent the modules from -# being loaded along with any modules that depends on them. -install cfg80211 /bin/true -install mac80211 /bin/true -EOF - - # unload the modules if they are already loaded - # This will most likely error so send the output to /dev/null - set +e - rmmod cfg80211 mac80211 >/dev/null 2>&1 - set -e -} - -# Install and configure pam-plugin-faillock. -# Any non-root account will get locked after 3 failed authentications within 15 minutes. -configure_faillock() { - log INFO Configuring faillock... - - log DEBUG Installing pam-plugin-faillock... - opkg install pam-plugin-faillock -} - - -## MAIN -# runtime environment safety checks -check_euid_root -check_nilrt -check_runmode - -syslog notice SNAC configuration utility activated. - -WORKSPACE=$(mktemp -d --suffix=nilrt-snac) -trap "test -e ${WORKSPACE} && rm -rfv ${WORKSPACE}" EXIT -cd ${WORKSPACE} - -configure_opkg - -remove_niauth - -configure_wireguard - -log INFO Checking that ip_tables is loaded... -check_iptables - -log INFO Disabling console output... -nirtcfg --set section="systemsettings",token="consoleout.enabled",value="False" -# requires reboot to take effect ^. Can we get around this? - -log INFO Removing the X11 stack... -opkg remove packagegroup-core-x11 packagegroup-ni-xfce -opkg remove '*xfce4*-locale-ja*' # where do these come from !?! - -#configure_usbguard # TODO: doesn't work yet. - -install_cryptsetup - -configure_ntp - -enable_pwquality - -disable_wifi - -configure_faillock - -syslog notice SNAC configuration completed. -exit 0 diff --git a/src/nilrt-snac b/src/nilrt-snac index 99f4d11..cc67794 100644 --- a/src/nilrt-snac +++ b/src/nilrt-snac @@ -1,127 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: MIT -set -euo pipefail - - -## CONSTANTS -SCRIPT_ROOT=$(realpath $(dirname ${BASH_SOURCE})) - -## IMPORTS -source "${SCRIPT_ROOT}/../lib/nilrt-snac/util.sh" - -## FUNCTIONS -print_version() { - cat <<-EOF - nilrt-snac 0.1 - Copyright (C) 2024 NI (Emerson Electric) - License MIT: MIT License - This is free software: you are free to change and redistribute it. - There is NO WARRANTY, to the extent permitted by law. - EOF -} - - -## CLI PARSING -usage() { - cat < - -NI LinuxRT Secured, Network-Attached Controller utility. - -# Options --h, --help - Print this usage information and exit. --V, --version - Print this script's version information and exit. --y, --yes - Skip user prompts (answer 'yes' to all prompts). - -# Commands -config, configure Affect the SNAC configuration on this machine. - -# Returns -$EX_OK when the operation was successful. -$EX_ERROR when an unexpected error occurred. -$EX_USAGE when given invalid arguments. -$EX_BAD_ENVIRONEMNT when the execution environment is invalid. -$EX_CHECK_FAILURE when a configuration check fails. -EOF -} - - -positionals=() -yes=false - -while [ $# -ge 1 ]; do case "$1" in - -h|--help) - usage - exit 0 - ;; - -V|--version) - print_version - exit 0 - ;; - -y|--yes) - yes=true - shift - ;; - -*|--*) - log ERROR "Invalid or unknown option \"$1\"." - exit 2 - ;; - *) - positionals+=($1) - shift - ;; -esac; done - -# If no comand is given, print usage and exit 2. -if [ ${#positionals[@]} -lt 1 ]; then\ - log ERROR "Missing required positional arguments."; - usage - exit 2 -fi -command=${positionals[0]} - - -# Configure the system for SNAC -# returns: The return code from configure-nilrt-snac; or 0, when the user does not give final consent. -cmd_configure() { - echo WARNING '!! THIS TOOL IS IN-DEVELOPMENT AND APPROPRIATE ONLY FOR DEVELOPER TESTING !!' - echo WARNING '!! Running this tool will irreversibly alter the state of your system. !!' - echo WARNING '!! If you are accessing your system using WiFi, you will lose connection. !!' - - # prompt for consent - local consent=false - if $yes; then - consent=y - else - read -p "Do you want to continue with SNAC configuration? [y/N] " consent - fi - - case "$consent" in - [yY]*) - bash "${SCRIPT_ROOT}/../lib/nilrt-snac/configure-nilrt-snac" - - echo "" # Blank line - echo "!! A reboot is now required to affect your system configuration. !!" - echo "!! Login with user 'root' and no password. !!" - - return $? - ;; - *) - return 0 - ;; - esac -} - -case "$command" in - config|configure) - cmd_configure - exit $? - ;; - *) - log ERROR Unrecognized command \"${command}\" - exit $EX_USAGE - ;; -esac +export SCRIPTPATH=$(dirname $0) +# realpath only works with the current working directory +pushd ${SCRIPTPATH} > /dev/null +export PYTHONSCRIPTPATH=$(realpath ../lib/nilrt-snac) +popd > /dev/null +PYTHONPATH=${PYTHONPATH}:${PYTHONSCRIPTPATH} python3 -m nilrt_snac $@ diff --git a/src/util.sh b/src/util.sh deleted file mode 100644 index 12a1ed5..0000000 --- a/src/util.sh +++ /dev/null @@ -1,35 +0,0 @@ -## CONSTANTS -export EX_OK=0 -export EX_ERROR=1 -export EX_USAGE=2 -export EX_BAD_ENVIRONEMNT=128 -export EX_CHECK_FAILURE=129 - -## FUNCTIONS -# Channels which are only printed when --verbose -export LOG_VERBOSEONLY_CHANNELS=() -log() { - local log_level=$1 - local log_msg="${@:2}" - if [[ "${LOG_VERBOSEONLY_CHANNELS[@]}" == *"$log_level"* \ - && "${verbose:-}" != true ]]; then - return - else - echo "${log_level}:" "$log_msg" >&2 - fi -} -export -f log - -# Log a message to the system logger. -# $1 : The syslog level (recommend: notice) -# $2 : The message to log -syslog() { - local level=$1 - local msg=${@:2} - logger \ - --priority user.${level} \ - --id=$$ \ - --tag "nilrt-snac" \ - ${msg} -} -export -f syslog