summaryrefslogtreecommitdiff
path: root/src/mm-base-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-base-manager.c')
-rw-r--r--src/mm-base-manager.c1486
1 files changed, 1078 insertions, 408 deletions
diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
index 3900af5b..46f1ad43 100644
--- a/src/mm-base-manager.c
+++ b/src/mm-base-manager.c
@@ -12,40 +12,63 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
- * Copyright (C) 2011 - 2012 Aleksander Morgado <aleksander@gnu.org>
- * Copyright (C) 2011 - 2012 Google, Inc.
+ * Copyright (C) 2011 - 2012 Google, Inc
+ * Copyright (C) 2016 Velocloud, Inc.
+ * Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es>
*/
+#include <config.h>
+
#include <string.h>
#include <ctype.h>
#include <gmodule.h>
-#include <gudev/gudev.h>
+
+#if defined WITH_QMI
+# include <libqmi-glib.h>
+#endif
+#if defined WITH_QRTR
+# include "mm-kernel-device-qrtr.h"
+# include "mm-qrtr-bus-watcher.h"
+#endif
+#if defined WITH_UDEV
+# include "mm-kernel-device-udev.h"
+#endif
+#include "mm-kernel-device-generic.h"
#include <ModemManager.h>
+#include <ModemManager-tags.h>
+
#include <mm-errors-types.h>
#include <mm-gdbus-manager.h>
#include <mm-gdbus-test.h>
+#include "mm-context.h"
#include "mm-base-manager.h"
+#include "mm-daemon-enums-types.h"
#include "mm-device.h"
#include "mm-plugin-manager.h"
-#include "mm-auth.h"
+#include "mm-auth-provider.h"
#include "mm-plugin.h"
-#include "mm-log.h"
+#include "mm-filter.h"
+#include "mm-log-object.h"
+#include "mm-base-modem.h"
-static void initable_iface_init (GInitableIface *iface);
+static void initable_iface_init (GInitableIface *iface);
+static void log_object_iface_init (MMLogObjectInterface *iface);
G_DEFINE_TYPE_EXTENDED (MMBaseManager, mm_base_manager, MM_GDBUS_TYPE_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON, 0,
- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
- initable_iface_init));
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
PROP_CONNECTION,
PROP_AUTO_SCAN,
+ PROP_FILTER_POLICY,
PROP_ENABLE_TEST,
PROP_PLUGIN_DIR,
+ PROP_INITIAL_KERNEL_EVENTS,
LAST_PROP
};
@@ -54,24 +77,39 @@ struct _MMBaseManagerPrivate {
GDBusConnection *connection;
/* Whether auto-scanning is enabled */
gboolean auto_scan;
+ /* Filter policy (mask of enabled rules) */
+ MMFilterRule filter_policy;
/* Whether the test interface is enabled */
gboolean enable_test;
/* Path to look for plugins */
gchar *plugin_dir;
- /* The UDev client */
- GUdevClient *udev;
+ /* Path to the list of initial kernel events */
+ gchar *initial_kernel_events;
/* The authorization provider */
MMAuthProvider *authp;
GCancellable *authp_cancellable;
/* The Plugin Manager object */
MMPluginManager *plugin_manager;
+ /* The port/device filter */
+ MMFilter *filter;
/* The container of devices being prepared */
GHashTable *devices;
/* The Object Manager server */
GDBusObjectManagerServer *object_manager;
+ /* The map of inhibited devices */
+ GHashTable *inhibited_devices;
/* The Test interface support */
MmGdbusTest *test_skeleton;
+
+#if defined WITH_UDEV
+ /* The UDev client */
+ GUdevClient *udev;
+#endif
+#if defined WITH_QRTR
+ /* The Qrtr Bus Watcher */
+ MMQrtrBusWatcher *qrtr_bus_watcher;
+#endif
};
/*****************************************************************************/
@@ -94,8 +132,8 @@ find_device_by_modem (MMBaseManager *manager,
}
static MMDevice *
-find_device_by_port (MMBaseManager *manager,
- GUdevDevice *port)
+find_device_by_port (MMBaseManager *manager,
+ MMKernelDevice *port)
{
GHashTableIter iter;
gpointer key, value;
@@ -111,18 +149,28 @@ find_device_by_port (MMBaseManager *manager,
}
static MMDevice *
-find_device_by_sysfs_path (MMBaseManager *self,
- const gchar *sysfs_path)
+find_device_by_port_name (MMBaseManager *manager,
+ const gchar *subsystem,
+ const gchar *name)
{
- return g_hash_table_lookup (self->priv->devices,
- sysfs_path);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, manager->priv->devices);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MMDevice *candidate = MM_DEVICE (value);
+
+ if (mm_device_owns_port_name (candidate, subsystem, name))
+ return candidate;
+ }
+ return NULL;
}
static MMDevice *
-find_device_by_udev_device (MMBaseManager *manager,
- GUdevDevice *udev_device)
+find_device_by_physdev_uid (MMBaseManager *self,
+ const gchar *physdev_uid)
{
- return find_device_by_sysfs_path (manager, g_udev_device_get_sysfs_path (udev_device));
+ return g_hash_table_lookup (self->priv->devices, physdev_uid);
}
/*****************************************************************************/
@@ -141,123 +189,116 @@ find_device_support_context_free (FindDeviceSupportContext *ctx)
}
static void
-find_device_support_ready (MMPluginManager *plugin_manager,
- GAsyncResult *result,
- FindDeviceSupportContext *ctx)
+device_support_check_ready (MMPluginManager *plugin_manager,
+ GAsyncResult *res,
+ FindDeviceSupportContext *ctx)
{
- GError *error = NULL;
+ GError *error = NULL;
+ MMPlugin *plugin;
+
+ /* If the device support check fails, either with an error, or afterwards
+ * when trying to create a modem object, we must remove the MMDevice from
+ * the tracking table of devices, so that a manual scan request afterwards
+ * re-scans all ports. */
- if (!mm_plugin_manager_find_device_support_finish (plugin_manager, result, &error)) {
- mm_warn ("Couldn't find support for device at '%s': %s",
- mm_device_get_path (ctx->device),
- error->message);
+ /* Receive plugin result from the plugin manager */
+ plugin = mm_plugin_manager_device_support_check_finish (plugin_manager, res, &error);
+ if (!plugin) {
+ mm_obj_info (ctx->self, "couldn't check support for device '%s': %s",
+ mm_device_get_uid (ctx->device), error->message);
g_error_free (error);
- } else if (!mm_device_create_modem (ctx->device, ctx->self->priv->object_manager, &error)) {
- mm_warn ("Couldn't create modem for device at '%s': %s",
- mm_device_get_path (ctx->device),
- error->message);
+ g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (ctx->device));
+ find_device_support_context_free (ctx);
+ return;
+ }
+
+ /* Set the plugin as the one expected in the device */
+ mm_device_set_plugin (ctx->device, G_OBJECT (plugin));
+ g_object_unref (plugin);
+
+ if (!mm_device_create_modem (ctx->device, &error)) {
+ mm_obj_warn (ctx->self, "couldn't create modem for device '%s': %s",
+ mm_device_get_uid (ctx->device), error->message);
g_error_free (error);
- } else {
- mm_info ("Modem for device at '%s' successfully created",
- mm_device_get_path (ctx->device));
+ g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (ctx->device));
+ find_device_support_context_free (ctx);
+ return;
}
+ /* Modem now created */
+ mm_obj_info (ctx->self, "modem for device '%s' successfully created",
+ mm_device_get_uid (ctx->device));
find_device_support_context_free (ctx);
}
-static GUdevDevice *
-find_physical_device (GUdevDevice *child)
+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,
+ const gchar *subsystem,
+ const gchar *name);
+
+static void
+device_removed (MMBaseManager *self,
+ const gchar *subsystem,
+ const gchar *name)
{
- GUdevDevice *iter, *old = NULL;
- GUdevDevice *physdev = NULL;
- const char *subsys, *type, *name;
- guint32 i = 0;
- gboolean is_usb = FALSE, is_pci = FALSE, is_pcmcia = FALSE, is_platform = FALSE;
- gboolean is_pnp = FALSE;
-
- g_return_val_if_fail (child != NULL, NULL);
-
- /* Bluetooth rfcomm devices are "virtual" and don't necessarily have
- * parents at all.
- */
- name = g_udev_device_get_name (child);
- if (name && strncmp (name, "rfcomm", 6) == 0)
- return g_object_ref (child);
-
- iter = g_object_ref (child);
- while (iter && i++ < 8) {
- subsys = g_udev_device_get_subsystem (iter);
- if (subsys) {
- if (is_usb || g_str_has_prefix (subsys, "usb")) {
- is_usb = TRUE;
- type = g_udev_device_get_devtype (iter);
- if (type && !strcmp (type, "usb_device")) {
- physdev = iter;
- break;
- }
- } else if (is_pcmcia || !strcmp (subsys, "pcmcia")) {
- GUdevDevice *pcmcia_parent;
- const char *tmp_subsys;
-
- is_pcmcia = TRUE;
-
- /* If the parent of this PCMCIA device is no longer part of
- * the PCMCIA subsystem, we want to stop since we're looking
- * for the base PCMCIA device, not the PCMCIA controller which
- * is usually PCI or some other bus type.
- */
- pcmcia_parent = g_udev_device_get_parent (iter);
- if (pcmcia_parent) {
- tmp_subsys = g_udev_device_get_subsystem (pcmcia_parent);
- if (tmp_subsys && strcmp (tmp_subsys, "pcmcia"))
- physdev = iter;
- g_object_unref (pcmcia_parent);
- if (physdev)
- break;
- }
- } else if (is_platform || !strcmp (subsys, "platform")) {
- /* Take the first platform parent as the physical device */
- is_platform = TRUE;
- physdev = iter;
- break;
- } else if (is_pci || !strcmp (subsys, "pci")) {
- is_pci = TRUE;
- physdev = iter;
- break;
- } else if (is_pnp || !strcmp (subsys, "pnp")) {
- is_pnp = TRUE;
- physdev = iter;
- break;
- }
- }
+ g_autoptr(MMDevice) device = NULL;
- old = iter;
- iter = g_udev_device_get_parent (old);
- g_object_unref (old);
+ g_assert (subsystem);
+ g_assert (name);
+ device = find_device_by_port_name (self, subsystem, name);
+ if (!device) {
+ /* 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, subsystem, name);
+ return;
}
- return physdev;
+ /* The callbacks triggered when the port is released or device support is
+ * cancelled may end up unreffing the device or removing it from the HT, and
+ * so in order to make sure the reference is still valid when we call
+ * support_check_cancel() and g_hash_table_remove(), we hold a full reference
+ * ourselves. */
+ g_object_ref (device);
+
+ mm_obj_info (self, "port %s released by device '%s'", name, mm_device_get_uid (device));
+ mm_device_release_port_name (device, subsystem, name);
+
+ /* If port probe list gets empty, remove the device object iself */
+ if (!mm_device_peek_port_probe_list (device)) {
+ mm_obj_dbg (self, "removing empty device '%s'", mm_device_get_uid (device));
+ if (mm_plugin_manager_device_support_check_cancel (self->priv->plugin_manager, device))
+ mm_obj_dbg (self, "device support check has been cancelled");
+
+ /* The device may have already been removed from the tracking HT, we
+ * just try to remove it and if it fails, we ignore it */
+ mm_device_remove_modem (device);
+ g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
+ }
}
static void
-device_added (MMBaseManager *manager,
- GUdevDevice *port,
- gboolean hotplugged,
- gboolean manual_scan)
+device_added (MMBaseManager *self,
+ MMKernelDevice *port,
+ gboolean hotplugged,
+ gboolean manual_scan)
{
- MMDevice *device;
- const char *subsys, *name, *physdev_path, *physdev_subsys;
- gboolean is_candidate;
- GUdevDevice *physdev = NULL;
+ MMDevice *device;
+ const gchar *physdev_uid;
+ const gchar *name;
g_return_if_fail (port != NULL);
- subsys = g_udev_device_get_subsystem (port);
- name = g_udev_device_get_name (port);
+ name = mm_kernel_device_get_name (port);
- /* ignore VTs */
- if (strncmp (name, "tty", 3) == 0 && isdigit (name[3]))
- return;
+ mm_obj_dbg (self, "adding port %s at sysfs path: %s",
+ name, mm_kernel_device_get_sysfs_path (port));
/* Ignore devices that aren't completely configured by udev yet. If
* ModemManager is started in parallel with udev, explicitly requesting
@@ -265,173 +306,197 @@ device_added (MMBaseManager *manager,
* applied (a bug in udev/gudev). Since we often need those rules to match
* the device to a specific ModemManager driver, we need to ensure that all
* rules have been processed before handling a device.
- */
- is_candidate = g_udev_device_get_property_as_boolean (port, "ID_MM_CANDIDATE");
- if (!is_candidate)
+ *
+ * This udev tag applies to each port in a device. In other words, the flag
+ * may be set in some ports, but not in others */
+ if (!mm_kernel_device_get_property_as_boolean (port, ID_MM_CANDIDATE)) {
+ /* This could mean that device changed, losing its candidate
+ * flags (such as Bluetooth RFCOMM devices upon disconnect.
+ * Try to forget it. */
+ device_removed (self, mm_kernel_device_get_subsystem (port), name);
+ mm_obj_dbg (self, "port %s not candidate", name);
return;
-
- if (find_device_by_port (manager, port))
- return;
-
- /* Find the port's physical device's sysfs path. This is the kernel device
- * that "owns" all the ports of the device, like the USB device or the PCI
- * device the provides each tty or network port.
- */
- physdev = find_physical_device (port);
- if (!physdev) {
- /* Warn about it, but filter out some common ports that we know don't have
- * anything to do with mobile broadband.
- */
- if ( strcmp (name, "console")
- && strcmp (name, "ptmx")
- && strcmp (name, "lo")
- && strcmp (name, "tty")
- && !strstr (name, "virbr"))
- mm_dbg ("(%s/%s): could not get port's parent device", subsys, name);
-
- goto out;
- }
-
- /* Is the device blacklisted? */
- if (g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_IGNORE")) {
- mm_dbg ("(%s/%s): port's parent device is blacklisted", subsys, name);
- goto out;
}
- /* Is the device in the manual-only greylist? If so, return if this is an
- * automatic scan. */
- if (!manual_scan && g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_MANUAL_SCAN_ONLY")) {
- mm_dbg ("(%s/%s): port probed only in manual scan", subsys, name);
- goto out;
+ /* 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 (self, 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 (self, physdev_uid, port, manual_scan);
+ return;
}
- /* If the physdev is a 'platform' or 'pnp' device that's not whitelisted, ignore it */
- physdev_subsys = g_udev_device_get_subsystem (physdev);
- if ( physdev_subsys
- && ( g_str_equal (physdev_subsys, "platform")
- || g_str_equal (physdev_subsys, "pnp"))
- && !g_udev_device_get_property_as_boolean (physdev, "ID_MM_PLATFORM_DRIVER_PROBE")) {
- mm_dbg ("(%s/%s): port's parent platform driver is not whitelisted", subsys, name);
- goto out;
- }
+ /* Run port filter */
+ if (!mm_filter_port (self->priv->filter, port, manual_scan))
+ return;
- physdev_path = g_udev_device_get_sysfs_path (physdev);
- if (!physdev_path) {
- mm_dbg ("(%s/%s): could not get port's parent device sysfs path", subsys, name);
- goto out;
+ /* If already added, ignore new event */
+ if (find_device_by_port (self, port)) {
+ mm_obj_dbg (self, "port %s already added", name);
+ return;
}
/* See if we already created an object to handle ports in this device */
- device = find_device_by_sysfs_path (manager, physdev_path);
+ device = find_device_by_physdev_uid (self, physdev_uid);
if (!device) {
FindDeviceSupportContext *ctx;
+ mm_obj_dbg (self, "port %s is first in device %s", name, physdev_uid);
+
/* Keep the device listed in the Manager */
- device = mm_device_new (physdev, hotplugged);
- g_hash_table_insert (manager->priv->devices,
- g_strdup (physdev_path),
+ device = mm_device_new (physdev_uid, hotplugged, FALSE, self->priv->object_manager);
+ g_hash_table_insert (self->priv->devices,
+ g_strdup (physdev_uid),
device);
/* Launch device support check */
ctx = g_slice_new (FindDeviceSupportContext);
- ctx->self = g_object_ref (manager);
+ ctx->self = g_object_ref (self);
ctx->device = g_object_ref (device);
- mm_plugin_manager_find_device_support (
- manager->priv->plugin_manager,
+ mm_plugin_manager_device_support_check (
+ self->priv->plugin_manager,
device,
- (GAsyncReadyCallback)find_device_support_ready,
+ (GAsyncReadyCallback) device_support_check_ready,
ctx);
- }
+ } else
+ mm_obj_dbg (self, "additional port %s in device %s", name, physdev_uid);
/* Grab the port in the existing device. */
mm_device_grab_port (device, port);
+}
-out:
- if (physdev)
- g_object_unref (physdev);
+#if defined WITH_QRTR
+
+static void
+handle_qrtr_device_added (MMBaseManager *self,
+ guint node_id,
+ MMQrtrBusWatcher *bus_watcher)
+{
+ g_autoptr(MMKernelDevice) kernel_device = NULL;
+ QrtrNode *node;
+
+ node = mm_qrtr_bus_watcher_peek_node (bus_watcher, node_id);
+
+ kernel_device = mm_kernel_device_qrtr_new (node);
+
+ device_added (self, kernel_device, TRUE, FALSE);
}
static void
-device_removed (MMBaseManager *self,
- GUdevDevice *udev_device)
+handle_qrtr_device_removed (MMBaseManager *self,
+ guint node_id)
{
- MMDevice *device;
- const gchar *subsys;
+ g_autofree gchar *qrtr_device_name = NULL;
+
+ qrtr_device_name = mm_kernel_device_qrtr_helper_build_name (node_id);
+ device_removed (self, MM_KERNEL_DEVICE_QRTR_SUBSYSTEM, qrtr_device_name);
+}
+#endif
+
+static gboolean
+handle_kernel_event (MMBaseManager *self,
+ MMKernelEventProperties *properties,
+ GError **error)
+{
+ const gchar *action;
+ const gchar *subsystem;
const gchar *name;
+ const gchar *uid;
- g_return_if_fail (udev_device != NULL);
-
- subsys = g_udev_device_get_subsystem (udev_device);
- name = g_udev_device_get_name (udev_device);
-
- if (!g_str_has_prefix (subsys, "usb") ||
- (name && g_str_has_prefix (name, "cdc-wdm"))) {
- /* Handle tty/net/wdm port removal */
- device = find_device_by_port (self, udev_device);
- if (device) {
- mm_info ("(%s/%s): released by modem %s",
- subsys,
- name,
- g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)));
- mm_device_release_port (device, udev_device);
-
- /* If port probe list gets empty, remove the device object iself */
- if (!mm_device_peek_port_probe_list (device)) {
- mm_dbg ("Removing empty device '%s'", mm_device_get_path (device));
- mm_device_remove_modem (device);
- g_hash_table_remove (self->priv->devices, mm_device_get_path (device));
- }
- }
+ action = mm_kernel_event_properties_get_action (properties);
+ if (!action) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'action'");
+ return FALSE;
+ }
+ if (g_strcmp0 (action, "add") != 0 && g_strcmp0 (action, "remove") != 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid 'action' parameter given: '%s' (expected 'add' or 'remove')", action);
+ return FALSE;
+ }
- return;
+ subsystem = mm_kernel_event_properties_get_subsystem (properties);
+ if (!subsystem) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'subsystem'");
+ return FALSE;
}
- /* This case is designed to handle the case where, at least with kernel 2.6.31, unplugging
- * an in-use ttyACMx device results in udev generating remove events for the usb, but the
- * ttyACMx device (subsystem tty) is not removed, since it was in-use. So if we have not
- * found a modem for the port (above), we're going to look here to see if we have a modem
- * associated with the newly removed device. If so, we'll remove the modem, since the
- * device has been removed. That way, if the device is reinserted later, we'll go through
- * the process of exporting it.
- */
- device = find_device_by_udev_device (self, udev_device);
- if (device) {
- mm_dbg ("Removing device '%s'", mm_device_get_path (device));
- mm_device_remove_modem (device);
- g_hash_table_remove (self->priv->devices, mm_device_get_path (device));
- return;
+ if (!g_strv_contains (mm_plugin_manager_get_subsystems (self->priv->plugin_manager), subsystem)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid 'subsystem' parameter given: '%s'", subsystem);
+ return FALSE;
}
- /* Maybe a plugin is checking whether or not the port is supported.
- * TODO: Cancel every possible supports check in this port. */
+ name = mm_kernel_event_properties_get_name (properties);
+ if (!name) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'name'");
+ return FALSE;
+ }
+
+ uid = mm_kernel_event_properties_get_uid (properties);
+
+ mm_obj_dbg (self, "kernel event reported:");
+ mm_obj_dbg (self, " action: %s", action);
+ mm_obj_dbg (self, " subsystem: %s", subsystem);
+ mm_obj_dbg (self, " name: %s", name);
+ mm_obj_dbg (self, " uid: %s", uid ? uid : "n/a");
+
+ if (g_strcmp0 (action, "add") == 0) {
+ g_autoptr(MMKernelDevice) kernel_device = NULL;
+#if defined WITH_UDEV
+ if (!mm_context_get_test_no_udev ())
+ kernel_device = mm_kernel_device_udev_new_from_properties (self->priv->udev, properties, error);
+ else
+#endif
+ kernel_device = mm_kernel_device_generic_new (properties, error);
+ if (!kernel_device)
+ return FALSE;
+
+ device_added (self, kernel_device, TRUE, TRUE);
+ return TRUE;
+ }
+
+ if (g_strcmp0 (action, "remove") == 0) {
+ device_removed (self, subsystem, name);
+ return TRUE;
+ }
+
+ g_assert_not_reached ();
}
+#if defined WITH_UDEV
+
static void
-handle_uevent (GUdevClient *client,
- const char *action,
- GUdevDevice *device,
- gpointer user_data)
+handle_uevent (MMBaseManager *self,
+ const gchar *action,
+ GUdevDevice *device)
{
- MMBaseManager *self = MM_BASE_MANAGER (user_data);
- const gchar *subsys;
+ const gchar *subsystem;
const gchar *name;
- g_return_if_fail (action != NULL);
-
- /* A bit paranoid */
- subsys = g_udev_device_get_subsystem (device);
- g_return_if_fail (subsys != NULL);
- g_return_if_fail (g_str_equal (subsys, "tty") || g_str_equal (subsys, "net") || g_str_has_prefix (subsys, "usb"));
-
- /* We only care about tty/net and usb/cdc-wdm devices when adding modem ports,
- * but for remove, also handle usb parent device remove events
- */
- name = g_udev_device_get_name (device);
- if ( (g_str_equal (action, "add") || g_str_equal (action, "move") || g_str_equal (action, "change"))
- && (!g_str_has_prefix (subsys, "usb") || (name && g_str_has_prefix (name, "cdc-wdm"))))
- device_added (self, device, TRUE, FALSE);
- else if (g_str_equal (action, "remove"))
- device_removed (self, device);
+ subsystem = g_udev_device_get_subsystem (device);
+ name = g_udev_device_get_name (device);
+
+ /* Valid udev devices must have subsystem and name set; if they don't have
+ * both things, we silently ignore them. */
+ if (!subsystem || !name)
+ return;
+
+ if (g_str_equal (action, "add") || g_str_equal (action, "move") || g_str_equal (action, "change")) {
+ g_autoptr(MMKernelDevice) kernel_device = NULL;
+
+ kernel_device = mm_kernel_device_udev_new (self->priv->udev, device);
+ device_added (self, kernel_device, TRUE, FALSE);
+ return;
+ }
+
+ if (g_str_equal (action, "remove")) {
+ device_removed (self, subsystem, name);
+ return;
+ }
}
typedef struct {
@@ -443,11 +508,25 @@ typedef struct {
static gboolean
start_device_added_idle (StartDeviceAdded *ctx)
{
- device_added (ctx->self, ctx->device, FALSE, ctx->manual_scan);
+ const gchar *subsystem;
+ const gchar *name;
+
+ subsystem = g_udev_device_get_subsystem (ctx->device);
+ name = g_udev_device_get_name (ctx->device);
+
+ /* Valid udev devices must have subsystem and name set; if they don't have
+ * both things, we silently ignore them. */
+ if (subsystem && name) {
+ g_autoptr(MMKernelDevice) kernel_device = NULL;
+
+ kernel_device = mm_kernel_device_udev_new (ctx->self->priv->udev, ctx->device);
+ device_added (ctx->self, kernel_device, FALSE, ctx->manual_scan);
+ }
+
g_object_unref (ctx->self);
g_object_unref (ctx->device);
g_slice_free (StartDeviceAdded, ctx);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -464,58 +543,96 @@ start_device_added (MMBaseManager *self,
g_idle_add ((GSourceFunc)start_device_added_idle, ctx);
}
-void
-mm_base_manager_start (MMBaseManager *manager,
- gboolean manual_scan)
+static void
+process_scan (MMBaseManager *self,
+ gboolean manual_scan)
{
- GList *devices, *iter;
+ const gchar **subsystems;
+ guint i;
+
+ subsystems = mm_plugin_manager_get_subsystems (self->priv->plugin_manager);
+ for (i = 0; subsystems[i]; i++) {
+ GList *devices;
+ GList *iter;
+
+ devices = g_udev_client_query_by_subsystem (self->priv->udev, subsystems[i]);
+ for (iter = devices; iter; iter = g_list_next (iter))
+ start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan);
+ g_list_free_full (devices, g_object_unref);
+ }
+}
- g_return_if_fail (manager != NULL);
- g_return_if_fail (MM_IS_BASE_MANAGER (manager));
+#endif
- if (!manager->priv->auto_scan && !manual_scan)
- return;
+static void
+process_initial_kernel_events (MMBaseManager *self)
+{
+ gchar *contents = NULL;
+ gchar *line;
+ GError *error = NULL;
- mm_dbg ("Starting %s device scan...", manual_scan ? "manual" : "automatic");
+ if (!self->priv->initial_kernel_events)
+ return;
- devices = g_udev_client_query_by_subsystem (manager->priv->udev, "tty");
- for (iter = devices; iter; iter = g_list_next (iter)) {
- start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan);
- g_object_unref (G_OBJECT (iter->data));
+ if (!g_file_get_contents (self->priv->initial_kernel_events, &contents, NULL, &error)) {
+ mm_obj_warn (self, "couldn't load initial kernel events: %s", error->message);
+ g_error_free (error);
+ return;
}
- g_list_free (devices);
- devices = g_udev_client_query_by_subsystem (manager->priv->udev, "net");
- for (iter = devices; iter; iter = g_list_next (iter)) {
- start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan);
- g_object_unref (G_OBJECT (iter->data));
- }
- g_list_free (devices);
+ line = contents;
+ while (line) {
+ gchar *next;
+
+ next = strchr (line, '\n');
+ if (next) {
+ *next = '\0';
+ next++;
+ }
- devices = g_udev_client_query_by_subsystem (manager->priv->udev, "usb");
- for (iter = devices; iter; iter = g_list_next (iter)) {
- const gchar *name;
+ /* ignore empty lines */
+ if (line[0] != '\0') {
+ MMKernelEventProperties *properties;
+
+ properties = mm_kernel_event_properties_new_from_string (line, &error);
+ if (!properties) {
+ mm_obj_warn (self, "couldn't parse line '%s' as initial kernel event %s", line, error->message);
+ g_clear_error (&error);
+ } else if (!handle_kernel_event (self, properties, &error)) {
+ mm_obj_warn (self, "couldn't process line '%s' as initial kernel event %s", line, error->message);
+ g_clear_error (&error);
+ } else
+ mm_obj_dbg (self, "processed initial kernel event:' %s'", line);
+ g_clear_object (&properties);
+ }
- name = g_udev_device_get_name (G_UDEV_DEVICE (iter->data));
- if (name && g_str_has_prefix (name, "cdc-wdm"))
- start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan);
- g_object_unref (G_OBJECT (iter->data));
+ line = next;
}
- g_list_free (devices);
- /* Newer kernels report 'usbmisc' subsystem */
- devices = g_udev_client_query_by_subsystem (manager->priv->udev, "usbmisc");
- for (iter = devices; iter; iter = g_list_next (iter)) {
- const gchar *name;
+ g_free (contents);
+}
- name = g_udev_device_get_name (G_UDEV_DEVICE (iter->data));
- if (name && g_str_has_prefix (name, "cdc-wdm"))
- start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan);
- g_object_unref (G_OBJECT (iter->data));
+void
+mm_base_manager_start (MMBaseManager *self,
+ gboolean manual_scan)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_BASE_MANAGER (self));
+
+ if (!self->priv->auto_scan && !manual_scan) {
+ /* If we have a list of initial kernel events, process it now */
+ process_initial_kernel_events (self);
+ return;
}
- g_list_free (devices);
- mm_dbg ("Finished device scan...");
+#if defined WITH_UDEV
+ if (!mm_context_get_test_no_udev ()) {
+ mm_obj_dbg (self, "starting %s device scan...", manual_scan ? "manual" : "automatic");
+ process_scan (self, manual_scan);
+ mm_obj_dbg (self, "finished device scan...");
+ } else
+#endif
+ mm_obj_dbg (self, "unsupported %s device scan...", manual_scan ? "manual" : "automatic");
}
/*****************************************************************************/
@@ -532,8 +649,9 @@ remove_disable_ready (MMBaseModem *modem,
device = find_device_by_modem (self, modem);
if (device) {
+ g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
mm_device_remove_modem (device);
- g_hash_table_remove (self->priv->devices, device);
+ g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
}
}
@@ -549,8 +667,23 @@ foreach_disable (gpointer key,
mm_base_modem_disable (modem, (GAsyncReadyCallback)remove_disable_ready, self);
}
+static gboolean
+foreach_remove (gpointer key,
+ MMDevice *device,
+ MMBaseManager *self)
+{
+ MMBaseModem *modem;
+
+ modem = mm_device_peek_modem (device);
+ if (modem)
+ g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
+ mm_device_remove_modem (device);
+ return TRUE;
+}
+
void
-mm_base_manager_shutdown (MMBaseManager *self)
+mm_base_manager_shutdown (MMBaseManager *self,
+ gboolean disable)
{
g_return_if_fail (self != NULL);
g_return_if_fail (MM_IS_BASE_MANAGER (self));
@@ -558,12 +691,18 @@ mm_base_manager_shutdown (MMBaseManager *self)
/* Cancel all ongoing auth requests */
g_cancellable_cancel (self->priv->authp_cancellable);
- g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, self);
+ if (disable) {
+ g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, self);
+
+ /* Disabling may take a few iterations of the mainloop, so the caller
+ * has to iterate the mainloop until all devices have been disabled and
+ * removed.
+ */
+ return;
+ }
- /* Disabling may take a few iterations of the mainloop, so the caller
- * has to iterate the mainloop until all devices have been disabled and
- * removed.
- */
+ /* Otherwise, just remove directly */
+ g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, self);
}
guint32
@@ -586,6 +725,50 @@ mm_base_manager_num_modems (MMBaseManager *self)
}
/*****************************************************************************/
+/* Quick resume synchronization */
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+static void
+base_modem_sync_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GError) error = NULL;
+
+ mm_base_modem_sync_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "synchronization failed: %s", error->message);
+ return;
+ }
+ mm_obj_info (self, "synchronization finished");
+}
+
+void
+mm_base_manager_sync (MMBaseManager *self)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_BASE_MANAGER (self));
+
+ /* Refresh each device */
+ g_hash_table_iter_init (&iter, self->priv->devices);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MMBaseModem *modem;
+
+ modem = mm_device_peek_modem (MM_DEVICE (value));
+
+ /* We just want to start the synchronization, we don't need the result */
+ if (modem)
+ mm_base_modem_sync (modem, (GAsyncReadyCallback)base_modem_sync_ready, NULL);
+ }
+}
+
+#endif
+
+/*****************************************************************************/
/* Set logging */
typedef struct {
@@ -604,18 +787,18 @@ set_logging_context_free (SetLoggingContext *ctx)
}
static void
-set_logging_auth_ready (MMAuthProvider *authp,
- GAsyncResult *res,
+set_logging_auth_ready (MMAuthProvider *authp,
+ GAsyncResult *res,
SetLoggingContext *ctx)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else if (!mm_log_set_level(ctx->level, &error))
+ else if (!mm_log_set_level (ctx->level, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else {
- mm_info ("logging: level '%s'", ctx->level);
+ mm_obj_info (ctx->self, "logging: level '%s'", ctx->level);
mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging (
MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
ctx->invocation);
@@ -632,7 +815,7 @@ handle_set_logging (MmGdbusOrgFreedesktopModemManager1 *manager,
SetLoggingContext *ctx;
ctx = g_new0 (SetLoggingContext, 1);
- ctx->self = g_object_ref (manager);
+ ctx->self = MM_BASE_MANAGER (g_object_ref (manager));
ctx->invocation = g_object_ref (invocation);
ctx->level = g_strdup (level);
@@ -671,11 +854,18 @@ scan_devices_auth_ready (MMAuthProvider *authp,
if (!mm_auth_provider_authorize_finish (authp, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else {
- /* Otherwise relaunch device scan */
- mm_base_manager_start (MM_BASE_MANAGER (ctx->self), TRUE);
- mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices (
- MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
- ctx->invocation);
+#if defined WITH_UDEV
+ if (!mm_context_get_test_no_udev ()) {
+ /* Otherwise relaunch device scan */
+ mm_base_manager_start (MM_BASE_MANAGER (ctx->self), TRUE);
+ mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices (
+ MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
+ ctx->invocation);
+ } else
+#endif
+ g_dbus_method_invocation_return_error_literal (
+ ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot request manual scan of devices: unsupported");
}
scan_devices_context_free (ctx);
@@ -688,7 +878,7 @@ handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager,
ScanDevicesContext *ctx;
ctx = g_new (ScanDevicesContext, 1);
- ctx->self = g_object_ref (manager);
+ ctx->self = MM_BASE_MANAGER (g_object_ref (manager));
ctx->invocation = g_object_ref (invocation);
mm_auth_provider_authorize (ctx->self->priv->authp,
@@ -701,6 +891,410 @@ handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager,
}
/*****************************************************************************/
+
+typedef struct {
+ MMBaseManager *self;
+ GDBusMethodInvocation *invocation;
+ GVariant *dictionary;
+} ReportKernelEventContext;
+
+static void
+report_kernel_event_context_free (ReportKernelEventContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_variant_unref (ctx->dictionary);
+ g_slice_free (ReportKernelEventContext, ctx);
+}
+
+static void
+report_kernel_event_auth_ready (MMAuthProvider *authp,
+ GAsyncResult *res,
+ ReportKernelEventContext *ctx)
+{
+ GError *error = NULL;
+ MMKernelEventProperties *properties = NULL;
+
+ if (!mm_auth_provider_authorize_finish (authp, res, &error))
+ goto out;
+
+#if defined WITH_UDEV
+ if (!mm_context_get_test_no_udev () && ctx->self->priv->auto_scan) {
+ error = g_error_new_literal (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot report kernel event: "
+ "udev monitoring already in place");
+ goto out;
+ }
+#endif
+
+ properties = mm_kernel_event_properties_new_from_dictionary (ctx->dictionary, &error);
+ if (!properties)
+ goto out;
+
+ handle_kernel_event (ctx->self, properties, &error);
+
+out:
+ if (error) {
+ mm_obj_warn (ctx->self, "couldn't handle kernel event: %s", error->message);
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ } else
+ mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event (
+ MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
+ ctx->invocation);
+
+ if (properties)
+ g_object_unref (properties);
+ report_kernel_event_context_free (ctx);
+}
+
+static gboolean
+handle_report_kernel_event (MmGdbusOrgFreedesktopModemManager1 *manager,
+ GDBusMethodInvocation *invocation,
+ GVariant *dictionary)
+{
+ ReportKernelEventContext *ctx;
+
+ ctx = g_slice_new0 (ReportKernelEventContext);
+ ctx->self = MM_BASE_MANAGER (g_object_ref (manager));
+ ctx->invocation = g_object_ref (invocation);
+ ctx->dictionary = g_variant_ref (dictionary);
+
+ mm_auth_provider_authorize (ctx->self->priv->authp,
+ invocation,
+ MM_AUTHORIZATION_MANAGER_CONTROL,
+ ctx->self->priv->authp_cancellable,
+ (GAsyncReadyCallback)report_kernel_event_auth_ready,
+ ctx);
+ 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,
+ const gchar *subsystem,
+ const gchar *name)
+{
+ 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 ((g_strcmp0 (subsystem, mm_kernel_device_get_subsystem (port_info->kernel_port)) == 0) &&
+ (g_strcmp0 (name, mm_kernel_device_get_name (port_info->kernel_port)) == 0)) {
+ mm_obj_dbg (self, "released port %s while inhibited", name);
+ 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_obj_dbg (self, "added port %s while inhibited", 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, &error)) {
+ mm_obj_warn (self, "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_obj_info (lost_ctx->self, "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_obj_info (ctx->self, "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_obj_info (ctx->self, "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_slice_new0 (InhibitDeviceContext);
+ ctx->self = MM_BASE_MANAGER (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 */
static gboolean
@@ -713,17 +1307,15 @@ handle_set_profile (MmGdbusTest *skeleton,
{
MMPlugin *plugin;
MMDevice *device;
- gchar *physdev;
+ gchar *physdev_uid;
GError *error = NULL;
- mm_info ("Test profile set to: '%s'", id);
+ mm_obj_info (self, "test profile set to: '%s'", id);
/* Create device and keep it listed in the Manager */
- physdev = g_strdup_printf ("/virtual/%s", id);
- device = mm_device_virtual_new (physdev, TRUE);
- g_hash_table_insert (self->priv->devices,
- g_strdup (physdev),
- device);
+ physdev_uid = g_strdup_printf ("/virtual/%s", id);
+ device = mm_device_new (physdev_uid, TRUE, TRUE, self->priv->object_manager);
+ g_hash_table_insert (self->priv->devices, physdev_uid, device);
/* Grab virtual ports */
mm_device_virtual_grab_ports (device, (const gchar **)ports);
@@ -735,29 +1327,29 @@ handle_set_profile (MmGdbusTest *skeleton,
MM_CORE_ERROR_NOT_FOUND,
"Requested plugin '%s' not found",
plugin_name);
- mm_warn ("Couldn't set plugin for virtual device at '%s': %s",
- mm_device_get_path (device),
- error->message);
+ mm_obj_warn (self, "couldn't set plugin for virtual device '%s': %s",
+ mm_device_get_uid (device),
+ error->message);
goto out;
}
mm_device_set_plugin (device, G_OBJECT (plugin));
/* Create modem */
- if (!mm_device_create_modem (device, self->priv->object_manager, &error)) {
- mm_warn ("Couldn't create modem for virtual device at '%s': %s",
- mm_device_get_path (device),
- error->message);
+ if (!mm_device_create_modem (device, &error)) {
+ mm_obj_warn (self, "couldn't create modem for virtual device '%s': %s",
+ mm_device_get_uid (device),
+ error->message);
goto out;
}
- mm_info ("Modem for virtual device at '%s' successfully created",
- mm_device_get_path (device));
+ mm_obj_info (self, "modem for virtual device '%s' successfully created",
+ mm_device_get_uid (device));
out:
if (error) {
mm_device_remove_modem (device);
- g_hash_table_remove (self->priv->devices, mm_device_get_path (device));
+ g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error);
} else
@@ -768,65 +1360,85 @@ out:
/*****************************************************************************/
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ return g_strdup ("base-manager");
+}
+
+/*****************************************************************************/
+
MMBaseManager *
-mm_base_manager_new (GDBusConnection *connection,
- const gchar *plugin_dir,
- gboolean auto_scan,
- gboolean enable_test,
- GError **error)
+mm_base_manager_new (GDBusConnection *connection,
+ const gchar *plugin_dir,
+ gboolean auto_scan,
+ MMFilterRule filter_policy,
+ const gchar *initial_kernel_events,
+ gboolean enable_test,
+ GError **error)
{
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
return g_initable_new (MM_TYPE_BASE_MANAGER,
NULL, /* cancellable */
error,
- MM_BASE_MANAGER_CONNECTION, connection,
- MM_BASE_MANAGER_PLUGIN_DIR, plugin_dir,
- MM_BASE_MANAGER_AUTO_SCAN, auto_scan,
- MM_BASE_MANAGER_ENABLE_TEST, enable_test,
+ MM_BASE_MANAGER_CONNECTION, connection,
+ MM_BASE_MANAGER_PLUGIN_DIR, plugin_dir,
+ MM_BASE_MANAGER_AUTO_SCAN, auto_scan,
+ MM_BASE_MANAGER_FILTER_POLICY, filter_policy,
+ MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS, initial_kernel_events,
+ MM_BASE_MANAGER_ENABLE_TEST, enable_test,
+ "version", MM_DIST_VERSION,
NULL);
}
static void
-set_property (GObject *object,
- guint prop_id,
+set_property (GObject *object,
+ guint prop_id,
const GValue *value,
- GParamSpec *pspec)
+ GParamSpec *pspec)
{
- MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv;
+ MMBaseManager *self = MM_BASE_MANAGER (object);
switch (prop_id) {
case PROP_CONNECTION: {
gboolean had_connection = FALSE;
- if (priv->connection) {
+ if (self->priv->connection) {
had_connection = TRUE;
- g_object_unref (priv->connection);
+ g_object_unref (self->priv->connection);
}
- priv->connection = g_value_dup_object (value);
+ self->priv->connection = g_value_dup_object (value);
/* Propagate connection loss to subobjects */
- if (had_connection && !priv->connection) {
- if (priv->object_manager) {
- mm_dbg ("Stopping connection in object manager server");
- g_dbus_object_manager_server_set_connection (priv->object_manager, NULL);
+ if (had_connection && !self->priv->connection) {
+ if (self->priv->object_manager) {
+ mm_obj_dbg (self, "stopping connection in object manager server");
+ g_dbus_object_manager_server_set_connection (self->priv->object_manager, NULL);
}
- if (priv->test_skeleton &&
- g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (priv->test_skeleton))) {
- mm_dbg ("Stopping connection in test skeleton");
- g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (priv->test_skeleton));
+ if (self->priv->test_skeleton &&
+ g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton))) {
+ mm_obj_dbg (self, "stopping connection in test skeleton");
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton));
}
}
break;
}
case PROP_AUTO_SCAN:
- priv->auto_scan = g_value_get_boolean (value);
+ self->priv->auto_scan = g_value_get_boolean (value);
+ break;
+ case PROP_FILTER_POLICY:
+ self->priv->filter_policy = g_value_get_flags (value);
break;
case PROP_ENABLE_TEST:
- priv->enable_test = g_value_get_boolean (value);
+ self->priv->enable_test = g_value_get_boolean (value);
break;
case PROP_PLUGIN_DIR:
- g_free (priv->plugin_dir);
- priv->plugin_dir = g_value_dup_string (value);
+ g_free (self->priv->plugin_dir);
+ self->priv->plugin_dir = g_value_dup_string (value);
+ break;
+ case PROP_INITIAL_KERNEL_EVENTS:
+ g_free (self->priv->initial_kernel_events);
+ self->priv->initial_kernel_events = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -835,25 +1447,31 @@ set_property (GObject *object,
}
static void
-get_property (GObject *object,
- guint prop_id,
- GValue *value,
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
GParamSpec *pspec)
{
- MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv;
+ MMBaseManager *self = MM_BASE_MANAGER (object);
switch (prop_id) {
case PROP_CONNECTION:
- g_value_set_object (value, priv->connection);
+ g_value_set_object (value, self->priv->connection);
break;
case PROP_AUTO_SCAN:
- g_value_set_boolean (value, priv->auto_scan);
+ g_value_set_boolean (value, self->priv->auto_scan);
+ break;
+ case PROP_FILTER_POLICY:
+ g_value_set_flags (value, self->priv->filter_policy);
break;
case PROP_ENABLE_TEST:
- g_value_set_boolean (value, priv->enable_test);
+ g_value_set_boolean (value, self->priv->enable_test);
break;
case PROP_PLUGIN_DIR:
- g_value_set_string (value, priv->plugin_dir);
+ g_value_set_string (value, self->priv->plugin_dir);
+ break;
+ case PROP_INITIAL_KERNEL_EVENTS:
+ g_value_set_string (value, self->priv->initial_kernel_events);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -862,82 +1480,100 @@ get_property (GObject *object,
}
static void
-mm_base_manager_init (MMBaseManager *manager)
+mm_base_manager_init (MMBaseManager *self)
{
- MMBaseManagerPrivate *priv;
- const gchar *subsys[5] = { "tty", "net", "usb", "usbmisc", NULL };
-
/* Setup private data */
- manager->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
- MM_TYPE_BASE_MANAGER,
- MMBaseManagerPrivate);
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_MANAGER, MMBaseManagerPrivate);
/* Setup authorization provider */
- priv->authp = mm_auth_get_provider ();
- priv->authp_cancellable = g_cancellable_new ();
+ self->priv->authp = mm_auth_provider_get ();
+ self->priv->authp_cancellable = g_cancellable_new ();
/* Setup internal lists of device objects */
- priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ self->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- /* Setup UDev client */
- priv->udev = g_udev_client_new (subsys);
+ /* Setup internal list of inhibited devices */
+ self->priv->inhibited_devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)inhibited_device_info_free);
/* By default, enable autoscan */
- priv->auto_scan = TRUE;
+ self->priv->auto_scan = TRUE;
/* By default, no test interface */
- priv->enable_test = FALSE;
+ self->priv->enable_test = FALSE;
/* Setup Object Manager Server */
- priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH);
+ self->priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH);
/* Enable processing of input DBus messages */
- g_signal_connect (manager,
- "handle-set-logging",
- G_CALLBACK (handle_set_logging),
- NULL);
- g_signal_connect (manager,
- "handle-scan-devices",
- G_CALLBACK (handle_scan_devices),
+ g_object_connect (self,
+ "signal::handle-set-logging", G_CALLBACK (handle_set_logging), NULL,
+ "signal::handle-scan-devices", G_CALLBACK (handle_scan_devices), NULL,
+ "signal::handle-report-kernel-event", G_CALLBACK (handle_report_kernel_event), NULL,
+ "signal::handle-inhibit-device", G_CALLBACK (handle_inhibit_device), NULL,
NULL);
}
static gboolean
-initable_init (GInitable *initable,
- GCancellable *cancellable,
- GError **error)
+initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
{
- MMBaseManagerPrivate *priv = MM_BASE_MANAGER (initable)->priv;
+ MMBaseManager *self = MM_BASE_MANAGER (initable);
- /* If autoscan enabled, list for udev events */
- if (priv->auto_scan)
- g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), initable);
+ /* Create filter */
+ self->priv->filter = mm_filter_new (self->priv->filter_policy, error);
+ if (!self->priv->filter)
+ return FALSE;
/* Create plugin manager */
- priv->plugin_manager = mm_plugin_manager_new (priv->plugin_dir, error);
- if (!priv->plugin_manager)
+ self->priv->plugin_manager = mm_plugin_manager_new (self->priv->plugin_dir, self->priv->filter, error);
+ if (!self->priv->plugin_manager)
return FALSE;
+#if defined WITH_UDEV
+ /* Create udev client based on the subsystems requested by the plugins */
+ self->priv->udev = g_udev_client_new (mm_plugin_manager_get_subsystems (self->priv->plugin_manager));
+
+ /* If autoscan enabled, list for udev events */
+ if (!mm_context_get_test_no_udev () && self->priv->auto_scan)
+ g_signal_connect_swapped (self->priv->udev, "uevent", G_CALLBACK (handle_uevent), initable);
+#endif
+
+#if defined WITH_QRTR
+ if (!mm_context_get_test_no_qrtr ()) {
+ /* Create and setup the QrtrBusWatcher */
+ self->priv->qrtr_bus_watcher = mm_qrtr_bus_watcher_new ();
+ mm_qrtr_bus_watcher_start (self->priv->qrtr_bus_watcher, NULL, NULL);
+ /* If autoscan enabled, list for QrtrBusWatcher events */
+ if (self->priv->auto_scan) {
+ g_object_connect (self->priv->qrtr_bus_watcher,
+ "swapped-signal::" MM_QRTR_BUS_WATCHER_DEVICE_ADDED, G_CALLBACK (handle_qrtr_device_added), self,
+ "swapped-signal::" MM_QRTR_BUS_WATCHER_DEVICE_REMOVED, G_CALLBACK (handle_qrtr_device_removed), self,
+ NULL);
+ }
+ }
+#endif
+
/* Export the manager interface */
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (initable),
- priv->connection,
+ self->priv->connection,
MM_DBUS_PATH,
error))
return FALSE;
/* Export the Object Manager interface */
- g_dbus_object_manager_server_set_connection (priv->object_manager,
- priv->connection);
+ g_dbus_object_manager_server_set_connection (self->priv->object_manager,
+ self->priv->connection);
/* Setup the Test skeleton and export the interface */
- if (priv->enable_test) {
- priv->test_skeleton = mm_gdbus_test_skeleton_new ();
- g_signal_connect (priv->test_skeleton,
+ if (self->priv->enable_test) {
+ self->priv->test_skeleton = mm_gdbus_test_skeleton_new ();
+ g_signal_connect (self->priv->test_skeleton,
"handle-set-profile",
G_CALLBACK (handle_set_profile),
initable);
- if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->test_skeleton),
- priv->connection,
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton),
+ self->priv->connection,
MM_DBUS_PATH,
error))
return FALSE;
@@ -950,32 +1586,43 @@ initable_init (GInitable *initable,
static void
finalize (GObject *object)
{
- MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv;
+ MMBaseManager *self = MM_BASE_MANAGER (object);
- g_free (priv->plugin_dir);
+ g_free (self->priv->initial_kernel_events);
+ g_free (self->priv->plugin_dir);
- g_hash_table_destroy (priv->devices);
+ g_hash_table_destroy (self->priv->inhibited_devices);
+ g_hash_table_destroy (self->priv->devices);
- if (priv->udev)
- g_object_unref (priv->udev);
+#if defined WITH_UDEV
+ if (self->priv->udev)
+ g_object_unref (self->priv->udev);
+#endif
- if (priv->plugin_manager)
- g_object_unref (priv->plugin_manager);
+#if defined WITH_QRTR
+ if (self->priv->qrtr_bus_watcher)
+ g_object_unref (self->priv->qrtr_bus_watcher);
+#endif
- if (priv->object_manager)
- g_object_unref (priv->object_manager);
+ if (self->priv->filter)
+ g_object_unref (self->priv->filter);
- if (priv->test_skeleton)
- g_object_unref (priv->test_skeleton);
+ if (self->priv->plugin_manager)
+ g_object_unref (self->priv->plugin_manager);
- if (priv->connection)
- g_object_unref (priv->connection);
+ if (self->priv->object_manager)
+ g_object_unref (self->priv->object_manager);
- if (priv->authp)
- g_object_unref (priv->authp);
+ if (self->priv->test_skeleton)
+ g_object_unref (self->priv->test_skeleton);
- if (priv->authp_cancellable)
- g_object_unref (priv->authp_cancellable);
+ if (self->priv->connection)
+ g_object_unref (self->priv->connection);
+
+ /* note: authp is a singleton, we don't keep a full reference */
+
+ if (self->priv->authp_cancellable)
+ g_object_unref (self->priv->authp_cancellable);
G_OBJECT_CLASS (mm_base_manager_parent_class)->finalize (object);
}
@@ -987,6 +1634,12 @@ initable_iface_init (GInitableIface *iface)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_base_manager_class_init (MMBaseManagerClass *manager_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
@@ -1016,6 +1669,15 @@ mm_base_manager_class_init (MMBaseManagerClass *manager_class)
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_FILTER_POLICY,
+ g_param_spec_flags (MM_BASE_MANAGER_FILTER_POLICY,
+ "Filter policy",
+ "Mask of rules enabled in the filter",
+ MM_TYPE_FILTER_RULE,
+ MM_FILTER_RULE_NONE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
g_object_class_install_property
(object_class, PROP_ENABLE_TEST,
g_param_spec_boolean (MM_BASE_MANAGER_ENABLE_TEST,
@@ -1031,4 +1693,12 @@ mm_base_manager_class_init (MMBaseManagerClass *manager_class)
"Where to look for plugins",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_INITIAL_KERNEL_EVENTS,
+ g_param_spec_string (MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS,
+ "Initial kernel events",
+ "Path to a file with the list of initial kernel events",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}