blob: e041b3c22e98b8af1a33532822d0a363621b0540 [file] [edit]
# Copyright (C) 2024 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Script to gather information about ACK modules."""
import argparse
import concurrent.futures
import dataclasses
import hashlib
import itertools
import pathlib
import subprocess
import xml_handler
@dataclasses.dataclass(frozen=True)
class KernelModule:
"""Class representing minimal description of an ACK module."""
name: str
author: str
license: str
digest: str = dataclasses.field(init=False)
def __post_init__(self):
object.__setattr__(
self,
"digest",
hashlib.sha256(
(self.name + self.author + self.license).encode()
).hexdigest(),
)
def hexdigest(self) -> str:
return self.digest
def print(self):
print(
"name: {}\nauthor: {}\nlicense: {}\ndigest: {}".format(
self.name, self.author, self.license, self.digest
)
)
def _get_module_hexdigest(module: pathlib.Path, verbose: bool) -> str:
modinfo_name = subprocess.check_output(
["modinfo", "-F", "name", module], text=True
).strip()
modinfo_author = subprocess.check_output(
["modinfo", "-F", "author", module], text=True
).strip()
modinfo_license = subprocess.check_output(
["modinfo", "-F", "license", module], text=True
).strip()
kernel_module = KernelModule(
name=modinfo_name,
author=modinfo_author,
license=modinfo_license,
)
if verbose:
kernel_module.print()
return kernel_module.hexdigest()
def _get_modules(directory: pathlib.Path, verbose: bool) -> list[str]:
module_hashes = set()
with concurrent.futures.ProcessPoolExecutor() as executor:
module_hashes = set(
executor.map(
_get_module_hexdigest,
directory.glob("**/*.ko"),
itertools.repeat(verbose),
)
)
# Keep it deterministic and make it easy for searchs.
return sorted(module_hashes)
def load_arguments():
parser = argparse.ArgumentParser()
parser.add_argument(
"--directory",
type=pathlib.Path,
help="Path to directory with all .ko binaries.",
)
parser.add_argument(
"--output",
type=pathlib.Path,
help="Path to .xml file to store the result.",
)
parser.add_argument(
"--verbose",
action="store_true",
help="If enabled print additional info.",
)
return parser.parse_args()
def generate_report(
directory: pathlib.Path,
output: pathlib.Path,
verbose: bool,
) -> None:
module_hashes = _get_modules(directory, verbose)
# Populate the XML document with information from modules.
xml_handler.create_report(module_hashes, output)
if __name__ == "__main__":
arguments = load_arguments()
generate_report(**vars(arguments))