LCOV - code coverage report
Current view: top level - plugins - check_deps.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 31.4 % 204 64
Test Date: 2026-01-23 09:12:16 Functions: 50.0 % 6 3
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: Vratislav Podzimek <vpodzime@redhat.com>
      18              :  */
      19              : 
      20              : #include <glib.h>
      21              : #include <gio/gio.h>
      22              : #include <blockdev/utils.h>
      23              : 
      24              : #include "check_deps.h"
      25              : 
      26              : #define DBUS_PROPS_IFACE "org.freedesktop.DBus.Properties"
      27              : 
      28              : 
      29              : G_GNUC_INTERNAL gboolean
      30         2423 : check_deps (volatile guint *avail_deps, guint req_deps, const UtilDep *deps_specs, guint l_deps, GMutex *deps_check_lock, GError **error) {
      31         2423 :     guint i = 0;
      32         2423 :     gboolean ret = FALSE;
      33         2423 :     GError *l_error = NULL;
      34         2423 :     guint val = 0;
      35              : 
      36         2423 :     val = (guint) g_atomic_int_get (avail_deps);
      37         2423 :     if ((val & req_deps) == req_deps)
      38              :         /* we have everything we need */
      39         1605 :         return TRUE;
      40              : 
      41              :     /* else */
      42              :     /* grab a lock to prevent multiple checks from running in parallel */
      43          818 :     g_mutex_lock (deps_check_lock);
      44              : 
      45              :     /* maybe the other thread found out we have all we needed? */
      46          818 :     val = (guint) g_atomic_int_get (avail_deps);
      47          818 :     if ((val & req_deps) == req_deps) {
      48            0 :         g_mutex_unlock (deps_check_lock);
      49            0 :         return TRUE;
      50              :     }
      51              : 
      52         3989 :     for (i=0; i < l_deps; i++) {
      53         3171 :         if (((1 << i) & req_deps) && !((1 << i) & val)) {
      54         1757 :             ret = bd_utils_check_util_version (deps_specs[i].name, deps_specs[i].version,
      55         1757 :                                                deps_specs[i].ver_arg, deps_specs[i].ver_regexp, &l_error);
      56              :             /* if not ret and l_error -> set/prepend error */
      57         1757 :             if (!ret) {
      58           73 :                 if (error) {
      59           73 :                     if (*error)
      60            0 :                         g_prefix_error (error, "%s\n", l_error->message);
      61              :                     else
      62           73 :                         g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_CHECK_ERROR,
      63           73 :                                      "%s", l_error->message);
      64              :                 }
      65           73 :                 g_clear_error (&l_error);
      66              :             } else
      67         1684 :                 g_atomic_int_or (avail_deps, 1 << i);
      68              :         }
      69              :     }
      70              : 
      71          818 :     g_mutex_unlock (deps_check_lock);
      72          818 :     val = (guint) g_atomic_int_get (avail_deps);
      73          818 :     return (val & req_deps) == req_deps;
      74              : }
      75              : 
      76              : G_GNUC_INTERNAL gboolean
      77           93 : check_module_deps (volatile guint *avail_deps, guint req_deps, const gchar *const*modules, guint l_modules, GMutex *deps_check_lock, GError **error) {
      78           93 :     guint i = 0;
      79           93 :     gboolean ret = FALSE;
      80           93 :     GError *l_error = NULL;
      81           93 :     guint val = 0;
      82              : 
      83           93 :     val = (guint) g_atomic_int_get (avail_deps);
      84           93 :     if ((val & req_deps) == req_deps)
      85              :         /* we have everything we need */
      86           70 :         return TRUE;
      87              : 
      88              :     /* else */
      89              :     /* grab a lock to prevent multiple checks from running in parallel */
      90           23 :     g_mutex_lock (deps_check_lock);
      91              : 
      92              :     /* maybe the other thread found out we have all we needed? */
      93           23 :     val = (guint) g_atomic_int_get (avail_deps);
      94           23 :     if ((val & req_deps) == req_deps) {
      95            0 :         g_mutex_unlock (deps_check_lock);
      96            0 :         return TRUE;
      97              :     }
      98              : 
      99           46 :     for (i=0; i < l_modules; i++) {
     100           23 :         if (((1 << i) & req_deps) && !((1 << i) & val)) {
     101           23 :             ret = bd_utils_have_kernel_module (modules[i], &l_error);
     102              :             /* if not ret and l_error -> set/prepend error */
     103           23 :             if (!ret) {
     104            0 :                 if (l_error) {
     105            0 :                     if (error) {
     106            0 :                         if (*error)
     107            0 :                             g_prefix_error (error, "%s\n", l_error->message);
     108              :                         else
     109            0 :                             g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_MODULE_CHECK_ERROR,
     110            0 :                                          "%s", l_error->message);
     111              :                     }
     112            0 :                     g_clear_error (&l_error);
     113              :                 } else {
     114              :                     /* no error from have_kernel_module means we don't have it */
     115            0 :                     if (error) {
     116            0 :                         if (*error)
     117            0 :                             g_prefix_error (error, "Kernel module '%s' not available\n", modules[i]);
     118              :                         else
     119            0 :                             g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_MODULE_CHECK_ERROR,
     120            0 :                                          "Kernel module '%s' not available", modules[i]);
     121              :                     }
     122              :                 }
     123              : 
     124              :             } else
     125           23 :                 g_atomic_int_or (avail_deps, 1 << i);
     126              :         }
     127              :     }
     128              : 
     129           23 :     g_mutex_unlock (deps_check_lock);
     130           23 :     val = (guint) g_atomic_int_get (avail_deps);
     131           23 :     return (val & req_deps) == req_deps;
     132              : }
     133              : 
     134            0 : static gboolean _check_dbus_api_version (GBusType bus_type, const gchar *version, const gchar *version_iface, const gchar* version_prop, const gchar *version_bus, const gchar *version_path, GError **error) {
     135            0 :     GDBusConnection *bus = NULL;
     136            0 :     GVariant *args = NULL;
     137            0 :     GVariant *ret = NULL;
     138            0 :     GVariant *prop = NULL;
     139            0 :     const gchar *bus_version = NULL;
     140            0 :     int cmp = 0;
     141              : 
     142            0 :     bus = g_bus_get_sync (bus_type, NULL, error);
     143            0 :     if (!bus)
     144            0 :         return FALSE;
     145              : 
     146            0 :     args = g_variant_new ("(ss)", version_iface, version_prop);
     147              : 
     148              :     /* consumes (frees) the 'args' parameter */
     149            0 :     ret = g_dbus_connection_call_sync (bus, version_bus, version_path, DBUS_PROPS_IFACE,
     150              :                                        "Get", args, NULL, G_DBUS_CALL_FLAGS_NONE,
     151              :                                        -1, NULL, error);
     152            0 :     if (!ret) {
     153            0 :         g_prefix_error (error, "Failed to get %s property of the %s object: ", version_prop, version_path);
     154            0 :         return FALSE;
     155              :     }
     156              : 
     157            0 :     g_variant_get (ret, "(v)", &prop);
     158            0 :     g_variant_unref (ret);
     159              : 
     160            0 :     bus_version = g_variant_get_string (prop, NULL);
     161              : 
     162            0 :     cmp = bd_utils_version_cmp (bus_version, version, error);
     163            0 :     g_variant_unref (prop);
     164              : 
     165            0 :     return cmp >= 0;
     166              : }
     167              : 
     168              : G_GNUC_INTERNAL gboolean
     169          422 : check_dbus_deps (volatile guint *avail_deps, guint req_deps, const DBusDep *buses, guint l_buses, GMutex *deps_check_lock, GError **error) {
     170          422 :     guint i = 0;
     171          422 :     gboolean ret = FALSE;
     172          422 :     GError *l_error = NULL;
     173          422 :     guint val = 0;
     174              : 
     175          422 :     val = (guint) g_atomic_int_get (avail_deps);
     176          422 :     if ((val & req_deps) == req_deps)
     177              :         /* we have everything we need */
     178          412 :         return TRUE;
     179              : 
     180              :     /* else */
     181              :     /* grab a lock to prevent multiple checks from running in parallel */
     182           10 :     g_mutex_lock (deps_check_lock);
     183              : 
     184              :     /* maybe the other thread found out we have all we needed? */
     185           10 :     val = (guint) g_atomic_int_get (avail_deps);
     186           10 :     if ((val & req_deps) == req_deps) {
     187            0 :         g_mutex_unlock (deps_check_lock);
     188            0 :         return TRUE;
     189              :     }
     190              : 
     191           30 :     for (i=0; i < l_buses; i++) {
     192           20 :         if (((1 << i) & req_deps) && !((1 << i) & val)) {
     193           10 :             ret = bd_utils_dbus_service_available (NULL, buses[i].bus_type, buses[i].bus_name, buses[i].obj_prefix, &l_error);
     194              :             /* if not ret and l_error -> set/prepend error */
     195           10 :             if (!ret) {
     196            0 :                 if (l_error) {
     197            0 :                     if (error) {
     198            0 :                         if (*error)
     199            0 :                             g_prefix_error (error, "%s\n", l_error->message);
     200              :                         else
     201            0 :                             g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_MODULE_CHECK_ERROR,
     202            0 :                                          "%s", l_error->message);
     203              :                     }
     204            0 :                     g_clear_error (&l_error);
     205              :                 } else {
     206            0 :                     if (error) {
     207            0 :                         if (*error)
     208            0 :                             g_prefix_error (error, "DBus service '%s' not available\n", buses[i].bus_name);
     209              :                         else
     210            0 :                             g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_MODULE_CHECK_ERROR,
     211            0 :                                          "DBus service '%s' not available", buses[i].bus_name);
     212              :                     }
     213              :                 }
     214              :             } else {
     215              :                 /* check version of the DBus API if specified */
     216           10 :                 if (buses[i].version) {
     217            0 :                     ret = _check_dbus_api_version (buses[i].bus_type, buses[i].version, buses[i].ver_intf, buses[i].ver_prop,
     218            0 :                                                    buses[i].bus_name, buses[i].ver_path, &l_error);
     219              :                     /* if not ret and l_error -> set/prepend error */
     220            0 :                     if (!ret) {
     221            0 :                         if (l_error) {
     222            0 :                             if (error) {
     223            0 :                                 if (*error)
     224            0 :                                     g_prefix_error (error, "%s\n", l_error->message);
     225              :                                 else
     226            0 :                                     g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_MODULE_CHECK_ERROR,
     227            0 :                                                  "%s", l_error->message);
     228              :                             }
     229            0 :                             g_clear_error (&l_error);
     230              :                         } else {
     231            0 :                             if (error) {
     232            0 :                                 if (*error)
     233            0 :                                     g_prefix_error (error, "DBus service '%s' not available in version '%s'\n", buses[i].bus_name, buses[i].version);
     234              :                                 else
     235            0 :                                     g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_MODULE_CHECK_ERROR,
     236            0 :                                                  "DBus service '%s' not available in version '%s'", buses[i].bus_name, buses[i].version);
     237              :                             }
     238              :                         }
     239              :                     } else
     240            0 :                        g_atomic_int_or (avail_deps, 1 << i);
     241              :                 } else
     242           10 :                     g_atomic_int_or (avail_deps, 1 << i);
     243              :             }
     244              :         }
     245              :     }
     246              : 
     247           10 :     g_mutex_unlock (deps_check_lock);
     248           10 :     val = (guint) g_atomic_int_get (avail_deps);
     249           10 :     return (val & req_deps) == req_deps;
     250              : }
     251              : 
     252            0 : static gboolean _check_util_feature (const gchar *util, const gchar *feature, const gchar *feature_arg, const gchar *feature_regexp, GError **error) {
     253            0 :     g_autofree gchar *util_path = NULL;
     254            0 :     const gchar *argv[] = {util, feature_arg, NULL};
     255            0 :     g_autofree gchar *output = NULL;
     256            0 :     gboolean succ = FALSE;
     257            0 :     GRegex *regex = NULL;
     258            0 :     GMatchInfo *match_info = NULL;
     259            0 :     g_autofree gchar *features_str = NULL;
     260            0 :     GError *l_error = NULL;
     261              : 
     262            0 :     util_path = g_find_program_in_path (util);
     263            0 :     if (!util_path) {
     264            0 :         g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_UNAVAILABLE,
     265              :                      "The '%s' utility is not available", util);
     266            0 :         return FALSE;
     267              :     }
     268              : 
     269            0 :     succ = bd_utils_exec_and_capture_output (argv, NULL, &output, &l_error);
     270            0 :     if (!succ) {
     271              :         /* if we got nothing on STDOUT, try using STDERR data from error message */
     272            0 :         if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
     273            0 :             output = g_strdup (l_error->message);
     274            0 :             g_clear_error (&l_error);
     275            0 :         } else if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED)) {
     276              :             /* exit status != 0, try using the output anyway */
     277            0 :             output = g_strdup (l_error->message);
     278            0 :             g_clear_error (&l_error);
     279              :         } else
     280            0 :             return FALSE;
     281              :     }
     282              : 
     283            0 :     if (feature_regexp) {
     284            0 :         regex = g_regex_new (feature_regexp, 0, 0, error);
     285            0 :         if (!regex) {
     286              :             /* error is already populated */
     287            0 :             return FALSE;
     288              :         }
     289              : 
     290            0 :         succ = g_regex_match (regex, output, 0, &match_info);
     291            0 :         if (!succ) {
     292            0 :             g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_FEATURE_CHECK_ERROR,
     293              :                          "Failed to determine %s's features from: %s", util, output);
     294            0 :             g_regex_unref (regex);
     295            0 :             g_match_info_free (match_info);
     296            0 :             return FALSE;
     297              :         }
     298            0 :         g_regex_unref (regex);
     299              : 
     300            0 :         features_str = g_match_info_fetch (match_info, 1);
     301            0 :         g_match_info_free (match_info);
     302              :     }
     303              :     else
     304            0 :         features_str = g_strstrip (g_strdup (output));
     305              : 
     306            0 :     if (!features_str || (g_strcmp0 (features_str, "") == 0)) {
     307            0 :         g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_FEATURE_CHECK_ERROR,
     308              :                      "Failed to determine %s's features from: %s", util, output);
     309            0 :         return FALSE;
     310              :     }
     311              : 
     312              : 
     313            0 :     if (!g_strrstr (features_str, feature)) {
     314            0 :         g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_FEATURE_UNAVAILABLE,
     315              :                      "Required feature %s not supported by this version of %s",
     316              :                      feature, util);
     317            0 :         return FALSE;
     318              :     }
     319              : 
     320            0 :     return TRUE;
     321              : }
     322              : 
     323              : G_GNUC_INTERNAL gboolean
     324            0 : check_features (volatile guint *avail_deps, guint req_deps, const UtilFeatureDep *deps_specs, guint l_deps, GMutex *deps_check_lock, GError **error) {
     325            0 :     guint i = 0;
     326            0 :     gboolean ret = FALSE;
     327            0 :     GError *l_error = NULL;
     328            0 :     guint val = 0;
     329              : 
     330            0 :     val = (guint) g_atomic_int_get (avail_deps);
     331            0 :     if ((val & req_deps) == req_deps)
     332              :         /* we have everything we need */
     333            0 :         return TRUE;
     334              : 
     335              :     /* else */
     336              :     /* grab a lock to prevent multiple checks from running in parallel */
     337            0 :     g_mutex_lock (deps_check_lock);
     338              : 
     339              :     /* maybe the other thread found out we have all we needed? */
     340            0 :     val = (guint) g_atomic_int_get (avail_deps);
     341            0 :     if ((val & req_deps) == req_deps) {
     342            0 :         g_mutex_unlock (deps_check_lock);
     343            0 :         return TRUE;
     344              :     }
     345              : 
     346            0 :     for (i=0; i < l_deps; i++) {
     347            0 :         if (((1 << i) & req_deps) && !((1 << i) & val)) {
     348            0 :             ret = _check_util_feature (deps_specs[i].util_name, deps_specs[i].feature,
     349            0 :                                        deps_specs[i].feature_arg, deps_specs[i].feature_regexp, &l_error);
     350              :             /* if not ret and l_error -> set/prepend error */
     351            0 :             if (!ret) {
     352            0 :                 if (l_error) {
     353            0 :                     if (error) {
     354            0 :                         if (*error)
     355            0 :                             g_prefix_error (error, "%s\n", l_error->message);
     356              :                         else
     357            0 :                             g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_FEATURE_CHECK_ERROR,
     358            0 :                                         "%s", l_error->message);
     359              :                     }
     360            0 :                     g_clear_error (&l_error);
     361              :                 }
     362              :             } else
     363            0 :                 g_atomic_int_or (avail_deps, 1 << i);
     364              :         }
     365              :     }
     366              : 
     367            0 :     g_mutex_unlock (deps_check_lock);
     368            0 :     val = (guint) g_atomic_int_get (avail_deps);
     369            0 :     return (val & req_deps) == req_deps;
     370              : }
        

Generated by: LCOV version 2.0-1