Skip to content

Commit 30910b4

Browse files
committed
add method to resolve some kernel module names from modules.alias
this works in some cases, an important thing to note is that the "cache" made at _KMOD_ALIASES is tied to the `kernel_version` at the time it's first read. This should generally be fine, as the kernel_version should only be set once at runtime, and this should run after it's set Signed-off-by: Zen <[email protected]>
1 parent 1c9b7cf commit 30910b4

File tree

1 file changed

+61
-10
lines changed

1 file changed

+61
-10
lines changed

src/ugrd/kmod/kmod.py

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
__author__ = "desultory"
2-
__version__ = "3.5.0"
2+
__version__ = "3.6.0"
33

44
from pathlib import Path
55
from platform import uname
6+
from re import search
67
from struct import error as StructError
78
from struct import unpack
89
from subprocess import run
910
from typing import Union
1011

1112
from ugrd.exceptions import AutodetectError, ValidationError
12-
from ugrd.kmod import (
13-
BuiltinModuleError,
14-
DependencyResolutionError,
15-
IgnoredModuleError,
16-
MissingModuleError,
17-
)
13+
from ugrd.kmod import BuiltinModuleError, DependencyResolutionError, IgnoredModuleError, MissingModuleError
1814
from zenlib.util import colorize as c_
1915
from zenlib.util import contains, unset
2016

17+
_KMOD_ALIASES = {}
2118
MODULE_METADATA_FILES = ["modules.order", "modules.builtin", "modules.builtin.modinfo"]
2219

20+
2321
def _normalize_kmod_name(self, module: Union[str, list]) -> str:
2422
"""Replaces -'s with _'s in a kernel module name.
2523
ignores modules defined in kmod_no_normalize.
@@ -34,6 +32,43 @@ def _normalize_kmod_name(self, module: Union[str, list]) -> str:
3432
return module.replace("-", "_")
3533

3634

35+
def _get_kmod_aliases(self):
36+
"""Returns a processed dictionary of kernel module aliases."""
37+
if _KMOD_ALIASES:
38+
return _KMOD_ALIASES
39+
40+
if not self.get("kernel_version"):
41+
raise AutodetectError("Kernel version is not set, cannot resolve kernel module aliases.")
42+
43+
alias_file = Path("/lib/modules") / self["kernel_version"] / "modules.alias"
44+
if not alias_file.exists():
45+
raise FileNotFoundError(f"Kernel module alias file does not exist: {c_(alias_file, 'red', bold=True)}")
46+
47+
for line in alias_file.read_text().splitlines():
48+
_, alias, module = line.strip().split(" ", 2)
49+
# Strip bus type
50+
alias = alias.split(":", 1)[-1]
51+
alias = alias.split(",", 1)[-1]
52+
_KMOD_ALIASES[alias] = _normalize_kmod_name(self, module)
53+
return _KMOD_ALIASES
54+
55+
56+
def _resolve_kmod_alias(self, module: str) -> str:
57+
"""Attempts to resolve a kernel module alias to a module name.
58+
Uses /lib/modules/<kernel_version>/modules.alias to find the module name.
59+
normalizes - to _ then replaces _ with [_-] to allow for both _ and - in the module name.
60+
"""
61+
module = module.replace("-", "_")
62+
module = module.replace("_", "[_-]") # Allow for both _ and - in the module name
63+
aliases = _get_kmod_aliases(self)
64+
for alias, kmod in aliases.items():
65+
if search(module, alias):
66+
self.logger.debug(f"Resolved kernel module alias: {c_(alias, 'green')} -> {c_(kmod, 'cyan')}")
67+
return kmod
68+
69+
raise MissingModuleError(f"Failed to resolve kernel module alias: {module}")
70+
71+
3772
def _process_kernel_modules_multi(self, module: str) -> None:
3873
"""Adds kernel modules to self['kernel_modules']."""
3974
module = _normalize_kmod_name(self, module)
@@ -98,7 +133,13 @@ def _get_kmod_info(self, module: str):
98133
raise DependencyResolutionError("[%s] Failed to run modinfo command: %s" % (module, " ".join(args))) from e
99134

100135
if not cmd.stdout and cmd.stderr:
101-
raise DependencyResolutionError("[%s] Modinfo returned no output." % module)
136+
try:
137+
resolved_module = _resolve_kmod_alias(self, module)
138+
return _get_kmod_info(self, resolved_module)
139+
except MissingModuleError:
140+
raise DependencyResolutionError(
141+
"[%s] Modinfo returned no output and alias name could no be resolved." % module
142+
)
102143

103144
module_info = {}
104145
for line in cmd.stdout.decode().split("\n"):
@@ -159,7 +200,9 @@ def _autodetect_modules_lsmod(self) -> None:
159200
modules = [line.split()[0] for line in f.readlines()]
160201

161202
if len(modules) > 25:
162-
self.logger.warning(f"[{len(modules)}] More than 25 kernel modules were autodetected from the running kernel. If lsmod detection is required for your use case, please file a bug report so more appropriate detection methods can be implemented.")
203+
self.logger.warning(
204+
f"[{len(modules)}] More than 25 kernel modules were autodetected from the running kernel. If lsmod detection is required for your use case, please file a bug report so more appropriate detection methods can be implemented."
205+
)
163206
for module in modules:
164207
self["_kmod_auto"] = module.split()[0]
165208

@@ -477,7 +520,9 @@ def _process_optional_modules(self) -> None:
477520
self.logger.debug(f"Optional kmod_init module is built-in, skipping: {c_(kmod, 'yellow')}")
478521
continue
479522
except DependencyResolutionError as e:
480-
self.logger.warning(f"[{c_(kmod, 'yellow', bold=True)}] Failed to process optional kernel module dependencies: {e}")
523+
self.logger.warning(
524+
f"[{c_(kmod, 'yellow', bold=True)}] Failed to process optional kernel module dependencies: {e}"
525+
)
481526

482527

483528
@unset("no_kmod", "no_kmod is enabled, skipping.", log_level=30)
@@ -486,6 +531,11 @@ def process_modules(self) -> None:
486531
_process_optional_modules(self)
487532
self.logger.debug("Processing kernel modules: %s" % self["kernel_modules"])
488533
for kmod in self["kernel_modules"].copy():
534+
""" Process all kernel modules
535+
for kmod_init modules, log an error if info can't be retreived, but continue processing.
536+
in successful cases, continue, if he module processing fails, add to the ignore list.
537+
Later, when ignored modules are processed, an exception is raised if the module is required.
538+
"""
489539
self.logger.debug("Processing kernel module: %s" % kmod)
490540
try:
491541
_process_kmod_dependencies(self, kmod)
@@ -507,6 +557,7 @@ def process_modules(self) -> None:
507557
self["kmod_ignore"] = kmod
508558

509559
for kmod in self["_kmod_auto"]:
560+
""" Do similar for automatic modules, but log warnings insead of errors if dependencies are missing. """
510561
if kmod in self["kernel_modules"]:
511562
self.logger.debug("Autodetected module is already in kernel_modules: %s" % kmod)
512563
continue

0 commit comments

Comments
 (0)