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