LCOV - code coverage report
Current view: top level - utils - module.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 70.7 % 181 128
Test Date: 2026-01-26 13:19:28 Functions: 100.0 % 9 9
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2017  Red Hat, Inc.
       3              :  *
       4              :  * This library is free software; you can redistribute it and/or
       5              :  * modify it under the terms of the GNU Lesser General Public
       6              :  * License as published by the Free Software Foundation; either
       7              :  * version 2.1 of the License, or (at your option) any later version.
       8              :  *
       9              :  * This library is distributed in the hope that it will be useful,
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12              :  * Lesser General Public License for more details.
      13              :  *
      14              :  * You should have received a copy of the GNU Lesser General Public
      15              :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      16              :  *
      17              :  * Author: Vojtech Trefny <vtrefny@redhat.com>
      18              :  */
      19              : 
      20              : #include <glib.h>
      21              : #include <glib/gprintf.h>
      22              : #include <libkmod.h>
      23              : #include <string.h>
      24              : #include <syslog.h>
      25              : #include <locale.h>
      26              : #include <sys/utsname.h>
      27              : 
      28              : #include "module.h"
      29              : #include "exec.h"
      30              : #include "logging.h"
      31              : 
      32              : 
      33              : /**
      34              :  * bd_utils_module_error_quark: (skip)
      35              :  */
      36            5 : GQuark bd_utils_module_error_quark (void)
      37              : {
      38            5 :     return g_quark_from_static_string ("g-bd-utils-module-error-quark");
      39              : }
      40              : 
      41          678 : static void utils_kmod_log_redirect (void *log_data G_GNUC_UNUSED, int priority,
      42              :                                      const char *file G_GNUC_UNUSED, int line G_GNUC_UNUSED,
      43              :                                      const char *fn G_GNUC_UNUSED, const char *format,
      44              :                                      va_list args) {
      45          678 :     gchar *kmod_msg = NULL;
      46          678 :     gchar *message = NULL;
      47          678 :     gint ret = 0;
      48              : 
      49          678 :     ret = g_vasprintf (&kmod_msg, format, args);
      50          678 :     if (ret < 0) {
      51            0 :         g_free (kmod_msg);
      52            0 :         return;
      53              :     }
      54              : 
      55              : #ifdef DEBUG
      56              :     message = g_strdup_printf ("[libmkod] %s:%d %s() %s", file, line, fn, kmod_msg);
      57              : #else
      58          678 :     message = g_strdup_printf ("[libmkod] %s", kmod_msg);
      59              : #endif
      60          678 :     bd_utils_log (priority, message);
      61              : 
      62          678 :     g_free (kmod_msg);
      63          678 :     g_free (message);
      64              : 
      65              : }
      66              : 
      67          339 : static void set_kmod_logging (struct kmod_ctx *ctx) {
      68              : #ifdef DEBUG
      69              :     kmod_set_log_priority (ctx, LOG_DEBUG);
      70              : #else
      71          339 :     kmod_set_log_priority (ctx, LOG_INFO);
      72              : #endif
      73          339 :     kmod_set_log_fn (ctx, utils_kmod_log_redirect, NULL);
      74          339 : }
      75              : 
      76              : /**
      77              :  * bd_utils_have_kernel_module:
      78              :  * @module_name: name of the kernel module to check
      79              :  * @error: (out) (optional): place to store error (if any)
      80              :  *
      81              :  * Returns: whether the @module_name was found in the system, either as a module
      82              :  * or built-in in the kernel
      83              :  */
      84          335 : gboolean bd_utils_have_kernel_module (const gchar *module_name, GError **error) {
      85          335 :     gint ret = 0;
      86          335 :     struct kmod_ctx *ctx = NULL;
      87          335 :     struct kmod_module *mod = NULL;
      88          335 :     struct kmod_list *list = NULL;
      89          335 :     gchar *null_config = NULL;
      90          335 :     const gchar *path = NULL;
      91          335 :     gboolean have_path = FALSE;
      92          335 :     gboolean builtin = FALSE;
      93          335 :     locale_t c_locale = newlocale (LC_ALL_MASK, "C", (locale_t) 0);
      94              : 
      95          335 :     ctx = kmod_new (NULL, (const gchar * const*) &null_config);
      96          335 :     if (!ctx) {
      97            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_KMOD_INIT_FAIL,
      98              :                      "Failed to initialize kmod context");
      99            0 :         freelocale (c_locale);
     100            0 :         return FALSE;
     101              :     }
     102          335 :     set_kmod_logging (ctx);
     103              : 
     104          335 :     ret = kmod_module_new_from_lookup (ctx, module_name, &list);
     105          335 :     if (ret < 0) {
     106            1 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
     107            1 :                      "Failed to get the module: %s", strerror_l (-ret, c_locale));
     108            1 :         kmod_unref (ctx);
     109            1 :         kmod_module_unref_list (list);
     110            1 :         freelocale (c_locale);
     111            1 :         return FALSE;
     112              :     }
     113              : 
     114          334 :     if (list == NULL) {
     115            1 :         kmod_unref (ctx);
     116            1 :         freelocale (c_locale);
     117            1 :         return FALSE;
     118              :     }
     119              : 
     120          333 :     mod = kmod_module_get_module (list);
     121          333 :     path = kmod_module_get_path (mod);
     122          333 :     have_path = (path != NULL) && (g_strcmp0 (path, "") != 0);
     123          333 :     if (!have_path) {
     124          143 :       builtin = kmod_module_get_initstate (mod) == KMOD_MODULE_BUILTIN;
     125              :     }
     126          333 :     kmod_module_unref (mod);
     127          333 :     kmod_module_unref_list (list);
     128          333 :     kmod_unref (ctx);
     129          333 :     freelocale (c_locale);
     130              : 
     131          333 :     return have_path || builtin;
     132              : }
     133              : 
     134              : /**
     135              :  * bd_utils_load_kernel_module:
     136              :  * @module_name: name of the kernel module to load
     137              :  * @options: (nullable): module options
     138              :  * @error: (out) (optional): place to store error (if any)
     139              :  *
     140              :  * Returns: whether the @module_name was successfully loaded or not
     141              :  */
     142            3 : gboolean bd_utils_load_kernel_module (const gchar *module_name, const gchar *options, GError **error) {
     143            3 :     gint ret = 0;
     144            3 :     struct kmod_ctx *ctx = NULL;
     145            3 :     struct kmod_module *mod = NULL;
     146            3 :     gchar *null_config = NULL;
     147            3 :     locale_t c_locale = newlocale (LC_ALL_MASK, "C", (locale_t) 0);
     148              : 
     149            3 :     ctx = kmod_new (NULL, (const gchar * const*) &null_config);
     150            3 :     if (!ctx) {
     151            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_KMOD_INIT_FAIL,
     152              :                      "Failed to initialize kmod context");
     153            0 :         freelocale (c_locale);
     154            0 :         return FALSE;
     155              :     }
     156            3 :     set_kmod_logging (ctx);
     157              : 
     158            3 :     ret = kmod_module_new_from_name (ctx, module_name, &mod);
     159            3 :     if (ret < 0) {
     160            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
     161            0 :                      "Failed to get the module: %s", strerror_l (-ret, c_locale));
     162            0 :         kmod_unref (ctx);
     163            0 :         freelocale (c_locale);
     164            0 :         return FALSE;
     165              :     }
     166              : 
     167            3 :     if (!kmod_module_get_path (mod)) {
     168            1 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_NOEXIST,
     169              :                      "Module '%s' doesn't exist", module_name);
     170            1 :         kmod_module_unref (mod);
     171            1 :         kmod_unref (ctx);
     172            1 :         freelocale (c_locale);
     173            1 :         return FALSE;
     174              :     }
     175              : 
     176              :     /* module, flags, options, run_install, data, print_action
     177              :        flag KMOD_PROBE_FAIL_ON_LOADED is used for backwards compatibility */
     178            2 :     ret = kmod_module_probe_insert_module (mod, KMOD_PROBE_FAIL_ON_LOADED,
     179              :                                            options, NULL, NULL, NULL);
     180            2 :     if (ret < 0) {
     181            2 :         if (options)
     182            0 :             g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
     183              :                          "Failed to load the module '%s' with options '%s': %s",
     184            0 :                          module_name, options, strerror_l (-ret, c_locale));
     185              :         else
     186            2 :             g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
     187              :                          "Failed to load the module '%s': %s",
     188            2 :                          module_name, strerror_l (-ret, c_locale));
     189            2 :         kmod_module_unref (mod);
     190            2 :         kmod_unref (ctx);
     191            2 :         freelocale (c_locale);
     192            2 :         return FALSE;
     193              :     }
     194              : 
     195            0 :     kmod_module_unref (mod);
     196            0 :     kmod_unref (ctx);
     197            0 :     freelocale (c_locale);
     198            0 :     return TRUE;
     199              : }
     200              : 
     201              : /**
     202              :  * bd_utils_unload_kernel_module:
     203              :  * @module_name: name of the kernel module to unload
     204              :  * @error: (out) (optional): place to store error (if any)
     205              :  *
     206              :  * Returns: whether the @module_name was successfully unloaded or not
     207              :  */
     208            1 : gboolean bd_utils_unload_kernel_module (const gchar *module_name, GError **error) {
     209            1 :     gint ret = 0;
     210            1 :     struct kmod_ctx *ctx = NULL;
     211            1 :     struct kmod_module *mod = NULL;
     212            1 :     struct kmod_list *list = NULL;
     213            1 :     struct kmod_list *cur = NULL;
     214            1 :     gchar *null_config = NULL;
     215            1 :     gboolean found = FALSE;
     216            1 :     locale_t c_locale = newlocale (LC_ALL_MASK, "C", (locale_t) 0);
     217              : 
     218            1 :     ctx = kmod_new (NULL, (const gchar * const*) &null_config);
     219            1 :     if (!ctx) {
     220            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_KMOD_INIT_FAIL,
     221              :                      "Failed to initialize kmod context");
     222            0 :         freelocale (c_locale);
     223            0 :         return FALSE;
     224              :     }
     225            1 :     set_kmod_logging (ctx);
     226              : 
     227            1 :     ret = kmod_module_new_from_loaded (ctx, &list);
     228            1 :     if (ret < 0) {
     229            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
     230            0 :                      "Failed to get the module: %s", strerror_l (-ret, c_locale));
     231            0 :         kmod_unref (ctx);
     232            0 :         freelocale (c_locale);
     233            0 :         return FALSE;
     234              :     }
     235              : 
     236          189 :     for (cur=list; !found && cur != NULL; cur = kmod_list_next(list, cur)) {
     237          188 :         mod = kmod_module_get_module (cur);
     238          188 :         if (g_strcmp0 (kmod_module_get_name (mod), module_name) == 0)
     239            0 :             found = TRUE;
     240              :         else
     241          188 :             kmod_module_unref (mod);
     242              :     }
     243            1 :     kmod_module_unref_list (list);
     244              : 
     245            1 :     if (!found) {
     246            1 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_NOEXIST,
     247              :                      "Module '%s' is not loaded", module_name);
     248            1 :         kmod_unref (ctx);
     249            1 :         freelocale (c_locale);
     250            1 :         return FALSE;
     251              :     }
     252              : 
     253              :     /* module, flags */
     254            0 :     ret = kmod_module_remove_module (mod, 0);
     255            0 :     if (ret < 0) {
     256            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
     257              :                      "Failed to unload the module '%s': %s",
     258            0 :                      module_name, strerror_l (-ret, c_locale));
     259            0 :         kmod_module_unref (mod);
     260            0 :         kmod_unref (ctx);
     261            0 :         freelocale (c_locale);
     262            0 :         return FALSE;
     263              :     }
     264              : 
     265            0 :     kmod_module_unref (mod);
     266            0 :     kmod_unref (ctx);
     267            0 :     freelocale (c_locale);
     268            0 :     return TRUE;
     269              : }
     270              : 
     271              : 
     272              : static BDUtilsLinuxVersion detected_linux_ver;
     273              : static gboolean have_linux_ver = FALSE;
     274              : 
     275              : G_LOCK_DEFINE_STATIC (detected_linux_ver);
     276              : 
     277            2 : static BDUtilsLinuxVersion * _get_linux_version (gboolean lock, GError **error) {
     278              :     struct utsname buf;
     279              : 
     280            2 :     if (lock)
     281            2 :         G_LOCK (detected_linux_ver);
     282              : 
     283              :     /* return cached value if available */
     284            2 :     if (have_linux_ver) {
     285            1 :         if (lock)
     286            1 :             G_UNLOCK (detected_linux_ver);
     287            1 :         return &detected_linux_ver;
     288              :     }
     289              : 
     290            1 :     memset (&detected_linux_ver, 0, sizeof (BDUtilsLinuxVersion));
     291              : 
     292            1 :     if (uname (&buf)) {
     293            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
     294              :                      "Failed to get linux kernel version: %m");
     295            0 :         if (lock)
     296            0 :             G_UNLOCK (detected_linux_ver);
     297            0 :         return NULL;
     298              :       }
     299              : 
     300            1 :     if (g_ascii_strncasecmp (buf.sysname, "Linux", sizeof buf.sysname) != 0) {
     301            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_INVALID_PLATFORM,
     302              :                      "Failed to get kernel version: spurious sysname '%s' detected", buf.sysname);
     303            0 :         if (lock)
     304            0 :             G_UNLOCK (detected_linux_ver);
     305            0 :         return NULL;
     306              :       }
     307              : 
     308            1 :     if (sscanf (buf.release, "%d.%d.%d",
     309              :                 &detected_linux_ver.major,
     310              :                 &detected_linux_ver.minor,
     311              :                 &detected_linux_ver.micro) < 1) {
     312            0 :         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
     313              :                      "Failed to parse kernel version: malformed release string '%s'", buf.release);
     314            0 :         if (lock)
     315            0 :             G_UNLOCK (detected_linux_ver);
     316            0 :         return NULL;
     317              :     }
     318              : 
     319            1 :     have_linux_ver = TRUE;
     320            1 :     if (lock)
     321            1 :         G_UNLOCK (detected_linux_ver);
     322            1 :     return &detected_linux_ver;
     323              : }
     324              : 
     325              : /**
     326              :  * bd_utils_get_linux_version:
     327              :  * @error: (out) (optional): place to store error (if any)
     328              :  *
     329              :  * Retrieves version of currently running Linux kernel. Acts also as an initializer for statically cached data.
     330              :  *
     331              :  * Returns: (transfer none): Detected Linux kernel version or %NULL in case of an error. The returned value belongs to the library, do not free.
     332              :  */
     333            2 : BDUtilsLinuxVersion * bd_utils_get_linux_version (GError **error) {
     334            2 :     return _get_linux_version (TRUE, error);
     335              : }
     336              : 
     337              : /**
     338              :  * bd_utils_check_linux_version:
     339              :  * @major: Minimal major linux kernel version.
     340              :  * @minor: Minimal minor linux kernel version.
     341              :  * @micro: Minimal micro linux kernel version.
     342              :  *
     343              :  * Checks whether the currently running linux kernel version is equal or higher
     344              :  * than the specified required @major.@minor.@micro version.
     345              :  *
     346              :  * Returns: an integer less than, equal to, or greater than zero, if detected version is <, == or > than the specified @major.@minor.@micro version.
     347              :  */
     348            3 : gint bd_utils_check_linux_version (guint major, guint minor, guint micro) {
     349              :     gint ret;
     350              : 
     351            3 :     G_LOCK (detected_linux_ver);
     352            3 :     if (!have_linux_ver)
     353            0 :         _get_linux_version (FALSE, NULL);
     354              : 
     355            3 :     ret = detected_linux_ver.major - major;
     356            3 :     if (ret == 0)
     357            1 :         ret = detected_linux_ver.minor - minor;
     358            3 :     if (ret == 0)
     359            1 :         ret = detected_linux_ver.micro - micro;
     360              : 
     361            3 :     G_UNLOCK (detected_linux_ver);
     362              : 
     363            3 :     return ret;
     364              : }
        

Generated by: LCOV version 2.0-1