1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2014 Tomas Bzatek <tbzatek@redhat.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "config.h"
22 #include <glib/gi18n-lib.h>
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31
32 #include <glib.h>
33 #include <glib-object.h>
34 #include <gmodule.h>
35
36 #include "udisksdaemon.h"
37 #include "udisksdaemonutil.h"
38 #include "udisksmodulemanager.h"
39 #include "udisksconfigmanager.h"
40 #include "udisksprivate.h"
41 #include "udiskslogging.h"
42 #include "udisksmodule.h"
43 #include "udisksstate.h"
44
45 /**
46 * SECTION:UDisksModuleManager
47 * @title: UDisksModuleManager
48 * @short_description: Manage daemon modules
49 *
50 * ## UDisks modular approach # {#udisks-modular-design}
51 *
52 * UDisks functionality can be extended by modules. It's not that traditional
53 * fully pluggable system as we all know it, modules are essentially just
54 * carved-out parts of the daemon code and are free to access whatever internal
55 * daemon objects they need. There's no universal module API other than a couple
56 * of module initialization functions and a stateful module object. Out-of-tree
57 * modules are not supported either and no ABI guarantee exists at all.
58 *
59 * This fact allows us to stay code-wise simple and transparent. It's also easier
60 * to adapt modules for any change done to the core daemon. As a design decision
61 * and leading from #GType system limitation modules can't be unloaded once
62 * initialized. This may be a subject to change in the future, though unlikely.
63 *
64 * The primary motivation for introducing the modular system was to keep the daemon
65 * low on resource footprint for basic usage (typically desktop environments) and
66 * activating extended functionality only as needed (e.g. enterprise storage
67 * applications). As the extra information comes in form of additional D-Bus
68 * objects and interfaces, no difference should be observed by ordinary clients.
69 *
70 * ## Modules activation # {#udisks-modular-activation}
71 *
72 * The UDisks daemon constructs a #UDisksModuleManager singleton acting as
73 * a module manager. This object tracks module usage and takes care of their
74 * activation.
75 *
76 * By default #UDisksModuleManager is constructed on daemon startup with module
77 * loading delayed until requested. This can be overridden by the
78 * <literal>--force-load-modules</literal> and <literal>--disable-modules</literal>
79 * commandline switches that makes modules loaded right on startup or never loaded
80 * respectively.
81 *
82 * Clients are supposed to call the <link linkend="gdbus-method-org-freedesktop-UDisks2-Manager.EnableModule">org.freedesktop.UDisks2.Manager.EnableModule()</link>
83 * D-Bus method as a <emphasis>"greeter"</emphasis> call for each module requested.
84 * Proper error is reported should the module initialization fail or the module
85 * is not available. Clients are supposed to act accordingly and make sure that all
86 * requested modules are available and loaded prior to using any of the extra API.
87 *
88 * Upon successful activation, a <literal>modules-activated</literal> signal is
89 * emitted internally on the #UDisksModuleManager object. Any daemon objects
90 * connected to this signal are responsible for performing <emphasis>"coldplug"</emphasis>
91 * on exported objects to assure modules would pick up the devices they're
92 * interested in.
93 *
94 * ## D-Bus interface extensibility # {#udisks-modular-design-dbus}
95 *
96 * The modular approach is fairly simple, there are basically three primary ways
97 * of extending the D-Bus API:
98 * * by attaching custom interfaces to existing block and drive objects - see
99 * udisks_module_new_block_object_interface() and
100 * udisks_module_new_drive_object_interface().
101 * * by exporting objects of its own type (socalled <emphasis>"module objects"</emphasis>)
102 * directly on the object manager root - see udisks_module_new_object().
103 * * attaching a common manager interface on the master <filename>/org/freedesktop/UDisks2/Manager</filename>
104 * object - see udisks_module_new_manager().
105 *
106 * All these ways of extensibility are implemented as #UDisksModule methods and
107 * it is a #UDisksModuleManager task to provide interconnection between #UDisksModule
108 * instances and daemon objects representing drives and block devices.
109 */
110
111
112 /**
113 * UDisksModuleManager:
114 *
115 * The #UDisksModuleManager structure contains only private data and
116 * should only be accessed using the provided API.
117 */
118 struct _UDisksModuleManager
119 {
120 GObject parent_instance;
121
122 UDisksDaemon *daemon;
123
124 GList *modules;
125 GMutex modules_lock;
126
127 gboolean uninstalled;
128 };
129
130 typedef struct _UDisksModuleManagerClass UDisksModuleManagerClass;
131
132 struct _UDisksModuleManagerClass
133 {
134 GObjectClass parent_class;
135
136 /* Signals */
137 void (*modules_activated) (UDisksModuleManager *manager);
138 };
139
140 /*--------------------------------------------------------------------------------------------------------------*/
141
142 enum
143 {
144 PROP_0,
145 PROP_DAEMON,
146 PROP_UNINSTALLED,
147 };
148
149 enum
150 {
151 MODULES_ACTIVATED_SIGNAL,
152 LAST_SIGNAL,
153 };
154
155 static guint signals[LAST_SIGNAL] = { 0 };
156
G_DEFINE_TYPE(UDisksModuleManager,udisks_module_manager,G_TYPE_OBJECT)157 G_DEFINE_TYPE (UDisksModuleManager, udisks_module_manager, G_TYPE_OBJECT)
158
159 static void
160 udisks_module_manager_finalize (GObject *object)
161 {
162 UDisksModuleManager *manager = UDISKS_MODULE_MANAGER (object);
163
164 g_mutex_clear (&manager->modules_lock);
165
166 if (G_OBJECT_CLASS (udisks_module_manager_parent_class)->finalize != NULL)
167 G_OBJECT_CLASS (udisks_module_manager_parent_class)->finalize (object);
168 }
169
170
171 static void
udisks_module_manager_init(UDisksModuleManager * manager)172 udisks_module_manager_init (UDisksModuleManager *manager)
173 {
174 g_return_if_fail (UDISKS_IS_MODULE_MANAGER (manager));
175
176 g_mutex_init (&manager->modules_lock);
177 }
178
179 static gchar *
get_module_sopath_for_name(UDisksModuleManager * manager,const gchar * module_name)180 get_module_sopath_for_name (UDisksModuleManager *manager,
181 const gchar *module_name)
182 {
183 gchar *module_dir;
184 gchar *module_path;
185 gchar *lib_filename;
186
187 g_return_val_if_fail (UDISKS_IS_MODULE_MANAGER (manager), NULL);
188
189 if (! udisks_module_manager_get_uninstalled (manager))
190 module_dir = g_build_path (G_DIR_SEPARATOR_S, UDISKS_MODULE_DIR, NULL);
191 else
192 module_dir = g_build_path (G_DIR_SEPARATOR_S, BUILD_DIR, "modules", NULL);
193
194 lib_filename = g_strdup_printf ("lib" PACKAGE_NAME_UDISKS2 "_%s.so", module_name);
195 module_path = g_build_filename (G_DIR_SEPARATOR_S,
196 module_dir,
197 lib_filename,
198 NULL);
199 g_free (lib_filename);
200 g_free (module_dir);
201
202 return module_path;
203 }
204
205 static GList *
get_modules_list(UDisksModuleManager * manager)206 get_modules_list (UDisksModuleManager *manager)
207 {
208 UDisksConfigManager *config_manager;
209 GDir *dir;
210 GError *error = NULL;
211 GList *modules_list = NULL;
212 const gchar *dent;
213 gchar *module_dir;
214 gchar *pth;
215
216 g_return_val_if_fail (UDISKS_IS_MODULE_MANAGER (manager), NULL);
217
218 /* Open a directory with modules. */
219 if (! udisks_module_manager_get_uninstalled (manager))
220 module_dir = g_build_path (G_DIR_SEPARATOR_S, UDISKS_MODULE_DIR, NULL);
221 else
222 module_dir = g_build_path (G_DIR_SEPARATOR_S, BUILD_DIR, "modules", NULL);
223 dir = g_dir_open (module_dir, 0, &error);
224 if (! dir)
225 {
226 udisks_warning ("Error loading modules: %s", error->message);
227 g_clear_error (&error);
228 g_free (module_dir);
229 return NULL;
230 }
231
232 config_manager = udisks_daemon_get_config_manager (manager->daemon);
233 if (udisks_config_manager_get_modules_all (config_manager))
234 {
235 /* Load all the modules from modules directory. */
236 while ((dent = g_dir_read_name (dir)))
237 {
238 if (!g_str_has_suffix (dent, ".so"))
239 continue;
240
241 pth = g_build_filename (G_DIR_SEPARATOR_S, module_dir, dent, NULL);
242 modules_list = g_list_append (modules_list, pth);
243 }
244 }
245 else
246 {
247 GList *configured_modules;
248 GList *modules_i;
249
250 /* Load only those modules which are specified in config file. */
251 configured_modules = udisks_config_manager_get_modules (config_manager);
252 for (modules_i = configured_modules; modules_i; modules_i = modules_i->next)
253 {
254 pth = get_module_sopath_for_name (manager, modules_i->data);
255 modules_list = g_list_append (modules_list, pth);
256 }
257
258 g_list_free_full (configured_modules, (GDestroyNotify) g_free);
259 }
260
261 g_dir_close (dir);
262 g_free (module_dir);
263
264 return modules_list;
265 }
266
267 static gboolean
have_module(UDisksModuleManager * manager,const gchar * module_name)268 have_module (UDisksModuleManager *manager,
269 const gchar *module_name)
270 {
271 GList *l;
272
273 for (l = manager->modules; l != NULL; l = g_list_next (l))
274 {
275 UDisksModule *module = l->data;
276
277 if (g_strcmp0 (udisks_module_get_name (module), module_name) == 0)
278 return TRUE;
279 }
280
281 return FALSE;
282 }
283
284 static gboolean
load_single_module_unlocked(UDisksModuleManager * manager,const gchar * sopath,gboolean * do_notify,GError ** error)285 load_single_module_unlocked (UDisksModuleManager *manager,
286 const gchar *sopath,
287 gboolean *do_notify,
288 GError **error)
289 {
290 UDisksState *state;
291 GModule *handle;
292 gchar *module_id;
293 gchar *module_new_func_name;
294 UDisksModuleIDFunc module_id_func;
295 UDisksModuleNewFunc module_new_func;
296 UDisksModule *module;
297
298 handle = g_module_open (sopath, 0);
299 if (handle == NULL)
300 {
301 g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
302 "%s",
303 g_module_error ());
304 return FALSE;
305 }
306
307 if (! g_module_symbol (handle, "udisks_module_id", (gpointer *) &module_id_func))
308 {
309 g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
310 "%s: %s", sopath, g_module_error ());
311 g_module_close (handle);
312 return FALSE;
313 }
314
315 module_id = module_id_func ();
316 if (have_module (manager, module_id))
317 {
318 /* module with the same name already loaded, skip */
319 udisks_debug ("Module '%s' already loaded, skipping", module_id);
320 g_free (module_id);
321 g_module_close (handle);
322 return TRUE;
323 }
324
325 udisks_notice ("Loading module %s ...", module_id);
326
327 module_new_func_name = g_strdup_printf ("udisks_module_%s_new", module_id);
328 if (! g_module_symbol (handle, module_new_func_name, (gpointer *) &module_new_func))
329 {
330 g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
331 "%s", g_module_error ());
332 g_module_close (handle);
333 g_free (module_new_func_name);
334 g_free (module_id);
335 return FALSE;
336 }
337 g_free (module_new_func_name);
338
339 /* The following calls will initialize new GType's from the module,
340 * making it uneligible for unload.
341 */
342 g_module_make_resident (handle);
343
344 module = module_new_func (manager->daemon,
345 NULL /* cancellable */,
346 error);
347 if (module == NULL)
348 {
349 /* Workaround for broken modules to avoid segfault */
350 if (error == NULL)
351 {
352 g_set_error_literal (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
353 "unknown fatal error");
354 }
355 g_free (module_id);
356 g_module_close (handle);
357 return FALSE;
358 }
359
360 manager->modules = g_list_append (manager->modules, module);
361
362 state = udisks_daemon_get_state (manager->daemon);
363 udisks_state_add_module (state, module_id);
364
365 g_free (module_id);
366
367 *do_notify = TRUE;
368 return TRUE;
369 }
370
371 /**
372 * udisks_module_manager_load_single_module:
373 * @manager: A #UDisksModuleManager instance.
374 * @name: Module name.
375 * @error: Return location for error or %NULL.
376 *
377 * Loads single module and emits the <literal>modules-activated</literal> signal
378 * in case the module activation was successful. Already active module is not
379 * being reinitialized on subsequent calls to this method and %TRUE is returned
380 * immediately.
381 *
382 * Returns: %TRUE if module was activated successfully, %FALSE otherwise with @error being set.
383 */
384 gboolean
udisks_module_manager_load_single_module(UDisksModuleManager * manager,const gchar * name,GError ** error)385 udisks_module_manager_load_single_module (UDisksModuleManager *manager,
386 const gchar *name,
387 GError **error)
388 {
389 gchar *module_path;
390 gboolean do_notify = FALSE;
391 gboolean ret;
392
393 g_return_val_if_fail (UDISKS_IS_MODULE_MANAGER (manager), FALSE);
394
395 module_path = get_module_sopath_for_name (manager, name);
396 if (module_path == NULL)
397 {
398 g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
399 "Cannot determine module path for '%s'",
400 name);
401 return FALSE;
402 }
403
404 g_mutex_lock (&manager->modules_lock);
405 ret = load_single_module_unlocked (manager, module_path, &do_notify, error);
406 g_mutex_unlock (&manager->modules_lock);
407
408 g_free (module_path);
409
410 if (do_notify)
411 {
412 /* This will run connected signal handlers synchronously, i.e.
413 * performs coldplug on all existing objects within #UDisksLinuxProvider.
414 */
415 g_signal_emit (manager, signals[MODULES_ACTIVATED_SIGNAL], 0);
416 }
417
418 return ret;
419 }
420
421 /**
422 * udisks_module_manager_load_modules:
423 * @manager: A #UDisksModuleManager instance.
424 *
425 * Loads all modules at a time and emits the <literal>modules-activated</literal>
426 * signal in case any new module has been activated. Modules that are already loaded
427 * are skipped on subsequent calls to this method.
428 */
429 void
udisks_module_manager_load_modules(UDisksModuleManager * manager)430 udisks_module_manager_load_modules (UDisksModuleManager *manager)
431 {
432 GList *modules_to_load;
433 GList *modules_to_load_tmp;
434 GError *error = NULL;
435 gboolean do_notify = FALSE;
436
437 g_return_if_fail (UDISKS_IS_MODULE_MANAGER (manager));
438
439 g_mutex_lock (&manager->modules_lock);
440
441 /* Load the modules */
442 modules_to_load = get_modules_list (manager);
443 for (modules_to_load_tmp = modules_to_load;
444 modules_to_load_tmp;
445 modules_to_load_tmp = modules_to_load_tmp->next)
446 {
447
448 if (! load_single_module_unlocked (manager,
449 modules_to_load_tmp->data,
450 &do_notify,
451 &error))
452 {
453 udisks_critical ("Error loading module: %s",
454 error->message);
455 g_clear_error (&error);
456 continue;
457 }
458 }
459
460 g_mutex_unlock (&manager->modules_lock);
461
462 g_list_free_full (modules_to_load, (GDestroyNotify) g_free);
463
464 /* Emit 'modules-activated' in case new modules have been loaded. */
465 if (do_notify)
466 g_signal_emit (manager, signals[MODULES_ACTIVATED_SIGNAL], 0);
467 }
468
469 /**
470 * udisks_module_manager_unload_modules:
471 * @manager: A #UDisksModuleManager instance.
472 *
473 * Unloads all modules at a time. A <literal>modules-activated</literal> signal
474 * is emitted if there are any modules staged for unload to give listeners room
475 * to unexport all module interfaces and objects. The udisks_module_manager_get_modules()
476 * would return %NULL at that time. Note that proper module unload is not fully
477 * supported, this is just a convenience call for cleanup.
478 */
479 void
udisks_module_manager_unload_modules(UDisksModuleManager * manager)480 udisks_module_manager_unload_modules (UDisksModuleManager *manager)
481 {
482 UDisksState *state;
483 GList *l;
484
485 g_return_if_fail (UDISKS_IS_MODULE_MANAGER (manager));
486
487 g_mutex_lock (&manager->modules_lock);
488
489 l = g_steal_pointer (&manager->modules);
490 if (l)
491 {
492 /* notify listeners that the list of active modules has changed */
493 g_signal_emit (manager, signals[MODULES_ACTIVATED_SIGNAL], 0);
494 }
495 /* only unref module objects after all listeners have performed cleanup */
496 g_list_free_full (l, g_object_unref);
497
498 /* clear the state file */
499 state = udisks_daemon_get_state (manager->daemon);
500 udisks_state_clear_modules (state);
501
502 g_mutex_unlock (&manager->modules_lock);
503 }
504
505 static void
udisks_module_manager_constructed(GObject * object)506 udisks_module_manager_constructed (GObject *object)
507 {
508 if (! g_module_supported ())
509 {
510 udisks_warning ("Modules are unsupported on the current platform");
511 return;
512 }
513
514 if (G_OBJECT_CLASS (udisks_module_manager_parent_class)->constructed != NULL)
515 (*G_OBJECT_CLASS (udisks_module_manager_parent_class)->constructed) (object);
516 }
517
518 static void
udisks_module_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)519 udisks_module_manager_get_property (GObject *object,
520 guint prop_id,
521 GValue *value,
522 GParamSpec *pspec)
523 {
524 UDisksModuleManager *manager = UDISKS_MODULE_MANAGER (object);
525
526 switch (prop_id)
527 {
528 case PROP_DAEMON:
529 g_value_set_object (value, udisks_module_manager_get_daemon (manager));
530 break;
531
532 case PROP_UNINSTALLED:
533 g_value_set_boolean (value, manager->uninstalled);
534 break;
535
536 default:
537 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
538 break;
539 }
540 }
541
542 static void
udisks_module_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)543 udisks_module_manager_set_property (GObject *object,
544 guint prop_id,
545 const GValue *value,
546 GParamSpec *pspec)
547 {
548 UDisksModuleManager *manager = UDISKS_MODULE_MANAGER (object);
549
550 switch (prop_id)
551 {
552 case PROP_DAEMON:
553 g_assert (manager->daemon == NULL);
554 /* We don't take a reference to the daemon */
555 manager->daemon = g_value_get_object (value);
556 break;
557
558 case PROP_UNINSTALLED:
559 manager->uninstalled = g_value_get_boolean (value);
560 break;
561
562 default:
563 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
564 break;
565 }
566 }
567
568 static void
udisks_module_manager_class_init(UDisksModuleManagerClass * klass)569 udisks_module_manager_class_init (UDisksModuleManagerClass *klass)
570 {
571 GObjectClass *gobject_class = (GObjectClass *) klass;
572
573 gobject_class->finalize = udisks_module_manager_finalize;
574 gobject_class->constructed = udisks_module_manager_constructed;
575 gobject_class->get_property = udisks_module_manager_get_property;
576 gobject_class->set_property = udisks_module_manager_set_property;
577
578 /**
579 * UDisksModuleManager:daemon:
580 *
581 * The #UDisksDaemon for the object.
582 */
583 g_object_class_install_property (gobject_class,
584 PROP_DAEMON,
585 g_param_spec_object ("daemon",
586 "Daemon",
587 "The daemon for the object",
588 UDISKS_TYPE_DAEMON,
589 G_PARAM_READABLE |
590 G_PARAM_WRITABLE |
591 G_PARAM_CONSTRUCT_ONLY |
592 G_PARAM_STATIC_STRINGS));
593
594 /**
595 * UDisksModuleManager:uninstalled:
596 *
597 * Loads modules from the build directory.
598 */
599 g_object_class_install_property (gobject_class,
600 PROP_UNINSTALLED,
601 g_param_spec_boolean ("uninstalled",
602 "Load modules from the build directory",
603 "Whether the modules should be loaded from the build directory",
604 FALSE,
605 G_PARAM_READABLE |
606 G_PARAM_WRITABLE |
607 G_PARAM_CONSTRUCT_ONLY));
608
609 /**
610 * UDisksModuleManager:modules-activated:
611 * @manager: A #UDisksModuleManager.
612 *
613 * Emitted after new modules have been activated.
614 *
615 * This signal is emitted in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread that @manager was created in.
616 */
617 signals[MODULES_ACTIVATED_SIGNAL] = g_signal_new ("modules-activated",
618 G_TYPE_FROM_CLASS (klass),
619 G_SIGNAL_RUN_LAST,
620 G_STRUCT_OFFSET (UDisksModuleManagerClass, modules_activated),
621 NULL,
622 NULL,
623 g_cclosure_marshal_generic,
624 G_TYPE_NONE,
625 0);
626 }
627
628 /**
629 * udisks_module_manager_new:
630 * @daemon: A #UDisksDaemon instance.
631 *
632 * Creates a new #UDisksModuleManager object.
633 *
634 * Returns: A #UDisksModuleManager. Free with g_object_unref().
635 */
636 UDisksModuleManager *
udisks_module_manager_new(UDisksDaemon * daemon)637 udisks_module_manager_new (UDisksDaemon *daemon)
638 {
639 return UDISKS_MODULE_MANAGER (g_object_new (UDISKS_TYPE_MODULE_MANAGER,
640 "daemon", daemon, NULL));
641 }
642
643 /**
644 * udisks_module_manager_new_uninstalled:
645 * @daemon: A #UDisksDaemon instance.
646 *
647 * Creates a new #UDisksModuleManager object with indication that
648 * the daemon runs from a source tree.
649 *
650 * Returns: A #UDisksModuleManager. Free with g_object_notify().
651 */
652 UDisksModuleManager *
udisks_module_manager_new_uninstalled(UDisksDaemon * daemon)653 udisks_module_manager_new_uninstalled (UDisksDaemon *daemon)
654 {
655 return UDISKS_MODULE_MANAGER (g_object_new (UDISKS_TYPE_MODULE_MANAGER,
656 "daemon", daemon,
657 "uninstalled", TRUE, NULL));
658 }
659
660 /**
661 * udisks_module_manager_get_daemon:
662 * @manager: A #UDisksModuleManager.
663 *
664 * Gets the daemon used by @manager.
665 *
666 * Returns: A #UDisksDaemon. Do not free, the object is owned by @manager.
667 */
668 UDisksDaemon *
udisks_module_manager_get_daemon(UDisksModuleManager * manager)669 udisks_module_manager_get_daemon (UDisksModuleManager *manager)
670 {
671 g_return_val_if_fail (UDISKS_IS_MODULE_MANAGER (manager), NULL);
672 return manager->daemon;
673 }
674
675 /**
676 * udisks_module_manager_get_uninstalled:
677 * @manager: A #UDisksModuleManager.
678 *
679 * Indicates whether the udisks daemon runs from a source tree
680 * rather than being a regular system instance.
681 *
682 * Returns: %TRUE when the daemon runs from a source tree, %FALSE otherwise.
683 */
684 gboolean
udisks_module_manager_get_uninstalled(UDisksModuleManager * manager)685 udisks_module_manager_get_uninstalled (UDisksModuleManager *manager)
686 {
687 g_return_val_if_fail (UDISKS_IS_MODULE_MANAGER (manager), FALSE);
688 return manager->uninstalled;
689 }
690
691 /* ---------------------------------------------------------------------------------------------------- */
692
693 /**
694 * udisks_module_manager_get_modules:
695 * @manager: A #UDisksModuleManager instance.
696 *
697 * Gets list of active modules. Can be called from different threads.
698 *
699 * Returns: (transfer full) (nullable) (element-type UDisksModule): A list of #UDisksModule
700 * or %NULL if no modules are presently loaded. Free the elements
701 * with g_object_unref().
702 */
703 GList *
udisks_module_manager_get_modules(UDisksModuleManager * manager)704 udisks_module_manager_get_modules (UDisksModuleManager *manager)
705 {
706 GList *l;
707
708 g_return_val_if_fail (UDISKS_IS_MODULE_MANAGER (manager), NULL);
709
710 /* Return fast to avoid bottleneck over locking, expecting
711 * a simple pointer check would be atomic.
712 */
713 if (manager->modules == NULL)
714 return NULL;
715
716 g_mutex_lock (&manager->modules_lock);
717 l = g_list_copy_deep (manager->modules, (GCopyFunc) udisks_g_object_ref_copy, NULL);
718 g_mutex_unlock (&manager->modules_lock);
719
720 return l;
721 }
722