Top |
UDisks functionality can be extended by modules. It's not a fully pluggable system as we know it, in this case modules are almost integral parts of the source tree. Meaning that modules are free to use whatever internal objects they need as there is no universal module API (or a translation layer).
This fact allows us to stay code-wise simple and transparent. It also means that there's no support for out-of-the-tree modules and care must be taken when changing UDisks internals. As a design decision, for sake of simplicity, once modules are loaded they stay active until the daemon exits (this may be a subject to change in the future).
The primary motivation for this was to keep the daemon low on resource footprint for basic usage (typically desktop environments) and only activating the extended functionality when needed (e.g. enterprise storage applications). As the extra information comes in form of additional D-Bus objects and interfaces, no difference should be observed by legacy clients.
The modular approach is fairly simple, there are basically two primary ways of extending the D-Bus API:
by attaching custom interfaces to existing objects (limited to block and drive objects for the moment)
by exporting objects of its own type directly in the object manager root
Besides that there are several other ways of extensibility such as attaching custom interfaces on the master /org/freedesktop/UDisks2/Manager object.
The UDisks daemon constructs a UDisksModuleManager singleton acting as a manager. This object tracks module usage and takes care of its activation.
By default, module manager is constructed on daemon startup but module loading is delayed until requested. This can be overriden by the --force-load-modules and --disable-modules commandline switches that makes modules loaded right on startup or never loaded respectively.
Upon successful activation, the "modules-ready" property on the UDisksModuleManager
instance is set to TRUE
. Any daemon objects watching this property are
responsible for performing "coldplug" on their exported objects to assure
modules would pick up the devices they're interested in. See e.g.
UDisksModuleObjectNewFunc()
to see how device binding works for
UDisksModuleObject.
Modules are in fact separate shared objects (.so) that are loaded from the "$(libdir)/udisks2/modules" path (usually "/usr/lib/udisks2/modules"). No extra or service files are needed, the directory is enumerated and all files are attempted to be loaded.
Clients are supposed to call the org.freedesktop.UDisks2.Manager.EnableModules() D-Bus method as a "greeter" call. Please note that from asynchronous nature of uevents and the way modules are processing them the extra D-Bus interfaces may not be available right after this method call returns.
The (strictly internal) module API is simple - only a couple of functions are needed. The following text contains brief description of individual parts of the module API with further links to detailed description within this API reference book.
The UDisksModuleManager first loads all module entry functions, i.e. symbols defined in the public facing header "udisksmoduleiface.h". Only those symbols should be exported from each module. The header file is only meant to be compiled in modules, not the daemon. If any of the symbols is missing in the module library, the whole module is skipped.
Once module symbols are resolved, module manager activates each module by
calling udisks_module_init()
on it. The returned so-called "state" pointer
is stored in the UDisksModuleManager and can be later retrieved by calling
the udisks_module_manager_get_module_state_pointer()
method. This is typically
used further in the module code to retrieve and store module-specific runtime
data.
Every one of the "udisksmoduleiface.h" header file symbols has its counterpart defined in the "udisksmoduleifacetypes.h" header file in form of function pointers. Those are used internally for symbol resolving purposes. However, they also carry detailed documentation. For illustration purposes, let's call these symbol pairs the "module setup entry functions". See UDisksModuleIfaceSetupFunc, UDisksModuleObjectNewSetupFunc and UDisksModuleNewManagerIfaceSetupFunc for reference. These however are essentially auxiliary symbols only described for demonstrating the big picture; for the useful part of the module API please read on.
Every module setup entry function (besides the very simple udisks_module_init()
)
returns an array of setup structures or functions, containing either none
(NULL result), one or more elements. The result is then mixed by
UDisksModuleManager from all modules and separate lists are created for
each kind of UDisks way of extension. Such lists are then used in the daemon
code at appropriate places, sequentially calling elements from the lists to
obtain data or objects that are then typically exported on D-Bus.
In short, have a look at the UDisksModuleInterfaceInfo, UDisksModuleObjectNewFunc and UDisksModuleNewManagerIfaceFunc definitions to learn more about particular ways of extending UDisks.
gboolean
(*UDisksObjectHasInterfaceFunc) (UDisksObject *object
);
Function prototype that is used to determine whether the object
is applicable
for carrying a particular D-Bus interface (determined by the callback function itself).
Used typically over UDisksLinuxBlockObject and UDisksLinuxDriveObject objects for checking specific feature that leads to exporting extra D-Bus interface on the object.
void
(*UDisksObjectConnectInterfaceFunc) (UDisksObject *object
);
Function prototype that is used once a new D-Bus interface is created (meaning
the UDisksObjectHasInterfaceFunc call was successful) to perform optional
additional tasks before the interface is exported on the object
.
Used typically over UDisksLinuxBlockObject and UDisksLinuxDriveObject objects.
gboolean (*UDisksObjectUpdateInterfaceFunc) (UDisksObject *object
,const gchar *uevent_action
,GDBusInterface *interface
);
Function prototype that is used on existing interface
on the object
to process
incoming uevents.
Used typically over UDisksLinuxBlockObject and UDisksLinuxDriveObject objects.
object |
A UDisksObject. |
|
uevent_action |
An uevent action string. |
|
interface |
Existing GDBusInterface exported on the |
GDBusObjectSkeleton * (*UDisksModuleObjectNewFunc) (UDisksDaemon *daemon
,UDisksLinuxDevice *device
);
Function prototype that creates new GDBusObjectSkeleton instance that implements the UDisksModuleObject interface.
This is an another way of extending UDisks functionality. Objects in this scope are meant to be of virtual kind and are pretty flexible - not necessarily bound to any block device or perhaps representing a group of resources. For illustration this kind of object may represent e.g. a RAID array comprised of several block devices, all devices of the same kind such as loop devices or any higher level representation of something else.
This function may be called quite often, for nearly any uevent received. It's done this way for broad flexibility and to give a chance to UDisksModuleObjectNewFunc functions to claim any device needed.
Every GDBusObjectSkeleton can claim one or more devices and UDisks automatically manages uevent routing and instance lifecycle. A hierarchy of claimed devices is maintained for the combination of particular module and every UDisksModuleObjectNewFunc - see below. This list lives in UDisksLinuxProvider and is strictly internal. Every module can provide multiple UDisksModuleObjectNewFunc functions for different kind of objects.
Two scenarios are distinguished:
The device
is already claimed by existing GDBusObjectSkeleton
instance for the current UDisksModuleObjectNewFunc, it is guaranteed
that only that instance will receive further uevents for the particular
device
. This is done by calling the udisks_module_object_process_uevent()
method of the UDisksModuleObject interface. Depending on the returning
value the device is either kept claimed by the object or removed. When
last claimed device has been removed from the instance, it is
automatically destroyed. In either case no further processing is done
at this cycle to prevent creating new bogus instances for a device
that has just given up.
In case the device
is not claimed by any existing GDBusObjectSkeleton
instance for the current UDisksModuleObjectNewFunc. It now all depends
on the return value of the function called. If it returns a new
GDBusObjectSkeleton instance, it also indicates to UDisksLinuxProvider
that it claims the device. The UDisksLinuxProvider then registers the
value with the current function and keeps track of it. If it returns
NULL
, it inidcates that the current function is not interested in this
kind of device.
It's guaranteed that existing GDBusObjectSkeleton instances will receive uevents for devices they took and creating a new instance will take place only if the event was not processed by any of them.
GDBusInterfaceSkeleton *
(*UDisksModuleNewManagerIfaceFunc) (UDisksDaemon *daemon
);
Function prototype that creates new GDBusInterfaceSkeleton instance carrying an additional D-Bus interface to be exported on the UDisks manager object (on the "/org/freedesktop/UDisks2/Manager" path). It is a fairly simple stateless object not related to any device and serves the purpose of performing general tasks or creating new resources.
gpointer
(*UDisksModuleInitFunc) (UDisksDaemon *daemon
);
Function prototype that is called upon module initialization. Its purpose is
to perform internal initialization and allocate memory that is then used e.g.
for saving state. See the udisks_module_manager_get_module_state_pointer()
method for how to work with state pointers.
Corresponds with the udisks_module_init()
module symbol.
Used internally by UDisksModuleManager.
UDisksModuleInterfaceInfo **
(*UDisksModuleIfaceSetupFunc) (void
);
Type declaration of a module setup entry function.
Corresponds with the udisks_module_get_block_object_iface_setup_entries()
and
udisks_module_get_drive_object_iface_setup_entries()
module symbols.
Used internally by UDisksModuleManager.
UDisksModuleObjectNewFunc *
(*UDisksModuleObjectNewSetupFunc) (void
);
Type declaration of a module setup entry function.
Corresponds with the udisks_module_get_object_new_funcs()
module symbol.
Used internally by UDisksModuleManager.
UDisksModuleNewManagerIfaceFunc *
(*UDisksModuleNewManagerIfaceSetupFunc)
(void
);
Type declaration of a module setup entry function.
Corresponds with the udisks_module_get_new_manager_iface_funcs()
module symbol.
Used internally by UDisksModuleManager.
UDisksModuleManager *
udisks_module_manager_new (UDisksDaemon *daemon
);
Creates a new UDisksModuleManager object.
gboolean
udisks_module_manager_get_modules_available
(UDisksModuleManager *manager
);
Indicates whether modules have been loaded.
void
udisks_module_manager_load_modules (UDisksModuleManager *manager
);
Loads all modules at a time and emits the "modules-ready" signal. Does nothing when called multiple times.
gpointer udisks_module_manager_get_module_state_pointer (UDisksModuleManager *manager
,const gchar *module_name
);
Retrieves the stored module state pointer for the given module_name
.
void udisks_module_manager_set_module_state_pointer (UDisksModuleManager *manager
,const gchar *module_name
,gpointer state
);
Stores the state
pointer for the given module_name
.
manager |
A UDisksModuleManager instance. |
|
module_name |
A module name. |
|
state |
Pointer to a private data. |
GList *
udisks_module_manager_get_block_object_iface_infos
(UDisksModuleManager *manager
);
Returns a list of block object interface info structs that can be plugged in UDisksLinuxBlockObject instances. See UDisksModuleIfaceSetupFunc for details.
A list of UDisksModuleIfaceSetupFunc structs that belongs to the manager and must not be freed.
[element-type UDisksModuleIfaceSetupFunc][transfer full]
GList *
udisks_module_manager_get_drive_object_iface_infos
(UDisksModuleManager *manager
);
Returns a list of drive object interface info structs that can be plugged in UDisksLinuxDriveObject instances. See UDisksModuleIfaceSetupFunc for details.
A list of UDisksModuleIfaceSetupFunc structs that belongs to the manager and must not be freed.
[element-type UDisksModuleIfaceSetupFunc][transfer full]
GList *
udisks_module_manager_get_module_object_new_funcs
(UDisksModuleManager *manager
);
Returns a list of all module object new functions. See UDisksModuleObjectNewFunc for details.
A list of UDisksModuleObjectNewFunc function pointers that belongs to the manager and must not be freed.
[element-type UDisksModuleObjectNewFunc][transfer full]
GList *
udisks_module_manager_get_new_manager_iface_funcs
(UDisksModuleManager *manager
);
Returns a list of all module new manager interface functions. See UDisksModuleNewManagerIfaceFunc for details.
A list of UDisksModuleNewManagerIfaceFunc function pointers that belongs to the manager and must not be freed.
[element-type UDisksModuleNewManagerIfaceFunc][transfer full]
gboolean udisks_module_object_process_uevent (UDisksModuleObject *object
,const gchar *action
,UDisksLinuxDevice *device
);
Virtual method that processes the uevent and updates all information on interfaces
on object
.
See UDisksModuleObjectNewFunc for detailed information on how to work with the events.
object |
||
action |
Uevent action, common values are "add", "changed" and "removed" or |
|
device |
A UDisksLinuxDevice device object or |
gboolean udisks_module_object_housekeeping (UDisksModuleObject *object
,guint secs_since_last
,GCancellable *cancellable
,GError **error
);
Virtual method that is called periodically (every ten minutes or so) to perform housekeeping tasks such as refreshing ATA SMART data.
The function runs in a dedicated thread and is allowed to perform blocking I/O.
Long-running tasks should periodically check cancellable
to see if
they have been cancelled.
typedef struct _UDisksModuleManager UDisksModuleManager;
The UDisksModuleManager structure contains only private data and should only be accessed using the provided API.
typedef struct { UDisksObjectHasInterfaceFunc has_func; UDisksObjectConnectInterfaceFunc connect_func; UDisksObjectUpdateInterfaceFunc update_func; GType skeleton_type; } UDisksModuleInterfaceInfo;
Structure containing interface setup functions used by modules for exporting custom interfaces on existing block and drive objects.
Event processing is solely done by UDisksLinuxBlockObject and UDisksLinuxDriveObject
themselves whose call the has_func
, connect_func
and update_func
respectively
as needed. Purpose of these member functions is to check whether the particular
UDisksModuleInterfaceInfo record is applicable to the current device and
construct a new GDBusInterface if so.
See UDisksObjectHasInterfaceFunc, UDisksObjectConnectInterfaceFunc and UDisksObjectUpdateInterfaceFunc for detailed description and return values.
UDisksObjectHasInterfaceFunc |
||
UDisksObjectConnectInterfaceFunc |
||
UDisksObjectUpdateInterfaceFunc |
||
A GType of the instance that is created once |
typedef struct _UDisksModuleObject UDisksModuleObject;
The UDisksModuleObject structure contains only private data and should only be accessed using the provided API.
struct UDisksModuleObjectIface { GTypeInterface parent_iface; gboolean (*process_uevent) (UDisksModuleObject *object, const gchar *action, UDisksLinuxDevice *device); gboolean (*housekeeping) (UDisksModuleObject *object, guint secs_since_last, GCancellable *cancellable, GError **error); };