api,manager: new InhibitDevice() method

This new method allows users of the ModemManager API to take full
control of a given device.

Unlike other operations in the API, the inhibition is maintained as
long as the caller exists in the bus, or until the same caller
uninhibits the device.

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/issues/98
This commit is contained in:
Aleksander Morgado
2018-12-07 16:12:35 +01:00
parent e3766aef5d
commit 2212d3e054
8 changed files with 829 additions and 17 deletions

View File

@@ -56,6 +56,7 @@ static gboolean list_modems_flag;
static gboolean monitor_modems_flag; static gboolean monitor_modems_flag;
static gboolean scan_modems_flag; static gboolean scan_modems_flag;
static gchar *set_logging_str; static gchar *set_logging_str;
static gchar *inhibit_device_str;
static gchar *report_kernel_event_str; static gchar *report_kernel_event_str;
#if defined WITH_UDEV #if defined WITH_UDEV
@@ -83,6 +84,10 @@ static GOptionEntry entries[] = {
"Request to re-scan looking for modems", "Request to re-scan looking for modems",
NULL NULL
}, },
{ "inhibit-device", 'I', 0, G_OPTION_ARG_STRING, &inhibit_device_str,
"Inhibit device given a unique device identifier",
"[UID]"
},
{ "report-kernel-event", 'K', 0, G_OPTION_ARG_STRING, &report_kernel_event_str, { "report-kernel-event", 'K', 0, G_OPTION_ARG_STRING, &report_kernel_event_str,
"Report kernel event", "Report kernel event",
"[\"key=value,...\"]" "[\"key=value,...\"]"
@@ -126,6 +131,7 @@ mmcli_manager_options_enabled (void)
monitor_modems_flag + monitor_modems_flag +
scan_modems_flag + scan_modems_flag +
!!set_logging_str + !!set_logging_str +
!!inhibit_device_str +
!!report_kernel_event_str); !!report_kernel_event_str);
#if defined WITH_UDEV #if defined WITH_UDEV
@@ -145,7 +151,8 @@ mmcli_manager_options_enabled (void)
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
} }
mmcli_force_async_operation (); mmcli_force_async_operation ();
} } else if (inhibit_device_str)
mmcli_force_async_operation ();
#if defined WITH_UDEV #if defined WITH_UDEV
if (report_kernel_event_auto_scan) if (report_kernel_event_auto_scan)
@@ -180,6 +187,41 @@ mmcli_manager_shutdown (void)
context_free (ctx); context_free (ctx);
} }
static void
inhibition_cancelled (GCancellable *cancellable)
{
GError *error = NULL;
if (!mm_manager_uninhibit_device_sync (ctx->manager, inhibit_device_str, NULL, &error)) {
g_printerr ("error: couldn't uninhibit device: '%s'\n",
error ? error->message : "unknown error");
} else
g_print ("successfully uninhibited device with uid '%s'\n", inhibit_device_str);
mmcli_async_operation_done ();
}
static void
inhibit_device_ready (MMManager *manager,
GAsyncResult *result)
{
GError *error = NULL;
if (!mm_manager_inhibit_device_finish (manager, result, &error)) {
g_printerr ("error: couldn't inhibit device: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
g_print ("successfully inhibited device with uid '%s'\n", inhibit_device_str);
g_print ("type Ctrl+C to abort this program and remove the inhibition\n");
g_cancellable_connect (ctx->cancellable,
G_CALLBACK (inhibition_cancelled),
NULL,
NULL);
}
static void static void
report_kernel_event_process_reply (gboolean result, report_kernel_event_process_reply (gboolean result,
const GError *error) const GError *error)
@@ -463,6 +505,16 @@ get_manager_ready (GObject *source,
return; return;
} }
/* Request to inhibit device? */
if (inhibit_device_str) {
mm_manager_inhibit_device (ctx->manager,
inhibit_device_str,
ctx->cancellable,
(GAsyncReadyCallback)inhibit_device_ready,
NULL);
return;
}
g_warn_if_reached (); g_warn_if_reached ();
} }

View File

@@ -18,6 +18,12 @@ mm_manager_get_version
mm_manager_scan_devices mm_manager_scan_devices
mm_manager_scan_devices_finish mm_manager_scan_devices_finish
mm_manager_scan_devices_sync mm_manager_scan_devices_sync
mm_manager_inhibit_device
mm_manager_inhibit_device_finish
mm_manager_inhibit_device_sync
mm_manager_uninhibit_device
mm_manager_uninhibit_device_finish
mm_manager_uninhibit_device_sync
mm_manager_set_logging mm_manager_set_logging
mm_manager_set_logging_finish mm_manager_set_logging_finish
mm_manager_set_logging_sync mm_manager_set_logging_sync
@@ -1731,6 +1737,9 @@ mm_gdbus_org_freedesktop_modem_manager1_get_version
mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices
mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_finish mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_finish
mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_sync mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_sync
mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device
mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_finish
mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_sync
mm_gdbus_org_freedesktop_modem_manager1_call_set_logging mm_gdbus_org_freedesktop_modem_manager1_call_set_logging
mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish
mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_sync mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_sync
@@ -1740,6 +1749,7 @@ mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_sync
<SUBSECTION Private> <SUBSECTION Private>
mm_gdbus_org_freedesktop_modem_manager1_set_version mm_gdbus_org_freedesktop_modem_manager1_set_version
mm_gdbus_org_freedesktop_modem_manager1_override_properties mm_gdbus_org_freedesktop_modem_manager1_override_properties
mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device
mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices
mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging
mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event

View File

@@ -106,6 +106,27 @@
<arg name="properties" type="a{sv}" direction="in" /> <arg name="properties" type="a{sv}" direction="in" />
</method> </method>
<!--
InhibitDevice:
@uid: the unique ID of the physical device, given in the
#org.freedesktop.ModemManager1.Modem:Device property.
@inhibit: %TRUE to inhibit the modem and %FALSE to uninhibit it.
Inhibit or uninhibit the device.
When the modem is inhibited ModemManager will close all its ports and
unexport it from the bus, so that users of the interface are no longer
able to operate with it.
This operation binds the inhibition request to the existence of the
caller in the DBus bus. If the caller disappears from the bus, the
inhibition will automatically removed.
-->
<method name="InhibitDevice">
<arg name="uid" type="s" direction="in" />
<arg name="inhibit" type="b" direction="in" />
</method>
<!-- <!--
Version: Version:

View File

@@ -638,6 +638,242 @@ mm_manager_report_kernel_event_sync (MMManager *manager,
/*****************************************************************************/ /*****************************************************************************/
static gboolean
common_inhibit_device_finish (MMManager *manager,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
inhibit_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_finish (manager_iface_proxy, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
common_inhibit_device (MMManager *manager,
const gchar *uid,
gboolean inhibit,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
GError *inner_error = NULL;
task = g_task_new (manager, cancellable, callback, user_data);
if (!ensure_modem_manager1_proxy (manager, &inner_error)) {
g_task_return_error (task, inner_error);
g_object_unref (task);
return;
}
mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device (
manager->priv->manager_iface_proxy,
uid,
inhibit,
cancellable,
(GAsyncReadyCallback)inhibit_ready,
task);
}
static gboolean
common_inhibit_device_sync (MMManager *manager,
const gchar *uid,
gboolean inhibit,
GCancellable *cancellable,
GError **error)
{
if (!ensure_modem_manager1_proxy (manager, error))
return FALSE;
return (mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_sync (
manager->priv->manager_iface_proxy,
uid,
inhibit,
cancellable,
error));
}
/**
* mm_manager_inhibit_device_finish:
* @manager: A #MMManager.
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_manager_inhibit_device().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_manager_inhibit_device().
*
* Returns: %TRUE if the call succeeded, %FALSE if @error is set.
*/
gboolean
mm_manager_inhibit_device_finish (MMManager *manager,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
return common_inhibit_device_finish (manager, res, error);
}
/**
* mm_manager_inhibit_device:
* @manager: A #MMManager.
* @uid: the unique ID of the physical device.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to add an inhibition on the device identified by @uid.
*
* The @uid must be the unique ID retrieved from an existing #MMModem using
* mm_modem_get_device(). The caller should keep track of this @uid and use it
* in the mm_manager_uninhibit_device() call when the inhibition is no longer required.
*
* The inhibition added with this method may also be automatically removed when
* the caller program disappears from the bus (e.g. if the program ends before
* having called mm_manager_uninhibit_device() explicitly).
*
* When the operation is finished, @callback will be invoked in the
* <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
* of the thread you are calling this method from. You can then call
* mm_manager_inhibit_device_finish() to get the result of the operation.
*
* See mm_manager_inhibit_device_sync() for the synchronous, blocking version of this method.
*/
void
mm_manager_inhibit_device (MMManager *manager,
const gchar *uid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (MM_IS_MANAGER (manager));
common_inhibit_device (manager, uid, TRUE, cancellable, callback, user_data);
}
/**
* mm_manager_inhibit_device_sync:
* @manager: A #MMManager.
* @uid: the unique ID of the physical device.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously requests to add an inhibition on the device identified by @uid.
*
* The @uid must be the unique ID retrieved from an existing #MMModem using
* mm_modem_get_device(). The caller should keep track of this @uid and use it
* in the mm_manager_uninhibit_device_sync() call when the inhibition is no longer required.
*
* The inhibition added with this method may also be automatically removed when
* the caller program disappears from the bus (e.g. if the program ends before
* having called mm_manager_uninhibit_device_sync() explicitly).
*
* See mm_manager_inhibit_device() for the asynchronous version of this method.
*
* Returns: %TRUE if the call succeeded, %FALSE if @error is set.
*/
gboolean
mm_manager_inhibit_device_sync (MMManager *manager,
const gchar *uid,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
return common_inhibit_device_sync (manager, uid, TRUE, cancellable, error);
}
/**
* mm_manager_uninhibit_device_finish:
* @manager: A #MMManager.
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_manager_uninhibit_device().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_manager_uninhibit_device().
*
* Returns: %TRUE if the call succeeded, %FALSE if @error is set.
*/
gboolean
mm_manager_uninhibit_device_finish (MMManager *manager,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
return common_inhibit_device_finish (manager, res, error);
}
/**
* mm_manager_uninhibit_device:
* @manager: A #MMManager.
* @uid: the unique ID of the physical device.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to remove an inhibition on the device identified by @uid.
*
* The @uid must be the same unique ID that was sent in the inhibition request.
*
* Only the same program that placed an inhibition on a given device is able to remove
* the inhibition.
*
* When the operation is finished, @callback will be invoked in the
* <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
* of the thread you are calling this method from. You can then call
* mm_manager_uninhibit_device_finish() to get the result of the operation.
*
* See mm_manager_uninhibit_device_sync() for the synchronous, blocking version of this method.
*/
void
mm_manager_uninhibit_device (MMManager *manager,
const gchar *uid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (MM_IS_MANAGER (manager));
common_inhibit_device (manager, uid, FALSE, cancellable, callback, user_data);
}
/**
* mm_manager_uninhibit_device_sync:
* @manager: A #MMManager.
* @uid: the unique ID of the physical device.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously requests to remove an inhibition on the device identified by @uid.
*
* The @uid must be the same unique ID that was sent in the inhibition request.
*
* Only the same program that placed an inhibition on a given device is able to remove
* the inhibition.
*
* See mm_manager_uninhibit_device() for the asynchronous version of this method.
*
* Returns: %TRUE if the call succeeded, %FALSE if @error is set.
*/
gboolean
mm_manager_uninhibit_device_sync (MMManager *manager,
const gchar *uid,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
return common_inhibit_device_sync (manager, uid, FALSE, cancellable, error);
}
/*****************************************************************************/
static void static void
register_dbus_errors (void) register_dbus_errors (void)
{ {

View File

@@ -128,6 +128,32 @@ gboolean mm_manager_report_kernel_event_sync (MMManager *manage
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
void mm_manager_inhibit_device (MMManager *manager,
const gchar *uid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_manager_inhibit_device_finish (MMManager *manager,
GAsyncResult *res,
GError **error);
gboolean mm_manager_inhibit_device_sync (MMManager *manager,
const gchar *uid,
GCancellable *cancellable,
GError **error);
void mm_manager_uninhibit_device (MMManager *manager,
const gchar *uid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_manager_uninhibit_device_finish (MMManager *manager,
GAsyncResult *res,
GError **error);
gboolean mm_manager_uninhibit_device_sync (MMManager *manager,
const gchar *uid,
GCancellable *cancellable,
GError **error);
G_END_DECLS G_END_DECLS
#endif /* _MM_MANAGER_H_ */ #endif /* _MM_MANAGER_H_ */

View File

@@ -87,6 +87,8 @@ struct _MMBaseManagerPrivate {
GHashTable *devices; GHashTable *devices;
/* The Object Manager server */ /* The Object Manager server */
GDBusObjectManagerServer *object_manager; GDBusObjectManagerServer *object_manager;
/* The map of inhibited devices */
GHashTable *inhibited_devices;
/* The Test interface support */ /* The Test interface support */
MmGdbusTest *test_skeleton; MmGdbusTest *test_skeleton;
@@ -205,6 +207,15 @@ device_support_check_ready (MMPluginManager *plugin_manager,
find_device_support_context_free (ctx); find_device_support_context_free (ctx);
} }
static gboolean is_device_inhibited (MMBaseManager *self,
const gchar *physdev_uid);
static void device_inhibited_track_port (MMBaseManager *self,
const gchar *physdev_uid,
MMKernelDevice *port,
gboolean manual_scan);
static void device_inhibited_untrack_port (MMBaseManager *self,
MMKernelDevice *port);
static void static void
device_removed (MMBaseManager *self, device_removed (MMBaseManager *self,
MMKernelDevice *kernel_device) MMKernelDevice *kernel_device)
@@ -246,8 +257,14 @@ device_removed (MMBaseManager *self,
} }
} }
g_object_unref (device); g_object_unref (device);
return;
} }
/* If the device was inhibited and the port is gone, untrack it.
* This is only needed for ports that were tracked out of device objects.
* In this case we don't rely on the physdev uid, as API-reported
* remove kernel events may not include uid. */
device_inhibited_untrack_port (self, kernel_device);
return; return;
} }
@@ -318,6 +335,20 @@ device_added (MMBaseManager *manager,
return; return;
} }
/* Get the port's physical device's uid. All ports of the same physical
* device will share the same uid. */
physdev_uid = mm_kernel_device_get_physdev_uid (port);
g_assert (physdev_uid);
/* If the device is inhibited, do nothing else */
if (is_device_inhibited (manager, physdev_uid)) {
/* Note: we will not report as hotplugged an inhibited device port
* because we don't know what was done with the port out of our
* context. */
device_inhibited_track_port (manager, physdev_uid, port, manual_scan);
return;
}
/* Run port filter */ /* Run port filter */
if (!mm_filter_port (manager->priv->filter, port, manual_scan)) if (!mm_filter_port (manager->priv->filter, port, manual_scan))
return; return;
@@ -328,11 +359,6 @@ device_added (MMBaseManager *manager,
return; return;
} }
/* Get the port's physical device's uid. All ports of the same physical
* device will share the same uid. */
physdev_uid = mm_kernel_device_get_physdev_uid (port);
g_assert (physdev_uid);
/* See if we already created an object to handle ports in this device */ /* See if we already created an object to handle ports in this device */
device = find_device_by_physdev_uid (manager, physdev_uid); device = find_device_by_physdev_uid (manager, physdev_uid);
if (!device) { if (!device) {
@@ -898,6 +924,333 @@ handle_report_kernel_event (MmGdbusOrgFreedesktopModemManager1 *manager,
return TRUE; return TRUE;
} }
/*****************************************************************************/
/* Inhibit or uninhibit device */
typedef struct {
MMKernelDevice *kernel_port;
gboolean manual_scan;
} InhibitedDevicePortInfo;
static void
inhibited_device_port_info_free (InhibitedDevicePortInfo *port_info)
{
g_object_unref (port_info->kernel_port);
g_slice_free (InhibitedDevicePortInfo, port_info);
}
typedef struct {
gchar *sender;
guint name_lost_id;
GList *port_infos;
} InhibitedDeviceInfo;
static void
inhibited_device_info_free (InhibitedDeviceInfo *info)
{
g_list_free_full (info->port_infos, (GDestroyNotify)inhibited_device_port_info_free);
g_bus_unwatch_name (info->name_lost_id);
g_free (info->sender);
g_slice_free (InhibitedDeviceInfo, info);
}
static InhibitedDeviceInfo *
find_inhibited_device_info_by_physdev_uid (MMBaseManager *self,
const gchar *physdev_uid)
{
return (physdev_uid ? g_hash_table_lookup (self->priv->inhibited_devices, physdev_uid) : NULL);
}
static gboolean
is_device_inhibited (MMBaseManager *self,
const gchar *physdev_uid)
{
return !!find_inhibited_device_info_by_physdev_uid (self, physdev_uid);
}
static void
device_inhibited_untrack_port (MMBaseManager *self,
MMKernelDevice *kernel_port)
{
GHashTableIter iter;
gchar *uid;
InhibitedDeviceInfo *info;
g_hash_table_iter_init (&iter, self->priv->inhibited_devices);
while (g_hash_table_iter_next (&iter, (gpointer)&uid, (gpointer)&info)) {
GList *l;
for (l = info->port_infos; l; l = g_list_next (l)) {
InhibitedDevicePortInfo *port_info;
port_info = (InhibitedDevicePortInfo *)(l->data);
if (mm_kernel_device_cmp (port_info->kernel_port, kernel_port)) {
mm_dbg ("(%s/%s): released while inhibited",
mm_kernel_device_get_subsystem (kernel_port),
mm_kernel_device_get_name (kernel_port));
inhibited_device_port_info_free (port_info);
info->port_infos = g_list_delete_link (info->port_infos, l);
return;
}
}
}
}
static void
device_inhibited_track_port (MMBaseManager *self,
const gchar *physdev_uid,
MMKernelDevice *kernel_port,
gboolean manual_scan)
{
InhibitedDevicePortInfo *port_info;
InhibitedDeviceInfo *info;
GList *l;
info = find_inhibited_device_info_by_physdev_uid (self, physdev_uid);
g_assert (info);
for (l = info->port_infos; l; l = g_list_next (l)) {
/* If device is already tracked, just overwrite the manual scan info */
port_info = (InhibitedDevicePortInfo *)(l->data);
if (mm_kernel_device_cmp (port_info->kernel_port, kernel_port)) {
port_info->manual_scan = manual_scan;
return;
}
}
mm_dbg ("(%s/%s): added while inhibited",
mm_kernel_device_get_subsystem (kernel_port),
mm_kernel_device_get_name (kernel_port));
port_info = g_slice_new0 (InhibitedDevicePortInfo);
port_info->kernel_port = g_object_ref (kernel_port);
port_info->manual_scan = manual_scan;
info->port_infos = g_list_append (info->port_infos, port_info);
}
typedef struct {
MMBaseManager *self;
gchar *uid;
} InhibitSenderLostContext;
static void
inhibit_sender_lost_context_free (InhibitSenderLostContext *lost_ctx)
{
g_free (lost_ctx->uid);
g_slice_free (InhibitSenderLostContext, lost_ctx);
}
static void
remove_device_inhibition (MMBaseManager *self,
const gchar *uid)
{
InhibitedDeviceInfo *info;
MMDevice *device;
GList *port_infos;
info = find_inhibited_device_info_by_physdev_uid (self, uid);
g_assert (info);
device = find_device_by_physdev_uid (self, uid);
port_infos = info->port_infos;
info->port_infos = NULL;
g_hash_table_remove (self->priv->inhibited_devices, uid);
if (port_infos) {
GList *l;
/* Note that a device can only be inhibited if it had an existing
* modem exposed in the bus. And so, inhibition can only be placed
* AFTER all port probes have finished for a given device. This means
* that we either have a device tracked, or we have a list of port
* infos. Both at the same time should never happen. */
g_assert (!device);
/* Report as added all port infos that we had tracked while the
* device was inhibited. We can only report the added port after
* having removed the entry from the inhibited devices tracking
* table. */
for (l = port_infos; l; l = g_list_next (l)) {
InhibitedDevicePortInfo *port_info;
port_info = (InhibitedDevicePortInfo *)(l->data);
device_added (self, port_info->kernel_port, FALSE, port_info->manual_scan);
}
g_list_free_full (port_infos, (GDestroyNotify)inhibited_device_port_info_free);
}
/* The device may be totally gone from the system while we were
* keeping the inhibition, so do not error out if not found. */
else if (device) {
GError *error = NULL;
/* Uninhibit device, which will create and expose the modem object */
if (!mm_device_uninhibit (device, self->priv->object_manager, &error)) {
mm_warn ("Couldn't uninhibit device: %s", error->message);
g_error_free (error);
}
}
}
static void
inhibit_sender_lost (GDBusConnection *connection,
const gchar *sender_name,
InhibitSenderLostContext *lost_ctx)
{
mm_info ("Device inhibition teardown for uid '%s' (owner disappeared from bus)", lost_ctx->uid);
remove_device_inhibition (lost_ctx->self, lost_ctx->uid);
}
typedef struct {
MMBaseManager *self;
GDBusMethodInvocation *invocation;
gchar *uid;
gboolean inhibit;
} InhibitDeviceContext;
static void
inhibit_device_context_free (InhibitDeviceContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
g_free (ctx->uid);
g_slice_free (InhibitDeviceContext, ctx);
}
static void
device_inhibit_ready (MMDevice *device,
GAsyncResult *res,
InhibitDeviceContext *ctx)
{
InhibitSenderLostContext *lost_ctx;
InhibitedDeviceInfo *info;
GError *error = NULL;
if (!mm_device_inhibit_finish (device, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
inhibit_device_context_free (ctx);
return;
}
info = g_slice_new0 (InhibitedDeviceInfo);
info->sender = g_strdup (g_dbus_method_invocation_get_sender (ctx->invocation));
/* This context will exist as long as the sender name watcher exists,
* i.e. as long as the associated InhibitDeviceInfo exists. We don't need
* an extra reference of self here because these contexts are stored within
* self, and therefore bound to its lifetime. */
lost_ctx = g_slice_new0 (InhibitSenderLostContext);
lost_ctx->self = ctx->self;
lost_ctx->uid = g_strdup (ctx->uid);
info->name_lost_id = g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection (ctx->invocation),
info->sender,
G_BUS_NAME_WATCHER_FLAGS_NONE,
NULL,
(GBusNameVanishedCallback)inhibit_sender_lost,
lost_ctx,
(GDestroyNotify)inhibit_sender_lost_context_free);
g_hash_table_insert (ctx->self->priv->inhibited_devices, g_strdup (ctx->uid), info);
mm_info ("Device inhibition setup for uid '%s'", ctx->uid);
mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device (
MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
ctx->invocation);
inhibit_device_context_free (ctx);
}
static void
base_manager_inhibit_device (InhibitDeviceContext *ctx)
{
MMDevice *device;
device = find_device_by_physdev_uid (ctx->self, ctx->uid);
if (!device) {
g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
"No device found with uid '%s'", ctx->uid);
inhibit_device_context_free (ctx);
return;
}
if (mm_device_get_inhibited (device)) {
g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
"Device '%s' is already inhibited", ctx->uid);
inhibit_device_context_free (ctx);
return;
}
mm_device_inhibit (device,
(GAsyncReadyCallback) device_inhibit_ready,
ctx);
}
static void
base_manager_uninhibit_device (InhibitDeviceContext *ctx)
{
InhibitedDeviceInfo *info;
const gchar *sender;
/* Validate uninhibit request */
sender = g_dbus_method_invocation_get_sender (ctx->invocation);
info = find_inhibited_device_info_by_physdev_uid (ctx->self, ctx->uid);
if (!info || (g_strcmp0 (info->sender, sender) != 0)) {
g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
"No inhibition found for uid '%s'", ctx->uid);
inhibit_device_context_free (ctx);
return;
}
mm_info ("Device inhibition teardown for uid '%s'", ctx->uid);
remove_device_inhibition (ctx->self, ctx->uid);
mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device (
MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
ctx->invocation);
inhibit_device_context_free (ctx);
}
static void
inhibit_device_auth_ready (MMAuthProvider *authp,
GAsyncResult *res,
InhibitDeviceContext *ctx)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
inhibit_device_context_free (ctx);
return;
}
if (ctx->inhibit)
base_manager_inhibit_device (ctx);
else
base_manager_uninhibit_device (ctx);
}
static gboolean
handle_inhibit_device (MmGdbusOrgFreedesktopModemManager1 *manager,
GDBusMethodInvocation *invocation,
const gchar *uid,
gboolean inhibit)
{
InhibitDeviceContext *ctx;
ctx = g_new0 (InhibitDeviceContext, 1);
ctx->self = g_object_ref (manager);
ctx->invocation = g_object_ref (invocation);
ctx->uid = g_strdup (uid);
ctx->inhibit = inhibit;
mm_auth_provider_authorize (ctx->self->priv->authp,
invocation,
MM_AUTHORIZATION_MANAGER_CONTROL,
ctx->self->priv->authp_cancellable,
(GAsyncReadyCallback)inhibit_device_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/ /*****************************************************************************/
/* Test profile setup */ /* Test profile setup */
@@ -1092,6 +1445,9 @@ mm_base_manager_init (MMBaseManager *manager)
/* Setup internal lists of device objects */ /* Setup internal lists of device objects */
priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
/* Setup internal list of inhibited devices */
priv->inhibited_devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)inhibited_device_info_free);
#if defined WITH_UDEV #if defined WITH_UDEV
{ {
const gchar *subsys[5] = { "tty", "net", "usb", "usbmisc", NULL }; const gchar *subsys[5] = { "tty", "net", "usb", "usbmisc", NULL };
@@ -1111,17 +1467,11 @@ mm_base_manager_init (MMBaseManager *manager)
priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH); priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH);
/* Enable processing of input DBus messages */ /* Enable processing of input DBus messages */
g_signal_connect (manager, g_object_connect (manager,
"handle-set-logging", "signal::handle-set-logging", G_CALLBACK (handle_set_logging), NULL,
G_CALLBACK (handle_set_logging), "signal::handle-scan-devices", G_CALLBACK (handle_scan_devices), NULL,
NULL); "signal::handle-report-kernel-event", G_CALLBACK (handle_report_kernel_event), NULL,
g_signal_connect (manager, "signal::handle-inhibit-device", G_CALLBACK (handle_inhibit_device), NULL,
"handle-scan-devices",
G_CALLBACK (handle_scan_devices),
NULL);
g_signal_connect (manager,
"handle-report-kernel-event",
G_CALLBACK (handle_report_kernel_event),
NULL); NULL);
} }
@@ -1185,6 +1535,7 @@ finalize (GObject *object)
g_free (priv->initial_kernel_events); g_free (priv->initial_kernel_events);
g_free (priv->plugin_dir); g_free (priv->plugin_dir);
g_hash_table_destroy (priv->inhibited_devices);
g_hash_table_destroy (priv->devices); g_hash_table_destroy (priv->devices);
#if defined WITH_UDEV #if defined WITH_UDEV

View File

@@ -36,6 +36,7 @@ enum {
PROP_MODEM, PROP_MODEM,
PROP_HOTPLUGGED, PROP_HOTPLUGGED,
PROP_VIRTUAL, PROP_VIRTUAL,
PROP_INHIBITED,
PROP_LAST PROP_LAST
}; };
@@ -79,6 +80,9 @@ struct _MMDevicePrivate {
/* Whether the device was hot-plugged. */ /* Whether the device was hot-plugged. */
gboolean hotplugged; gboolean hotplugged;
/* Whether the device is inhibited. */
gboolean inhibited;
/* Virtual ports */ /* Virtual ports */
gchar **virtual_ports; gchar **virtual_ports;
}; };
@@ -370,6 +374,12 @@ mm_device_create_modem (MMDevice *self,
g_assert (self->priv->modem == NULL); g_assert (self->priv->modem == NULL);
g_assert (self->priv->object_manager == NULL); g_assert (self->priv->object_manager == NULL);
if (self->priv->inhibited) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Device is inhibited");
return FALSE;
}
if (!self->priv->virtual) { if (!self->priv->virtual) {
if (!self->priv->port_probes) { if (!self->priv->port_probes) {
g_set_error (error, g_set_error (error,
@@ -520,6 +530,85 @@ mm_device_get_hotplugged (MMDevice *self)
return self->priv->hotplugged; return self->priv->hotplugged;
} }
gboolean
mm_device_get_inhibited (MMDevice *self)
{
return self->priv->inhibited;
}
/*****************************************************************************/
gboolean
mm_device_inhibit_finish (MMDevice *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
inhibit_disable_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMDevice *self;
GError *error = NULL;
self = g_task_get_source_object (task);
if (!mm_base_modem_disable_finish (modem, res, &error))
g_task_return_error (task, error);
else {
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
mm_device_remove_modem (self);
g_task_return_boolean (task, TRUE);
}
g_object_unref (task);
}
void
mm_device_inhibit (MMDevice *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
/* We want to allow inhibiting only devices that are currently
* exported in the bus, because otherwise we may be inhibiting
* in the middle of port probing and that may lead to some ports
* tracked inside the device object during inhibition and some
* other ports tracked in the base manager. So, if the device
* does not have a valid modem created and exposed, do not allow
* the inhibition. */
if (!self->priv->modem || !g_dbus_object_get_object_path (G_DBUS_OBJECT (self->priv->modem))) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
"Modem not exported in the bus");
g_object_unref (task);
return;
}
/* Flag as inhibited right away */
g_assert (!self->priv->inhibited);
self->priv->inhibited = TRUE;
/* Make sure modem is disabled while inhibited */
mm_base_modem_disable (self->priv->modem,
(GAsyncReadyCallback)inhibit_disable_ready,
task);
}
gboolean
mm_device_uninhibit (MMDevice *self,
GDBusObjectManagerServer *object_manager,
GError **error)
{
g_assert (self->priv->inhibited);
self->priv->inhibited = FALSE;
return mm_device_create_modem (self, object_manager, error);
}
/*****************************************************************************/ /*****************************************************************************/
void void
@@ -602,6 +691,9 @@ set_property (GObject *object,
case PROP_VIRTUAL: case PROP_VIRTUAL:
self->priv->virtual = g_value_get_boolean (value); self->priv->virtual = g_value_get_boolean (value);
break; break;
case PROP_INHIBITED:
self->priv->inhibited = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -632,6 +724,9 @@ get_property (GObject *object,
case PROP_VIRTUAL: case PROP_VIRTUAL:
g_value_set_boolean (value, self->priv->virtual); g_value_set_boolean (value, self->priv->virtual);
break; break;
case PROP_INHIBITED:
g_value_set_boolean (value, self->priv->inhibited);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -719,6 +814,14 @@ mm_device_class_init (MMDeviceClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_VIRTUAL, properties[PROP_VIRTUAL]); g_object_class_install_property (object_class, PROP_VIRTUAL, properties[PROP_VIRTUAL]);
properties[PROP_INHIBITED] =
g_param_spec_boolean (MM_DEVICE_INHIBITED,
"Inhibited",
"Whether the modem is inhibited",
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_INHIBITED, properties[PROP_INHIBITED]);
signals[SIGNAL_PORT_GRABBED] = signals[SIGNAL_PORT_GRABBED] =
g_signal_new (MM_DEVICE_PORT_GRABBED, g_signal_new (MM_DEVICE_PORT_GRABBED,
G_OBJECT_CLASS_TYPE (object_class), G_OBJECT_CLASS_TYPE (object_class),

View File

@@ -38,6 +38,7 @@ typedef struct _MMDevicePrivate MMDevicePrivate;
#define MM_DEVICE_MODEM "modem" #define MM_DEVICE_MODEM "modem"
#define MM_DEVICE_HOTPLUGGED "hotplugged" #define MM_DEVICE_HOTPLUGGED "hotplugged"
#define MM_DEVICE_VIRTUAL "virtual" #define MM_DEVICE_VIRTUAL "virtual"
#define MM_DEVICE_INHIBITED "inhibited"
#define MM_DEVICE_PORT_GRABBED "port-grabbed" #define MM_DEVICE_PORT_GRABBED "port-grabbed"
#define MM_DEVICE_PORT_RELEASED "port-released" #define MM_DEVICE_PORT_RELEASED "port-released"
@@ -77,6 +78,17 @@ gboolean mm_device_create_modem (MMDevice *self,
GError **error); GError **error);
void mm_device_remove_modem (MMDevice *self); void mm_device_remove_modem (MMDevice *self);
void mm_device_inhibit (MMDevice *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_device_inhibit_finish (MMDevice *self,
GAsyncResult *res,
GError **error);
gboolean mm_device_uninhibit (MMDevice *self,
GDBusObjectManagerServer *object_manager,
GError **error);
const gchar *mm_device_get_uid (MMDevice *self); const gchar *mm_device_get_uid (MMDevice *self);
const gchar **mm_device_get_drivers (MMDevice *self); const gchar **mm_device_get_drivers (MMDevice *self);
guint16 mm_device_get_vendor (MMDevice *self); guint16 mm_device_get_vendor (MMDevice *self);
@@ -94,6 +106,7 @@ GObject *mm_device_get_port_probe (MMDevice *self,
GList *mm_device_peek_port_probe_list (MMDevice *self); GList *mm_device_peek_port_probe_list (MMDevice *self);
GList *mm_device_get_port_probe_list (MMDevice *self); GList *mm_device_get_port_probe_list (MMDevice *self);
gboolean mm_device_get_hotplugged (MMDevice *self); gboolean mm_device_get_hotplugged (MMDevice *self);
gboolean mm_device_get_inhibited (MMDevice *self);
/* For testing purposes */ /* For testing purposes */
void mm_device_virtual_grab_ports (MMDevice *self, void mm_device_virtual_grab_ports (MMDevice *self,