Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

op mode: T6501: add "run show kernel modules" #3683

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions op-mode-definitions/show-kernel-modules.xml.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="show">
<children>
<node name="kernel">
<properties>
<help>Show kernel information</help>
</properties>
<children>
<node name="modules">
<properties>
<help>Show kernel modules</help>
</properties>
<command>sudo ${vyos_op_scripts_dir}/kernel_modules.py show</command>
</node>
</children>
</node>
</children>
</node>
</interfaceDefinition>
77 changes: 76 additions & 1 deletion python/vyos/utils/kernel.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -36,3 +36,78 @@ def unload_kmod(k_mod):
if os.path.exists(f'/sys/module/{module}'):
if call(f'rmmod {module}') != 0:
raise ConfigError(f'Unloading Kernel module {module} failed')

def list_loaded_modules():
""" Returns the list of currently loaded kernel modules """
from os import listdir
return listdir('/sys/module/')

def get_module_data(module: str):
""" Retrieves information about a module """
from os import listdir
from os.path import isfile, dirname, basename, join
from vyos.utils.file import read_file

def _get_file(path):
# Some files inside some modules are not readable at all,
# we just skip them.
try:
return read_file(path)
except PermissionError:
return None

mod_path = join('/sys/module', module)
mod_data = {"name": module, "fields": {}, "parameters": {}}

for f in listdir(mod_path):
if f in ["sections", "notes", "uevent"]:
# The uevent file is not readable
# and module build info and memory layout
# in notes and sections generally aren't useful
# for anything but kernel debugging.
pass
elif f == "drivers":
# Drivers are dir symlinks,
# we just list them
drivers = listdir(join(mod_path, f))
if drivers:
mod_data["drivers"] = drivers
elif f == "holders":
# Holders (module that use this one)
# are always symlink to other modules.
# We only need the list.
holders = listdir(join(mod_path, f))
if holders:
mod_data["holders"] = holders
elif f == "parameters":
# Many modules keep their configuration
# in the "parameters" subdir.
ppath = join(mod_path, "parameters")
ps = listdir(ppath)
for p in ps:
data = _get_file(join(ppath, p))
if data:
mod_data["parameters"][p] = data
else:
# Everything else...
# There are standard fields like refcount and initstate,
# but many modules also keep custom information or settings
# in top-level fields.
# For now we don't separate well-known and custom fields.
if isfile(join(mod_path, f)):
data = _get_file(join(mod_path, f))
if data:
mod_data["fields"][f] = data
else:
raise RuntimeError(f"Unexpected directory inside module {module}: {f}")

return mod_data

def lsmod():
""" Returns information about all loaded modules.
Like lsmod(8), but more detailed.
"""
mods_data = []
for m in list_loaded_modules():
mods_data.append(get_module_data(m))
return mods_data
82 changes: 82 additions & 0 deletions src/op_mode/kernel_modules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
#
# Copyright (C) 2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Purpose:
# Provides commands for retrieving information about kernel modules.

import sys
import typing

import vyos.opmode


lsmod_tmpl = """
{% for m in modules -%}
Module: {{m.name}}

{% if m.holders -%}
Holders: {{m.holders | join(", ")}}
{%- endif %}

{% if m.drivers -%}
Drivers: {{m.drivers | join(", ")}}
{%- endif %}

{% for k in m.fields -%}
{{k}}: {{m["fields"][k]}}
{% endfor %}
{% if m.parameters %}

Parameters:

{% for p in m.parameters -%}
{{p}}: {{m["parameters"][p]}}
{% endfor -%}
{% endif -%}

-------------

{% endfor %}
"""

def _get_raw_data(module=None):
from vyos.utils.kernel import get_module_data, lsmod

if module:
return [get_module_data(module)]
else:
return lsmod()

def show(raw: bool, module: typing.Optional[str]):
from jinja2 import Template

data = _get_raw_data(module=module)

if raw:
return data
else:
t = Template(lsmod_tmpl)
output = t.render({"modules": data})
return output

if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])
if res:
print(res)
except (ValueError, vyos.opmode.Error) as e:
print(e)
sys.exit(1)
Loading