11__author__ = "desultory"
2- __version__ = "3.6 .0"
2+ __version__ = "4.0 .0"
33
44from pathlib import Path
55from platform import uname
@@ -32,25 +32,15 @@ def _normalize_kmod_name(self, module: Union[str, list]) -> str:
3232 return module .replace ("-" , "_" )
3333
3434
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
35+ def _normalize_kmod_alias (self , alias : str ) -> str :
36+ """Gets the base alias name from kmod alias info
37+ gets data after : and , if present.
38+ """
39+ if not alias :
40+ return ""
41+ alias = alias .split (":" , 1 )[- 1 ] # Strip bus type
42+ alias = alias .split ("," , 1 )[- 1 ]
43+ return _normalize_kmod_name (self , alias )
5444
5545
5646def _resolve_kmod_alias (self , module : str ) -> str :
@@ -60,8 +50,7 @@ def _resolve_kmod_alias(self, module: str) -> str:
6050 """
6151 module = module .replace ("-" , "_" )
6252 module = module .replace ("_" , "[_-]" ) # Allow for both _ and - in the module name
63- aliases = _get_kmod_aliases (self )
64- for alias , kmod in aliases .items ():
53+ for alias , kmod in _KMOD_ALIASES .items ():
6554 if search (module , alias ):
6655 self .logger .debug (f"Resolved kernel module alias: { c_ (alias , 'green' )} -> { c_ (kmod , 'cyan' )} " )
6756 return kmod
@@ -116,14 +105,14 @@ def _process__kmod_auto_multi(self, module: str) -> None:
116105 self ["_kmod_auto" ].append (module )
117106
118107
119- def _get_kmod_info (self , module : str ):
108+ def _get_kmod_info (self , module : str ) -> dict :
120109 """
121110 Runs modinfo on a kernel module, parses the output and stored the results in self['_kmod_modinfo'].
122111 !!! Should be run after metadata is processed so the kver is set properly !!!
123112 """
124113 module = _normalize_kmod_name (self , module )
125114 if module in self ["_kmod_modinfo" ]:
126- return self . logger . debug ( "[%s] Module info already exists." % module )
115+ return self [ "_kmod_modinfo" ][ module ]
127116 args = ["modinfo" , module , "--set-version" , self ["kernel_version" ]]
128117
129118 try :
@@ -138,10 +127,10 @@ def _get_kmod_info(self, module: str):
138127 return _get_kmod_info (self , resolved_module )
139128 except MissingModuleError :
140129 raise DependencyResolutionError (
141- "[%s] Modinfo returned no output and alias name could no be resolved." % module
130+ "[%s] Modinfo returned no output and the alias name could no be resolved." % module
142131 )
143132
144- module_info = {}
133+ module_info = {"filename" : None , "depends" : [], "softdep" : [], "firmware" : [] }
145134 for line in cmd .stdout .decode ().split ("\n " ):
146135 line = line .strip ()
147136 if line .startswith ("filename:" ):
@@ -166,6 +155,51 @@ def _get_kmod_info(self, module: str):
166155
167156 self .logger .debug ("[%s] Module info: %s" % (module , module_info ))
168157 self ["_kmod_modinfo" ][module ] = module_info
158+ return module_info
159+
160+
161+ @unset ("no_kmod" , "no_kmod is enabled, skipping module alias enumeration." , log_level = 30 )
162+ def get_module_aliases (self ):
163+ """Processes the kernel module aliases from /lib/modules/<kernel_version>/modules.alias."""
164+ alias_file = Path ("/lib/modules" ) / self ["kernel_version" ] / "modules.alias"
165+ if not alias_file .exists ():
166+ self .logger .error (f"Kernel module alias file does not exist: { c_ (alias_file , 'red' , bold = True )} " )
167+ else :
168+ for line in alias_file .read_text ().splitlines ():
169+ _ , alias , module = line .strip ().split (" " , 2 )
170+ _KMOD_ALIASES [_normalize_kmod_alias (self , alias )] = _normalize_kmod_name (self , module )
171+
172+
173+ @unset ("no_kmod" , "no_kmod is enabled, skipping builtin module enumeration." , log_level = 30 )
174+ def get_builtin_module_info (self ) -> None :
175+ """Gets the kernel module aliases from /lib/modules/<kernel_version>/modules.builtin.modinfo.
176+ puts it in _kmod_modinfo.
177+ also populates the _KMOD_ALIASES global variable with the aliases.
178+ """
179+
180+ builtin_modinfo_file = Path ("/lib/modules" ) / self ["kernel_version" ] / "modules.builtin.modinfo"
181+ if not builtin_modinfo_file .exists ():
182+ self .logger .error (f"Builtin modinfo file does not exist: { c_ (builtin_modinfo_file , 'red' , bold = True )} " )
183+ else :
184+ for line in builtin_modinfo_file .read_bytes ().split (b"\x00 " ):
185+ """ Lines are in the format <name>.<parameter>=<value>"""
186+ line = line .decode ("utf-8" , errors = "ignore" ).strip ()
187+ if not line or "." not in line or "=" not in line :
188+ continue
189+ name , parameter = line .split ("." , 1 )
190+ name = _normalize_kmod_name (self , name )
191+ parameter , value = parameter .split ("=" , 1 )
192+ modinfo = self ["_kmod_modinfo" ].get (
193+ name , {"filename" : "(builtin)" , "depends" : [], "softdep" : [], "firmware" : []}
194+ )
195+ if parameter == "firmware" :
196+ modinfo ["firmware" ] += value
197+ elif parameter != "alias" :
198+ continue
199+
200+ alias = _normalize_kmod_alias (self , value )
201+ self ["_kmod_modinfo" ][name ] = modinfo
202+ _KMOD_ALIASES [alias ] = name # Store the alias in the global aliases dict
169203
170204
171205@contains ("kmod_autodetect_lspci" , "kmod_autodetect_lspci is not enabled, skipping." )
@@ -207,7 +241,7 @@ def _autodetect_modules_lsmod(self) -> None:
207241 self ["_kmod_auto" ] = module .split ()[0 ]
208242
209243
210- @unset ("no_kmod" , "no_kmod is enabled, skipping." , log_level = 30 )
244+ @unset ("no_kmod" , "no_kmod is enabled, skipping module detection ." , log_level = 30 )
211245@contains ("hostonly" , "Skipping kmod autodetection, hostonly is disabled." , log_level = 30 )
212246def autodetect_modules (self ) -> None :
213247 """Autodetects kernel modules from lsmod and/or lspci -k."""
@@ -352,24 +386,24 @@ def _add_kmod_firmware(self, kmod: str) -> None:
352386
353387 Attempts to run even if no_kmod is set; this will not work if there are no kmods/no kernel version set
354388 """
355- kmod = _normalize_kmod_name ( self , kmod )
356-
357- if kmod not in self [ "_kmod_modinfo" ] :
389+ try :
390+ modinfo = _get_kmod_info ( self , kmod )
391+ except DependencyResolutionError as e :
358392 if self ["no_kmod" ]:
359393 return self .logger .warning (
360394 "[%s] Kernel module info for firmware detection does not exist, but no_kmod is set." % kmod
361395 )
362- raise DependencyResolutionError ("Kernel module info does not exist: %s" % kmod )
396+ raise DependencyResolutionError ("Kernel module info does not exist: %s" % kmod ) from e
363397
364- if self [ "_kmod_modinfo" ][ kmod ]. get ( " firmware") and not self ["kmod_pull_firmware" ]:
398+ if modinfo [ " firmware"] and not self ["kmod_pull_firmware" ]:
365399 # Log a warning if the kernel module has firmware files, but kmod_pull_firmware is not set
366400 self .logger .warning ("[%s] Kernel module has firmware files, but kmod_pull_firmware is not set." % kmod )
367401
368- if not self [ "_kmod_modinfo" ][ kmod ]. get ( " firmware") or not self .get ("kmod_pull_firmware" ):
402+ if not modinfo [ " firmware"] or not self .get ("kmod_pull_firmware" ):
369403 # No firmware files to add, or kmod_pull_firmware is not set
370404 return
371405
372- for firmware in self [ "_kmod_modinfo" ][ kmod ] ["firmware" ]:
406+ for firmware in modinfo ["firmware" ]:
373407 _add_firmware_dep (self , kmod , firmware )
374408
375409
@@ -401,15 +435,14 @@ def _process_kmod_dependencies(self, kmod: str, mod_tree=None) -> None:
401435 If the dependency is already in the module tree, skip it to prevent infinite recursion.
402436 """
403437 mod_tree = mod_tree or set ()
404- kmod = _normalize_kmod_name (self , kmod )
405- _get_kmod_info (self , kmod )
438+ modinfo = _get_kmod_info (self , kmod )
406439
407440 # Get kernel module dependencies, softedeps if not ignored
408441 dependencies = []
409- if harddeps := self [ "_kmod_modinfo" ][ kmod ]. get ( " depends") :
442+ if harddeps := modinfo [ " depends"] :
410443 dependencies += harddeps
411444
412- if sofdeps := self [ "_kmod_modinfo" ][ kmod ]. get ( " softdep") :
445+ if sofdeps := modinfo [ " softdep"] :
413446 if self .get ("kmod_ignore_softdeps" , False ):
414447 self .logger .warning ("[%s] Soft dependencies were detected, but are being ignored: %s" % (kmod , sofdeps ))
415448 else :
@@ -421,12 +454,11 @@ def _process_kmod_dependencies(self, kmod: str, mod_tree=None) -> None:
421454 if dependency in mod_tree :
422455 self .logger .debug ("[%s] Dependency is already in mod_tree: %s" % (kmod , dependency ))
423456 continue
457+ dep_modinfo = _get_kmod_info (self , dependency ) # Get modinfo for the dependency
424458 if dependency in self ["kmod_ignore" ]: # Don't add modules with ignored dependencies
425- _get_kmod_info (self , dependency ) # Make sure modinfo is queried in case it's built-in
426- if modinfo := self ["_kmod_modinfo" ].get (dependency ):
427- if modinfo ["filename" ] == "(builtin)" : # If it's ignored because builtin, that's fine
428- self .logger .debug ("[%s] Ignored dependency is a built-in module: %s" % (kmod , dependency ))
429- continue
459+ if dep_modinfo ["filename" ] == "(builtin)" :
460+ self .logger .debug ("[%s] Ignored dependency is a built-in module: %s" % (kmod , dependency ))
461+ continue
430462 # If modinfo doesn't exist, or it's not builtin, simply raise an ignored module error
431463 raise IgnoredModuleError ("[%s] Kernel module dependency is in ignore list: %s" % (kmod , dependency ))
432464 if dependency in self ["kernel_modules" ]:
@@ -441,7 +473,7 @@ def _process_kmod_dependencies(self, kmod: str, mod_tree=None) -> None:
441473 continue
442474 self ["kernel_modules" ] = dependency
443475
444- if self [ "_kmod_modinfo" ][ kmod ] ["filename" ] == "(builtin)" : # for built-in modules, just add firmware and return
476+ if modinfo ["filename" ] == "(builtin)" : # for built-in modules, just add firmware and return
445477 _add_kmod_firmware (self , kmod )
446478 raise BuiltinModuleError ("Not adding built-in module to dependencies: %s" % kmod )
447479
@@ -463,7 +495,8 @@ def add_kmod_deps(self):
463495 continue
464496
465497 # Add the kmod file to the initramfs dependenceis
466- filename = self ["_kmod_modinfo" ][kmod ]["filename" ]
498+ modinfo = _get_kmod_info (self , kmod )
499+ filename = modinfo ["filename" ]
467500 if filename .endswith (".ko" ):
468501 self ["dependencies" ] = filename
469502 elif filename .endswith (".ko.xz" ):
@@ -483,7 +516,8 @@ def process_ignored_module(self, module: str) -> None:
483516 for key in ["kmod_init" , "kernel_modules" , "_kmod_auto" ]:
484517 if module in self [key ]:
485518 if key == "kmod_init" :
486- if module in self ["_kmod_modinfo" ] and self ["_kmod_modinfo" ][module ]["filename" ] == "(builtin)" :
519+ modinfo = _get_kmod_info (self , module )
520+ if modinfo ["filename" ] == "(builtin)" :
487521 self .logger .debug ("Removing built-in module from kmod_init: %s" % module )
488522 elif module == "zfs" :
489523 self .logger .critical ("ZFS module is required but missing." )
0 commit comments