4242 Callable ,
4343 Dict ,
4444 Iterable ,
45+ List ,
4546 Optional ,
4647 Sequence ,
4748 TextIO ,
@@ -73,6 +74,7 @@ class InfoDict(TypedDict):
7374
7475
7576_UNIXCONFDIR = os .environ .get ("UNIXCONFDIR" , "/etc" )
77+ _UNIXPROCDIR = os .environ .get ("UNIXPROCDIR" , "/proc" )
7678_UNIXUSRLIBDIR = os .environ .get ("UNIXUSRLIBDIR" , "/usr/lib" )
7779_OS_RELEASE_BASENAME = "os-release"
7880
@@ -783,6 +785,7 @@ def __init__(
783785 """
784786 self .root_dir = root_dir
785787 self .etc_dir = os .path .join (root_dir , "etc" ) if root_dir else _UNIXCONFDIR
788+ self .proc_dir = os .path .join (root_dir , "proc" ) if root_dir else _UNIXPROCDIR
786789 self .usr_lib_dir = (
787790 os .path .join (root_dir , "usr/lib" ) if root_dir else _UNIXUSRLIBDIR
788791 )
@@ -1302,14 +1305,19 @@ def _armbian_version(self) -> str:
13021305 except FileNotFoundError :
13031306 return ""
13041307
1305- @staticmethod
1306- def _parse_uname_content (lines : Sequence [str ]) -> Dict [str , str ]:
1308+ def _parse_uname_content (self , lines : Sequence [str ]) -> Dict [str , str ]:
13071309 if not lines :
13081310 return {}
13091311 props = {}
1310- match = re .search (r"^([^\s]+)\s+([\d\. ]+)" , lines [0 ].strip ())
1312+ match = re .search (r"^([^\s]+)\s+([^\s ]+)" , lines [0 ].strip ())
13111313 if match :
1312- name , version = match .groups ()
1314+ name , release = match .groups ()
1315+
1316+ # CloudLinux detection relies on uname release information
1317+ release_parts = release .split ("." )
1318+ props = self ._cloudlinux_detection (release_parts )
1319+ if props :
1320+ return props
13131321
13141322 # This is to prevent the Linux kernel version from
13151323 # appearing as the 'best' version on otherwise
@@ -1318,9 +1326,38 @@ def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
13181326 return {}
13191327 props ["id" ] = name .lower ()
13201328 props ["name" ] = name
1321- props ["release" ] = version
1329+ props ["release" ] = release . split ( "-" )[ 0 ] # only keep version part
13221330 return props
13231331
1332+ def _cloudlinux_detection (self , release_parts : List [str ]) -> Dict [str , str ]:
1333+ if (
1334+ # check penultimate release component contains an "el*" version
1335+ len (release_parts ) > 1
1336+ and release_parts [- 2 ].startswith ("el" )
1337+ and (
1338+ # CloudLinux < 9 : "lve*" is set in kernel release
1339+ any (rc .startswith ("lve" ) for rc in release_parts )
1340+ # CloudLinux >= 9 : check whether "kmodlve" is loaded
1341+ or "kmodlve" in self ._kernel_modules
1342+ )
1343+ ):
1344+ return {
1345+ "id" : "cloudlinux" ,
1346+ "name" : "CloudLinux" ,
1347+ # strip "el" prefix and replace underscores by dots
1348+ "release" : release_parts [- 2 ][2 :].replace ("_" , "." ),
1349+ }
1350+
1351+ return {}
1352+
1353+ @cached_property
1354+ def _kernel_modules (self ) -> List [str ]:
1355+ try :
1356+ with open (os .path .join (self .proc_dir , "modules" ), encoding = "ascii" ) as fp :
1357+ return [line .split ()[0 ] for line in fp ]
1358+ except OSError :
1359+ return []
1360+
13241361 @staticmethod
13251362 def _to_str (bytestring : bytes ) -> str :
13261363 encoding = sys .getfilesystemencoding ()
0 commit comments