1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (C) 2020 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 "udisksmodule.h"
23 
24 #include "udisksdaemon.h"
25 
26 /**
27  * SECTION:UDisksModule
28  * @title: UDisksModule
29  * @short_description: Daemon module
30  *
31  * ## UDisks module design # {#udisks-module-design}
32  *
33  * #UDisksModule is a stateful object that represents a daemon module. It is supposed
34  * to hold arbitrary runtime data and perform proper initialization and cleanup within
35  * its constructor and destructor. Once initialized by #UDisksModuleManager the instance
36  * is usually kept around until the daemon exits. Although proper module unloading
37  * is not currently implemented the object destructor may be actually called in some
38  * cases.
39  *
40  * Derived #UDisksModule object is supposed to implement failable initialization
41  * and return proper error that the #UDisksModuleManager would propagate further
42  * up the stack. Modules are free to use failable initialization for checking runtime
43  * dependencies such as additional config files and fail if misconfigured.
44  *
45  * ## UDisks module naming conventions # {#udisks-module-naming}
46  *
47  * Every module must implement and export two symbols that are used as entry points: <link linkend="UDisksModuleIDFunc"><function>udisks_module_id()</function></link>
48  * and <link linkend="UDisksModuleNewFunc"><function>udisks_module_ID_new()</function></link>
49  * where <literal>ID</literal> is a string returned by <link linkend="UDisksModuleIDFunc"><function>udisks_module_id()</function></link>.
50  * This identification string is subsequently used at several places - primarily
51  * serves as an unique and user readable module identifier (e.g. <literal>lvm2</literal>)
52  * passed in as an argument to the <link linkend="gdbus-method-org-freedesktop-UDisks2-Manager.EnableModule">org.freedesktop.UDisks2.Manager.EnableModule()</link>
53  * method call.
54  *
55  * Physically modules are essentially regular shared objects (<literal>.so</literal>)
56  * that are loaded from <filename>$(libdir)/udisks2/modules</filename> directory
57  * (typically <filename>/usr/lib/udisks2/modules</filename>). No extra service or
58  * config files are needed, however a specific file naming of <filename>libudisks2_<emphasis>ID</emphasis>.so</filename>
59  * is required.
60  *
61  * ## Module API # {#udisks-modular-api}
62  *
63  * Other than the two entry points described in last paragraph the rest of the daemon
64  * to module interaction is done via #UDisksModule class methods over an instance
65  * created by the <link linkend="UDisksModuleNewFunc"><function>udisks_module_ID_new()</function></link>
66  * constructor. Please see particular #UDisksModule methods for detailed description
67  * of each way of extending the daemon functionality. Most methods are pretty
68  * straightforward with the exception of extra drive and block object interfaces.
69  *
70  * It's important to provide udisks_module_get_block_object_interface_types() and
71  * udisks_module_new_block_object_interface() methods (or <literal>drive</literal>
72  * respectively) always in pairs as the #UDisksLinuxBlockObject and #UDisksLinuxDriveObject
73  * machinery needs to register available interface skeleton types first and subsequently
74  * create target interfaces for each specified type and route uevents onto. There
75  * can be only one extra interface of a given type on a single #UDisksLinuxBlockObject
76  * or #UDisksLinuxDriveObject object.
77  *
78  * In case of an existing interface for a particular type uevents are routed through
79  * the udisks_module_object_process_uevent() method of a #UDisksModuleObject interface
80  * that the newly created #GDBusInterfaceSkeleton interface has to implement. This
81  * call is supposed to process updated information and indicate via the return @keep
82  * argument whether the particular interface is valid or should be removed from
83  * the object.
84  *
85  * In case no #GDBusInterfaceSkeleton interface of a given type is attached on the
86  * particular object, udisks_module_new_block_object_interface() or
87  * udisks_module_new_drive_object_interface() methods respectively are called
88  * in attempt to create new one. These methods are supposed to check whether the
89  * interface type is applicable for the current object and return %NULL if not.
90  *
91  * Exposing independent module objects on the master UDisks object manager as another
92  * way of daemon extensibility works in a similar way - please see udisks_module_new_object()
93  * for detailed description.
94  */
95 
96 enum
97 {
98   PROP_0,
99   PROP_DAEMON,
100   PROP_NAME,
101 };
102 
G_DEFINE_TYPE(UDisksModule,udisks_module,G_TYPE_OBJECT)103 G_DEFINE_TYPE (UDisksModule, udisks_module, G_TYPE_OBJECT)
104 
105 
106 static void
107 udisks_module_finalize (GObject *object)
108 {
109   UDisksModule *module = UDISKS_MODULE (object);
110 
111   g_free (module->name);
112 
113   if (G_OBJECT_CLASS (udisks_module_parent_class)->finalize != NULL)
114     G_OBJECT_CLASS (udisks_module_parent_class)->finalize (object);
115 }
116 
117 static void
udisks_module_init(UDisksModule * module)118 udisks_module_init (UDisksModule *module)
119 {
120 }
121 
122 static void
udisks_module_constructed(GObject * object)123 udisks_module_constructed (GObject *object)
124 {
125   if (G_OBJECT_CLASS (udisks_module_parent_class)->constructed != NULL)
126     (*G_OBJECT_CLASS (udisks_module_parent_class)->constructed) (object);
127 }
128 
129 static void
udisks_module_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)130 udisks_module_get_property (GObject    *object,
131                             guint       prop_id,
132                             GValue     *value,
133                             GParamSpec *pspec)
134 {
135   UDisksModule *module = UDISKS_MODULE (object);
136 
137   switch (prop_id)
138     {
139     case PROP_DAEMON:
140       g_value_set_object (value, udisks_module_get_daemon (module));
141       break;
142 
143     case PROP_NAME:
144       g_value_set_string (value, udisks_module_get_name (module));
145       break;
146 
147     default:
148       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
149       break;
150     }
151 }
152 
153 static void
udisks_module_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)154 udisks_module_set_property (GObject      *object,
155                             guint         prop_id,
156                             const GValue *value,
157                             GParamSpec   *pspec)
158 {
159   UDisksModule *module = UDISKS_MODULE (object);
160 
161   switch (prop_id)
162     {
163     case PROP_DAEMON:
164       g_assert (module->daemon == NULL);
165       /* We don't take a reference to the daemon */
166       module->daemon = g_value_get_object (value);
167       break;
168 
169     case PROP_NAME:
170       g_assert (module->name == NULL);
171       module->name = g_value_dup_string (value);
172       break;
173 
174     default:
175       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
176       break;
177     }
178 }
179 
180 /* ---------------------------------------------------------------------------------------------------- */
181 
182 static GDBusInterfaceSkeleton  *
udisks_module_new_manager_default(UDisksModule * module)183 udisks_module_new_manager_default (UDisksModule *module)
184 {
185   return NULL;
186 }
187 
188 static GDBusObjectSkeleton **
udisks_module_new_object_default(UDisksModule * module,UDisksLinuxDevice * device)189 udisks_module_new_object_default (UDisksModule      *module,
190                                   UDisksLinuxDevice *device)
191 {
192   return NULL;
193 }
194 
195 static gchar *
udisks_module_track_parent_default(UDisksModule * module,const gchar * path,gchar ** uuid)196 udisks_module_track_parent_default (UDisksModule  *module,
197                                     const gchar   *path,
198                                     gchar        **uuid)
199 {
200   return NULL;
201 }
202 
203 static GType *
udisks_module_get_block_object_interface_types_default(UDisksModule * module)204 udisks_module_get_block_object_interface_types_default (UDisksModule *module)
205 {
206   return NULL;
207 }
208 
209 static GType *
udisks_module_get_drive_object_interface_types_default(UDisksModule * module)210 udisks_module_get_drive_object_interface_types_default (UDisksModule *module)
211 {
212   return NULL;
213 }
214 
215 static GDBusInterfaceSkeleton *
udisks_module_new_block_object_interface_default(UDisksModule * module,UDisksLinuxBlockObject * object,GType interface_type)216 udisks_module_new_block_object_interface_default (UDisksModule           *module,
217                                                   UDisksLinuxBlockObject *object,
218                                                   GType                   interface_type)
219 {
220   return NULL;
221 }
222 
223 static GDBusInterfaceSkeleton *
udisks_module_new_drive_object_interface_default(UDisksModule * module,UDisksLinuxDriveObject * object,GType interface_type)224 udisks_module_new_drive_object_interface_default (UDisksModule           *module,
225                                                   UDisksLinuxDriveObject *object,
226                                                   GType                   interface_type)
227 {
228   return NULL;
229 }
230 
231 /* ---------------------------------------------------------------------------------------------------- */
232 
233 static void
udisks_module_class_init(UDisksModuleClass * klass)234 udisks_module_class_init (UDisksModuleClass *klass)
235 {
236   GObjectClass *gobject_class = (GObjectClass *) klass;
237 
238   gobject_class->finalize     = udisks_module_finalize;
239   gobject_class->constructed  = udisks_module_constructed;
240   gobject_class->get_property = udisks_module_get_property;
241   gobject_class->set_property = udisks_module_set_property;
242 
243   klass->new_manager                      = udisks_module_new_manager_default;
244   klass->new_object                       = udisks_module_new_object_default;
245   klass->track_parent                     = udisks_module_track_parent_default;
246   klass->get_block_object_interface_types = udisks_module_get_block_object_interface_types_default;
247   klass->get_drive_object_interface_types = udisks_module_get_drive_object_interface_types_default;
248   klass->new_block_object_interface       = udisks_module_new_block_object_interface_default;
249   klass->new_drive_object_interface       = udisks_module_new_drive_object_interface_default;
250 
251   /**
252    * UDisksModule:daemon:
253    *
254    * The #UDisksDaemon for the object.
255    *
256    * Since: 2.9.0
257    */
258   g_object_class_install_property (gobject_class,
259                                    PROP_DAEMON,
260                                    g_param_spec_object ("daemon",
261                                                         "Daemon",
262                                                         "The daemon for the object",
263                                                         UDISKS_TYPE_DAEMON,
264                                                         G_PARAM_READABLE |
265                                                         G_PARAM_WRITABLE |
266                                                         G_PARAM_CONSTRUCT_ONLY |
267                                                         G_PARAM_STATIC_STRINGS));
268 
269   /**
270    * UDisksModule:name:
271    *
272    * Name of the module.
273    *
274    * Since: 2.9.0
275    */
276   g_object_class_install_property (gobject_class,
277                                    PROP_NAME,
278                                    g_param_spec_string ("name",
279                                                         "Name",
280                                                         "Name of the module",
281                                                         NULL,
282                                                         G_PARAM_READABLE |
283                                                         G_PARAM_WRITABLE |
284                                                         G_PARAM_CONSTRUCT_ONLY |
285                                                         G_PARAM_STATIC_STRINGS));
286 }
287 
288 /**
289  * udisks_module_get_name:
290  * @module: A #UDisksModule.
291  *
292  * Gets the name of the @module.
293  *
294  * Returns: (transfer none): A module name string. Do not free, the string is owned by @module.
295  *
296  * Since: 2.9.0
297  */
298 const gchar *
udisks_module_get_name(UDisksModule * module)299 udisks_module_get_name (UDisksModule *module)
300 {
301   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
302   return module->name;
303 }
304 
305 /**
306  * udisks_module_get_daemon:
307  * @module: A #UDisksModule.
308  *
309  * Gets the daemon used by @module.
310  *
311  * Returns: (transfer none): A #UDisksDaemon. Do not free, the object is owned by @module.
312  *
313  * Since: 2.9.0
314  */
315 UDisksDaemon *
udisks_module_get_daemon(UDisksModule * module)316 udisks_module_get_daemon (UDisksModule *module)
317 {
318   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
319   return module->daemon;
320 }
321 
322 /* ---------------------------------------------------------------------------------------------------- */
323 
324 /**
325  * udisks_module_new_manager:
326  * @module: A #UDisksModule.
327  *
328  * Creates a new #GDBusInterfaceSkeleton instance carrying an additional D-Bus interface
329  * to be exported on the #UDisksManager object (at the <filename>/org/freedesktop/UDisks2/Manager</filename>
330  * path). It is a fairly simple stateless object not related to any device and serves
331  * the purpose of performing general tasks or creating new resources. Only a single
332  * manager interface can be provided by each module.
333  *
334  * Returns: (transfer full) (nullable): A new #GDBusInterfaceSkeleton. Free with g_object_unref().
335  *
336  * Since: 2.9.0
337  */
338 GDBusInterfaceSkeleton *
udisks_module_new_manager(UDisksModule * module)339 udisks_module_new_manager (UDisksModule *module)
340 {
341   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
342 
343   return UDISKS_MODULE_GET_CLASS (module)->new_manager (module);
344 }
345 
346 /**
347  * udisks_module_new_object:
348  * @module: A #UDisksModule.
349  * @device: A #UDisksLinuxDevice device object.
350  *
351  * Creates one or more #GDBusObjectSkeleton objects that implement the #UDisksModuleObject
352  * interface. Multiple objects may be returned by this method call, e.g. in case
353  * more than one object type is needed in order to represent a particular feature.
354  *
355  * Objects are exported by #UDisksLinuxProvider on the master object manager under
356  * the <filename>/org/freedesktop/UDisks2</filename> path just like regular block
357  * and drive objects. This allows to create brand new object types fully handled
358  * by modules and providing custom interfaces. Objects in this scope are meant to be
359  * of virtual kind and are pretty flexible in this regard - not necessarily bound
360  * to any specific block device or drive. Perhaps even representing a group of resources.
361  * For illustration this kind of object may represent a RAID array comprised of several
362  * block devices, devices of the same kind such as loop devices or any higher level
363  * representation of something else.
364  *
365  * Note that it's not currently possible to share module objects across multiple
366  * modules with the intention to attach extra interfaces on a foreign module object.
367  * In such case each module needs to export its own unique object, no matter if
368  * they share or represent similar kind of resource.
369  *
370  * This method may be called quite often, for nearly any uevent received. It's done
371  * this way for broad flexibility and to give module objects a chance to claim any
372  * device needed.
373  *
374  * Module objects are supposed to maintain internal list of claimed devices and track
375  * their validity, i.e. indicate removal only after all tracked devices are gone.
376  * Every module object may claim one or more devices. #UDisksLinuxProvider essentially
377  * provides uevent routing and guarantees that existing objects are asked first to
378  * consider a claim of the @device before new object is attempted to be created.
379  * This works always in the scope of a particular module, i.e. existing module objects
380  * and their claims are always considered separately for each module.
381  *
382  * The uevent routing works as follows:
383  *   1. Existing module objects are asked first to process the uevent for a particular
384  *      @device via the udisks_module_object_process_uevent() method on the
385  *      #UDisksModuleObject interface. The method return value and the @keep argument
386  *      control the claim:
387  *        * method return value of %FALSE means the object doesn't currently hold
388  *          the claim of the @device and is not interested of making new one. The
389  *          return value of @keep is ignored in this case.
390  *        * method return value of %TRUE and the @keep return value of %FALSE indicates
391  *          the object is not valid anymore and should be unexported from the object
392  *          manager.
393  *        * method return value of %TRUE and the @keep return value of %TRUE indicates
394  *          the object has processed the updated information and remains valid.
395  *
396  *   2. In case the @device has not been claimed by any existing module object, meaning
397  *      all the udisks_module_object_process_uevent() method calls from previous step
398  *      returned %FALSE, only then a new object is attempted to be created via this
399  *      udisks_module_new_object() method call. If there was a claim release in
400  *      the previous step, no attempt to create new object is made to prevent creating
401  *      bogus objects for recently released devices.
402  *
403  * Returns: (element-type GDBusObjectSkeleton) (array zero-terminated=1) (nullable) (transfer full):
404  *          NULL-terminated array of new #GDBusObjectSkeleton objects or %NULL when
405  *          the module is not interested in the @device.
406  *
407  * Since: 2.9.0
408  */
409 GDBusObjectSkeleton **
udisks_module_new_object(UDisksModule * module,UDisksLinuxDevice * device)410 udisks_module_new_object (UDisksModule      *module,
411                           UDisksLinuxDevice *device)
412 {
413   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
414 
415   return UDISKS_MODULE_GET_CLASS (module)->new_object (module, device);
416 }
417 
418 /**
419  * udisks_module_track_parent:
420  * @module: A #UDisksModule.
421  * @path: object path of a child to find parent of
422  * @uuid: a pointer to return parent UUID string
423  *
424  * Finds a parent block device and returns its object path and UUID.
425  * If the return value is %NULL, the value of @uuid has not been changed.
426  * Related to udisks_daemon_get_parent_for_tracking().
427  *
428  * Returns: (transfer full) (nullable): object path of the parent device. Free with g_free().
429  *
430  * Since: 2.9.0
431  */
432 gchar *
udisks_module_track_parent(UDisksModule * module,const gchar * path,gchar ** uuid)433 udisks_module_track_parent (UDisksModule  *module,
434                             const gchar   *path,
435                             gchar        **uuid)
436 {
437   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
438 
439   return UDISKS_MODULE_GET_CLASS (module)->track_parent (module, path, uuid);
440 }
441 
442 /**
443  * udisks_module_get_block_object_interface_types:
444  * @module: A #UDisksModule.
445  *
446  * Gets an array of interface skeleton #GType types the module provides as additional
447  * interfaces for the #UDisksLinuxBlockObject. This list is subsequently used by
448  * #UDisksLinuxBlockObject to track available interfaces and to create new ones via
449  * udisks_module_new_block_object_interface().
450  *
451  * Returns: (element-type GType) (array zero-terminated=1) (nullable) (transfer none):
452  *          A NULL-terminated array of #GType types or %NULL when the module doesn't
453  *          handle block object interfaces. Do not free, the data belongs to the module.
454  *
455  * Since: 2.9.0
456  */
457 GType *
udisks_module_get_block_object_interface_types(UDisksModule * module)458 udisks_module_get_block_object_interface_types (UDisksModule *module)
459 {
460   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
461 
462   return UDISKS_MODULE_GET_CLASS (module)->get_block_object_interface_types (module);
463 }
464 
465 /**
466  * udisks_module_get_drive_object_interface_types:
467  * @module: A #UDisksModule.
468  *
469  * Gets an array of interface skeleton #GType types the module provides as additional
470  * interfaces for the #UDisksLinuxDriveObject. This list is subsequently used by
471  * #UDisksLinuxDriveObject to track available interfaces and to create new ones via
472  * udisks_module_new_drive_object_interface().
473  *
474  * Returns: (element-type GType) (array zero-terminated=1) (nullable) (transfer none):
475  *          A NULL-terminated array of #GType types or %NULL when the module doesn't
476  *          handle drive object interfaces. Do not free, the data belongs to the module.
477  *
478  * Since: 2.9.0
479  */
480 GType *
udisks_module_get_drive_object_interface_types(UDisksModule * module)481 udisks_module_get_drive_object_interface_types (UDisksModule *module)
482 {
483   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
484 
485   return UDISKS_MODULE_GET_CLASS (module)->get_drive_object_interface_types (module);
486 }
487 
488 /**
489  * udisks_module_new_block_object_interface:
490  * @module: A #UDisksModule.
491  * @object: A #UDisksLinuxBlockObject.
492  * @interface_type: A #GType of the desired new interface skeleton.
493  *
494  * Tries to create a new #GDBusInterfaceSkeleton instance of type @interface_type
495  * that is supposed to be attached on the block @object. This method call is also
496  * supposed to check whether the desired @interface_type is applicable for
497  * the current @object and return %NULL if it's not. The returned instance must
498  * implement the #UDisksModuleObject interface with the udisks_module_object_process_uevent()
499  * method that is used to process uevents and controls whether the interface should
500  * be removed or not.
501  *
502  * <note>Note that it is important not to take reference to @object to avoid circular
503  * references. The returned #GDBusInterfaceSkeleton will be exported on the @object
504  * and unexported when no longer valid (typically as a result of a <emphasis>remove</emphasis>
505  * uevent). The returned object is responsible to perform cleanup in its destructor
506  * as it's not generally guaranteed the <emphasis>remove</emphasis> uevent will be
507  * sent prior to that.</note>
508  *
509  * Returns: (transfer full) (nullable): A new #GDBusInterfaceSkeleton instance or
510  *           %NULL when not applicable for the @object. Free with g_object_unref().
511  *
512  * Since: 2.9.0
513  */
514 GDBusInterfaceSkeleton *
udisks_module_new_block_object_interface(UDisksModule * module,UDisksLinuxBlockObject * object,GType interface_type)515 udisks_module_new_block_object_interface (UDisksModule           *module,
516                                           UDisksLinuxBlockObject *object,
517                                           GType                   interface_type)
518 {
519   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
520 
521   return UDISKS_MODULE_GET_CLASS (module)->new_block_object_interface (module, object, interface_type);
522 }
523 
524 /**
525  * udisks_module_new_drive_object_interface:
526  * @module: A #UDisksModule.
527  * @object: A #UDisksLinuxDriveObject.
528  * @interface_type: A #GType of the desired new interface skeleton.
529  *
530  * Tries to create a new #GDBusInterfaceSkeleton instance of type @interface_type
531  * that is supposed to be attached on the drive @object. This method call is also
532  * supposed to check whether the desired @interface_type is applicable for
533  * the current @object and return %NULL if it's not. The returned instance must
534  * implement the #UDisksModuleObject interface with the udisks_module_object_process_uevent()
535  * method that is used to process uevents and controls whether the interface should
536  * be removed or not.
537  *
538  * <note>Note that it is important not to take reference to @object to avoid circular
539  * references. The returned #GDBusInterfaceSkeleton will be exported on the @object
540  * and unexported when no longer valid (typically as a result of a <emphasis>remove</emphasis>
541  * uevent). The returned object is responsible to perform cleanup in its destructor
542  * as it's not generally guaranteed the <emphasis>remove</emphasis> uevent will be
543  * sent prior to that.</note>
544  *
545  * Returns: (transfer full) (nullable): A new #GDBusInterfaceSkeleton instance or
546  *           %NULL when not applicable for the @object. Free with g_object_unref().
547  *
548  * Since: 2.9.0
549  */
550 GDBusInterfaceSkeleton *
udisks_module_new_drive_object_interface(UDisksModule * module,UDisksLinuxDriveObject * object,GType interface_type)551 udisks_module_new_drive_object_interface (UDisksModule           *module,
552                                           UDisksLinuxDriveObject *object,
553                                           GType                   interface_type)
554 {
555   g_return_val_if_fail (UDISKS_IS_MODULE (module), NULL);
556 
557   return UDISKS_MODULE_GET_CLASS (module)->new_drive_object_interface (module, object, interface_type);
558 }
559