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