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 2479 : check_deps (volatile guint *avail_deps, guint req_deps, const UtilDep *deps_specs, guint l_deps, GMutex *deps_check_lock, GError **error) {
31 2479 : guint i = 0;
32 2479 : gboolean ret = FALSE;
33 2479 : GError *l_error = NULL;
34 2479 : guint val = 0;
35 :
36 2479 : val = (guint) g_atomic_int_get (avail_deps);
37 2479 : if ((val & req_deps) == req_deps)
38 : /* we have everything we need */
39 1648 : return TRUE;
40 :
41 : /* else */
42 : /* grab a lock to prevent multiple checks from running in parallel */
43 831 : g_mutex_lock (deps_check_lock);
44 :
45 : /* maybe the other thread found out we have all we needed? */
46 831 : val = (guint) g_atomic_int_get (avail_deps);
47 831 : if ((val & req_deps) == req_deps) {
48 0 : g_mutex_unlock (deps_check_lock);
49 0 : return TRUE;
50 : }
51 :
52 4040 : for (i=0; i < l_deps; i++) {
53 3209 : if (((1 << i) & req_deps) && !((1 << i) & val)) {
54 1770 : ret = bd_utils_check_util_version (deps_specs[i].name, deps_specs[i].version,
55 1770 : deps_specs[i].ver_arg, deps_specs[i].ver_regexp, &l_error);
56 : /* if not ret and l_error -> set/prepend error */
57 1770 : if (!ret) {
58 78 : if (error) {
59 78 : if (*error)
60 0 : g_prefix_error (error, "%s\n", l_error->message);
61 : else
62 78 : g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_UTIL_CHECK_ERROR,
63 78 : "%s", l_error->message);
64 : }
65 78 : g_clear_error (&l_error);
66 : } else
67 1692 : g_atomic_int_or (avail_deps, 1 << i);
68 : }
69 : }
70 :
71 831 : g_mutex_unlock (deps_check_lock);
72 831 : val = (guint) g_atomic_int_get (avail_deps);
73 831 : 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 442 : check_dbus_deps (volatile guint *avail_deps, guint req_deps, const DBusDep *buses, guint l_buses, GMutex *deps_check_lock, GError **error) {
170 442 : guint i = 0;
171 442 : gboolean ret = FALSE;
172 442 : GError *l_error = NULL;
173 442 : guint val = 0;
174 :
175 442 : val = (guint) g_atomic_int_get (avail_deps);
176 442 : if ((val & req_deps) == req_deps)
177 : /* we have everything we need */
178 430 : return TRUE;
179 :
180 : /* else */
181 : /* grab a lock to prevent multiple checks from running in parallel */
182 12 : g_mutex_lock (deps_check_lock);
183 :
184 : /* maybe the other thread found out we have all we needed? */
185 12 : val = (guint) g_atomic_int_get (avail_deps);
186 12 : if ((val & req_deps) == req_deps) {
187 0 : g_mutex_unlock (deps_check_lock);
188 0 : return TRUE;
189 : }
190 :
191 36 : for (i=0; i < l_buses; i++) {
192 24 : if (((1 << i) & req_deps) && !((1 << i) & val)) {
193 12 : 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 12 : 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 12 : 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 12 : g_atomic_int_or (avail_deps, 1 << i);
243 : }
244 : }
245 : }
246 :
247 12 : g_mutex_unlock (deps_check_lock);
248 12 : val = (guint) g_atomic_int_get (avail_deps);
249 12 : 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 : }
|