| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details: |
| * |
| * Copyright (C) 2008 - 2009 Novell, Inc. |
| * Copyright (C) 2009 - 2012 Red Hat, 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> |
| |
| #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-plugin.h" |
| #include "mm-filter.h" |
| #include "mm-log.h" |
| |
| static void initable_iface_init (GInitableIface *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)); |
| |
| enum { |
| PROP_0, |
| PROP_CONNECTION, |
| PROP_AUTO_SCAN, |
| PROP_FILTER_POLICY, |
| PROP_ENABLE_TEST, |
| PROP_PLUGIN_DIR, |
| PROP_INITIAL_KERNEL_EVENTS, |
| LAST_PROP |
| }; |
| |
| struct _MMBaseManagerPrivate { |
| /* The connection to the system bus */ |
| 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; |
| /* 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 |
| }; |
| |
| /*****************************************************************************/ |
| |
| static MMDevice * |
| find_device_by_modem (MMBaseManager *manager, |
| MMBaseModem *modem) |
| { |
| 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 (modem == mm_device_peek_modem (candidate)) |
| return candidate; |
| } |
| return NULL; |
| } |
| |
| static MMDevice * |
| find_device_by_port (MMBaseManager *manager, |
| MMKernelDevice *port) |
| { |
| 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 (candidate, port)) |
| return candidate; |
| } |
| return NULL; |
| } |
| |
| static MMDevice * |
| find_device_by_physdev_uid (MMBaseManager *self, |
| const gchar *physdev_uid) |
| { |
| return g_hash_table_lookup (self->priv->devices, physdev_uid); |
| } |
| |
| static MMDevice * |
| find_device_by_kernel_device (MMBaseManager *manager, |
| MMKernelDevice *kernel_device) |
| { |
| return find_device_by_physdev_uid (manager, mm_kernel_device_get_physdev_uid (kernel_device)); |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MMBaseManager *self; |
| MMDevice *device; |
| } FindDeviceSupportContext; |
| |
| static void |
| find_device_support_context_free (FindDeviceSupportContext *ctx) |
| { |
| g_object_unref (ctx->self); |
| g_object_unref (ctx->device); |
| g_slice_free (FindDeviceSupportContext, ctx); |
| } |
| |
| static void |
| device_support_check_ready (MMPluginManager *plugin_manager, |
| GAsyncResult *res, |
| FindDeviceSupportContext *ctx) |
| { |
| 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. */ |
| |
| /* Receive plugin result from the plugin manager */ |
| plugin = mm_plugin_manager_device_support_check_finish (plugin_manager, res, &error); |
| if (!plugin) { |
| mm_info ("Couldn't check support for device '%s': %s", |
| mm_device_get_uid (ctx->device), error->message); |
| g_error_free (error); |
| 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, ctx->self->priv->object_manager, &error)) { |
| mm_warn ("Couldn't create modem for device '%s': %s", |
| mm_device_get_uid (ctx->device), error->message); |
| g_error_free (error); |
| 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_info ("Modem for device '%s' successfully created", |
| mm_device_get_uid (ctx->device)); |
| 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 |
| device_removed (MMBaseManager *self, |
| MMKernelDevice *kernel_device) |
| { |
| MMDevice *device; |
| const gchar *subsys; |
| const gchar *name; |
| |
| g_return_if_fail (kernel_device != NULL); |
| |
| subsys = mm_kernel_device_get_subsystem (kernel_device); |
| name = mm_kernel_device_get_name (kernel_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, kernel_device); |
| if (device) { |
| /* 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_info ("(%s/%s): released by device '%s'", subsys, name, mm_device_get_uid (device)); |
| mm_device_release_port (device, kernel_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_uid (device)); |
| if (mm_plugin_manager_device_support_check_cancel (self->priv->plugin_manager, device)) |
| mm_dbg ("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)); |
| } |
| } |
| 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; |
| } |
| |
| #if defined WITH_UDEV |
| /* When a USB modem is switching its USB configuration, udev may deliver |
| * the remove events of USB interfaces associated with the old USB |
| * configuration and the add events of USB interfaces associated with the |
| * new USB configuration in an interleaved fashion. As we don't want a |
| * remove event of an USB interface trigger the removal of a MMDevice for |
| * the special case being handled here, we ignore any remove event with |
| * DEVTYPE != usb_device. |
| */ |
| if (g_strcmp0 (mm_kernel_device_get_property (kernel_device, "DEVTYPE"), "usb_device") != 0) |
| return; |
| #endif |
| |
| /* 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_kernel_device (self, kernel_device); |
| if (device) { |
| mm_dbg ("Removing device '%s'", mm_device_get_uid (device)); |
| mm_device_remove_modem (device); |
| g_hash_table_remove (self->priv->devices, mm_device_get_uid (device)); |
| return; |
| } |
| } |
| |
| static void |
| device_added (MMBaseManager *manager, |
| MMKernelDevice *port, |
| gboolean hotplugged, |
| gboolean manual_scan) |
| { |
| MMDevice *device; |
| const gchar *physdev_uid; |
| const gchar *subsys; |
| const gchar *name; |
| |
| g_return_if_fail (port != NULL); |
| |
| subsys = mm_kernel_device_get_subsystem (port); |
| name = mm_kernel_device_get_name (port); |
| |
| mm_dbg ("(%s/%s): adding device at sysfs path: %s", |
| subsys, 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 |
| * devices may return devices for which not all udev rules have yet been |
| * 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. |
| * |
| * 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 (manager, port); |
| mm_dbg ("(%s/%s): port not candidate", subsys, name); |
| 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 */ |
| if (!mm_filter_port (manager->priv->filter, port, manual_scan)) |
| return; |
| |
| /* If already added, ignore new event */ |
| if (find_device_by_port (manager, port)) { |
| mm_dbg ("(%s/%s): port already added", subsys, name); |
| return; |
| } |
| |
| /* See if we already created an object to handle ports in this device */ |
| device = find_device_by_physdev_uid (manager, physdev_uid); |
| if (!device) { |
| FindDeviceSupportContext *ctx; |
| |
| mm_dbg ("(%s/%s): first port in device %s", |
| subsys, name, physdev_uid); |
| |
| /* Keep the device listed in the Manager */ |
| device = mm_device_new (physdev_uid, hotplugged, FALSE); |
| g_hash_table_insert (manager->priv->devices, |
| g_strdup (physdev_uid), |
| device); |
| |
| /* Launch device support check */ |
| ctx = g_slice_new (FindDeviceSupportContext); |
| ctx->self = g_object_ref (manager); |
| ctx->device = g_object_ref (device); |
| mm_plugin_manager_device_support_check ( |
| manager->priv->plugin_manager, |
| device, |
| (GAsyncReadyCallback) device_support_check_ready, |
| ctx); |
| } else |
| mm_dbg ("(%s/%s): additional port in device %s", |
| subsys, name, physdev_uid); |
| |
| /* Grab the port in the existing device. */ |
| mm_device_grab_port (device, port); |
| } |
| |
| static gboolean |
| handle_kernel_event (MMBaseManager *self, |
| MMKernelEventProperties *properties, |
| GError **error) |
| { |
| MMKernelDevice *kernel_device; |
| const gchar *action; |
| const gchar *subsystem; |
| const gchar *name; |
| const gchar *uid; |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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_dbg ("Kernel event reported:"); |
| mm_dbg (" action: %s", action); |
| mm_dbg (" subsystem: %s", subsystem); |
| mm_dbg (" name: %s", name); |
| mm_dbg (" uid: %s", uid ? uid : "n/a"); |
| |
| #if defined WITH_UDEV |
| kernel_device = mm_kernel_device_udev_new_from_properties (properties, error); |
| #else |
| kernel_device = mm_kernel_device_generic_new (properties, error); |
| #endif |
| |
| if (!kernel_device) |
| return FALSE; |
| |
| if (g_strcmp0 (action, "add") == 0) |
| device_added (self, kernel_device, TRUE, TRUE); |
| else if (g_strcmp0 (action, "remove") == 0) |
| device_removed (self, kernel_device); |
| else |
| g_assert_not_reached (); |
| g_object_unref (kernel_device); |
| |
| return TRUE; |
| } |
| |
| #if defined WITH_UDEV |
| |
| static void |
| handle_uevent (GUdevClient *client, |
| const char *action, |
| GUdevDevice *device, |
| gpointer user_data) |
| { |
| MMBaseManager *self = MM_BASE_MANAGER (user_data); |
| const gchar *subsys; |
| const gchar *name; |
| MMKernelDevice *kernel_device; |
| |
| 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")); |
| |
| kernel_device = mm_kernel_device_udev_new (device); |
| |
| /* 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 = mm_kernel_device_get_name (kernel_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, kernel_device, TRUE, FALSE); |
| else if (g_str_equal (action, "remove")) |
| device_removed (self, kernel_device); |
| |
| g_object_unref (kernel_device); |
| } |
| |
| typedef struct { |
| MMBaseManager *self; |
| GUdevDevice *device; |
| gboolean manual_scan; |
| } StartDeviceAdded; |
| |
| static gboolean |
| start_device_added_idle (StartDeviceAdded *ctx) |
| { |
| MMKernelDevice *kernel_device; |
| |
| kernel_device = mm_kernel_device_udev_new (ctx->device); |
| device_added (ctx->self, kernel_device, FALSE, ctx->manual_scan); |
| g_object_unref (kernel_device); |
| |
| g_object_unref (ctx->self); |
| g_object_unref (ctx->device); |
| g_slice_free (StartDeviceAdded, ctx); |
| return G_SOURCE_REMOVE; |
| } |
| |
| static void |
| start_device_added (MMBaseManager *self, |
| GUdevDevice *device, |
| gboolean manual_scan) |
| { |
| StartDeviceAdded *ctx; |
| |
| ctx = g_slice_new (StartDeviceAdded); |
| ctx->self = g_object_ref (self); |
| ctx->device = g_object_ref (device); |
| ctx->manual_scan = manual_scan; |
| g_idle_add ((GSourceFunc)start_device_added_idle, ctx); |
| } |
| |
| static void |
| process_scan (MMBaseManager *self, |
| gboolean manual_scan) |
| { |
| GList *devices, *iter; |
| |
| devices = g_udev_client_query_by_subsystem (self->priv->udev, "tty"); |
| for (iter = devices; iter; iter = g_list_next (iter)) { |
| start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); |
| g_object_unref (G_OBJECT (iter->data)); |
| } |
| g_list_free (devices); |
| |
| devices = g_udev_client_query_by_subsystem (self->priv->udev, "net"); |
| for (iter = devices; iter; iter = g_list_next (iter)) { |
| start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); |
| g_object_unref (G_OBJECT (iter->data)); |
| } |
| g_list_free (devices); |
| |
| devices = g_udev_client_query_by_subsystem (self->priv->udev, "usb"); |
| for (iter = devices; iter; iter = g_list_next (iter)) { |
| const gchar *name; |
| |
| name = g_udev_device_get_name (G_UDEV_DEVICE (iter->data)); |
| if (name && g_str_has_prefix (name, "cdc-wdm")) |
| start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); |
| g_object_unref (G_OBJECT (iter->data)); |
| } |
| g_list_free (devices); |
| |
| /* Newer kernels report 'usbmisc' subsystem */ |
| devices = g_udev_client_query_by_subsystem (self->priv->udev, "usbmisc"); |
| for (iter = devices; iter; iter = g_list_next (iter)) { |
| const gchar *name; |
| |
| name = g_udev_device_get_name (G_UDEV_DEVICE (iter->data)); |
| if (name && g_str_has_prefix (name, "cdc-wdm")) |
| start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan); |
| g_object_unref (G_OBJECT (iter->data)); |
| } |
| g_list_free (devices); |
| } |
| |
| #endif |
| |
| static void |
| process_initial_kernel_events (MMBaseManager *self) |
| { |
| gchar *contents = NULL; |
| gchar *line; |
| GError *error = NULL; |
| |
| if (!self->priv->initial_kernel_events) |
| return; |
| |
| if (!g_file_get_contents (self->priv->initial_kernel_events, &contents, NULL, &error)) { |
| g_warning ("Couldn't load initial kernel events: %s", error->message); |
| g_error_free (error); |
| return; |
| } |
| |
| line = contents; |
| while (line) { |
| gchar *next; |
| |
| next = strchr (line, '\n'); |
| if (next) { |
| *next = '\0'; |
| next++; |
| } |
| |
| /* ignore empty lines */ |
| if (line[0] != '\0') { |
| MMKernelEventProperties *properties; |
| |
| properties = mm_kernel_event_properties_new_from_string (line, &error); |
| if (!properties) { |
| g_warning ("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)) { |
| g_warning ("Couldn't process line '%s' as initial kernel event %s", line, error->message); |
| g_clear_error (&error); |
| } else |
| g_debug ("Processed initial kernel event:' %s'", line); |
| g_clear_object (&properties); |
| } |
| |
| line = next; |
| } |
| |
| g_free (contents); |
| } |
| |
| 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; |
| } |
| |
| #if defined WITH_UDEV |
| mm_dbg ("Starting %s device scan...", manual_scan ? "manual" : "automatic"); |
| process_scan (self, manual_scan); |
| mm_dbg ("Finished device scan..."); |
| #else |
| mm_dbg ("Unsupported %s device scan...", manual_scan ? "manual" : "automatic"); |
| #endif |
| } |
| |
| /*****************************************************************************/ |
| |
| static void |
| remove_disable_ready (MMBaseModem *modem, |
| GAsyncResult *res, |
| MMBaseManager *self) |
| { |
| MMDevice *device; |
| |
| /* We don't care about errors disabling at this point */ |
| mm_base_modem_disable_finish (modem, res, NULL); |
| |
| 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, mm_device_get_uid (device)); |
| } |
| } |
| |
| static void |
| foreach_disable (gpointer key, |
| MMDevice *device, |
| MMBaseManager *self) |
| { |
| MMBaseModem *modem; |
| |
| modem = mm_device_peek_modem (device); |
| if (modem) |
| 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, |
| gboolean disable) |
| { |
| g_return_if_fail (self != NULL); |
| g_return_if_fail (MM_IS_BASE_MANAGER (self)); |
| |
| /* Cancel all ongoing auth requests */ |
| g_cancellable_cancel (self->priv->authp_cancellable); |
| |
| 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; |
| } |
| |
| /* Otherwise, just remove directly */ |
| g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, self); |
| } |
| |
| guint32 |
| mm_base_manager_num_modems (MMBaseManager *self) |
| { |
| GHashTableIter iter; |
| gpointer key, value; |
| guint32 n; |
| |
| g_return_val_if_fail (self != NULL, 0); |
| g_return_val_if_fail (MM_IS_BASE_MANAGER (self), 0); |
| |
| n = 0; |
| g_hash_table_iter_init (&iter, self->priv->devices); |
| while (g_hash_table_iter_next (&iter, &key, &value)) { |
| n += !!mm_device_peek_modem (MM_DEVICE (value)); |
| } |
| |
| return n; |
| } |
| |
| /*****************************************************************************/ |
| /* Set logging */ |
| |
| typedef struct { |
| MMBaseManager *self; |
| GDBusMethodInvocation *invocation; |
| gchar *level; |
| } SetLoggingContext; |
| |
| static void |
| set_logging_context_free (SetLoggingContext *ctx) |
| { |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_free (ctx->level); |
| g_free (ctx); |
| } |
| |
| static void |
| 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)) |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| else { |
| mm_info ("logging: level '%s'", ctx->level); |
| mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging ( |
| MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self), |
| ctx->invocation); |
| } |
| |
| set_logging_context_free (ctx); |
| } |
| |
| static gboolean |
| handle_set_logging (MmGdbusOrgFreedesktopModemManager1 *manager, |
| GDBusMethodInvocation *invocation, |
| const gchar *level) |
| { |
| SetLoggingContext *ctx; |
| |
| ctx = g_new0 (SetLoggingContext, 1); |
| ctx->self = g_object_ref (manager); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->level = g_strdup (level); |
| |
| mm_auth_provider_authorize (ctx->self->priv->authp, |
| invocation, |
| MM_AUTHORIZATION_MANAGER_CONTROL, |
| ctx->self->priv->authp_cancellable, |
| (GAsyncReadyCallback)set_logging_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| /* Manual scan */ |
| |
| typedef struct { |
| MMBaseManager *self; |
| GDBusMethodInvocation *invocation; |
| } ScanDevicesContext; |
| |
| static void |
| scan_devices_context_free (ScanDevicesContext *ctx) |
| { |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_free (ctx); |
| } |
| |
| static void |
| scan_devices_auth_ready (MMAuthProvider *authp, |
| GAsyncResult *res, |
| ScanDevicesContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_auth_provider_authorize_finish (authp, res, &error)) |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| else { |
| #if defined WITH_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 |
| g_dbus_method_invocation_return_error_literal ( |
| ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, |
| "Cannot request manual scan of devices: unsupported"); |
| #endif |
| } |
| |
| scan_devices_context_free (ctx); |
| } |
| |
| static gboolean |
| handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager, |
| GDBusMethodInvocation *invocation) |
| { |
| ScanDevicesContext *ctx; |
| |
| ctx = g_new (ScanDevicesContext, 1); |
| ctx->self = g_object_ref (manager); |
| ctx->invocation = g_object_ref (invocation); |
| |
| mm_auth_provider_authorize (ctx->self->priv->authp, |
| invocation, |
| MM_AUTHORIZATION_MANAGER_CONTROL, |
| ctx->self->priv->authp_cancellable, |
| (GAsyncReadyCallback)scan_devices_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| 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 (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) |
| 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 = 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, |
| 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_slice_new0 (InhibitDeviceContext); |
| 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 */ |
| |
| static gboolean |
| handle_set_profile (MmGdbusTest *skeleton, |
| GDBusMethodInvocation *invocation, |
| const gchar *id, |
| const gchar *plugin_name, |
| const gchar *const *ports, |
| MMBaseManager *self) |
| { |
| MMPlugin *plugin; |
| MMDevice *device; |
| gchar *physdev_uid; |
| GError *error = NULL; |
| |
| mm_info ("Test profile set to: '%s'", id); |
| |
| /* Create device and keep it listed in the Manager */ |
| physdev_uid = g_strdup_printf ("/virtual/%s", id); |
| device = mm_device_new (physdev_uid, TRUE, TRUE); |
| g_hash_table_insert (self->priv->devices, physdev_uid, device); |
| |
| /* Grab virtual ports */ |
| mm_device_virtual_grab_ports (device, (const gchar **)ports); |
| |
| /* Set plugin to use */ |
| plugin = mm_plugin_manager_peek_plugin (self->priv->plugin_manager, plugin_name); |
| if (!plugin) { |
| error = g_error_new (MM_CORE_ERROR, |
| MM_CORE_ERROR_NOT_FOUND, |
| "Requested plugin '%s' not found", |
| plugin_name); |
| mm_warn ("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 '%s': %s", |
| mm_device_get_uid (device), |
| error->message); |
| goto out; |
| } |
| |
| mm_info ("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_uid (device)); |
| g_dbus_method_invocation_return_gerror (invocation, error); |
| g_error_free (error); |
| } else |
| mm_gdbus_test_complete_set_profile (skeleton, invocation); |
| |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| MMBaseManager * |
| 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_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, |
| const GValue *value, |
| GParamSpec *pspec) |
| { |
| MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv; |
| |
| switch (prop_id) { |
| case PROP_CONNECTION: { |
| gboolean had_connection = FALSE; |
| |
| if (priv->connection) { |
| had_connection = TRUE; |
| g_object_unref (priv->connection); |
| } |
| 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 (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)); |
| } |
| } |
| break; |
| } |
| case PROP_AUTO_SCAN: |
| priv->auto_scan = g_value_get_boolean (value); |
| break; |
| case PROP_FILTER_POLICY: |
| priv->filter_policy = g_value_get_flags (value); |
| break; |
| case PROP_ENABLE_TEST: |
| 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); |
| break; |
| case PROP_INITIAL_KERNEL_EVENTS: |
| g_free (priv->initial_kernel_events); |
| priv->initial_kernel_events = g_value_dup_string (value); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| get_property (GObject *object, |
| guint prop_id, |
| GValue *value, |
| GParamSpec *pspec) |
| { |
| MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv; |
| |
| switch (prop_id) { |
| case PROP_CONNECTION: |
| g_value_set_object (value, priv->connection); |
| break; |
| case PROP_AUTO_SCAN: |
| g_value_set_boolean (value, priv->auto_scan); |
| break; |
| case PROP_FILTER_POLICY: |
| g_value_set_flags (value, priv->filter_policy); |
| break; |
| case PROP_ENABLE_TEST: |
| g_value_set_boolean (value, priv->enable_test); |
| break; |
| case PROP_PLUGIN_DIR: |
| g_value_set_string (value, priv->plugin_dir); |
| break; |
| case PROP_INITIAL_KERNEL_EVENTS: |
| g_value_set_string (value, priv->initial_kernel_events); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| mm_base_manager_init (MMBaseManager *manager) |
| { |
| MMBaseManagerPrivate *priv; |
| |
| /* Setup private data */ |
| manager->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, |
| MM_TYPE_BASE_MANAGER, |
| MMBaseManagerPrivate); |
| |
| /* Setup authorization provider */ |
| priv->authp = mm_auth_get_provider (); |
| 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); |
| |
| /* 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 |
| { |
| const gchar *subsys[5] = { "tty", "net", "usb", "usbmisc", NULL }; |
| |
| /* Setup UDev client */ |
| priv->udev = g_udev_client_new (subsys); |
| } |
| #endif |
| |
| /* By default, enable autoscan */ |
| priv->auto_scan = TRUE; |
| |
| /* By default, no test interface */ |
| priv->enable_test = FALSE; |
| |
| /* Setup Object Manager Server */ |
| priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH); |
| |
| /* Enable processing of input DBus messages */ |
| g_object_connect (manager, |
| "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) |
| { |
| MMBaseManagerPrivate *priv = MM_BASE_MANAGER (initable)->priv; |
| |
| #if defined WITH_UDEV |
| /* If autoscan enabled, list for udev events */ |
| if (priv->auto_scan) |
| g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), initable); |
| #endif |
| |
| /* Create filter */ |
| priv->filter = mm_filter_new (priv->filter_policy, error); |
| if (!priv->filter) |
| return FALSE; |
| |
| /* Create plugin manager */ |
| priv->plugin_manager = mm_plugin_manager_new (priv->plugin_dir, priv->filter, error); |
| if (!priv->plugin_manager) |
| return FALSE; |
| |
| /* Export the manager interface */ |
| if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (initable), |
| 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); |
| |
| /* 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, |
| "handle-set-profile", |
| G_CALLBACK (handle_set_profile), |
| initable); |
| if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->test_skeleton), |
| priv->connection, |
| MM_DBUS_PATH, |
| error)) |
| return FALSE; |
| } |
| |
| /* All good */ |
| return TRUE; |
| } |
| |
| static void |
| finalize (GObject *object) |
| { |
| MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv; |
| |
| g_free (priv->initial_kernel_events); |
| g_free (priv->plugin_dir); |
| |
| g_hash_table_destroy (priv->inhibited_devices); |
| g_hash_table_destroy (priv->devices); |
| |
| #if defined WITH_UDEV |
| if (priv->udev) |
| g_object_unref (priv->udev); |
| #endif |
| |
| if (priv->filter) |
| g_object_unref (priv->filter); |
| |
| if (priv->plugin_manager) |
| g_object_unref (priv->plugin_manager); |
| |
| if (priv->object_manager) |
| g_object_unref (priv->object_manager); |
| |
| if (priv->test_skeleton) |
| g_object_unref (priv->test_skeleton); |
| |
| if (priv->connection) |
| g_object_unref (priv->connection); |
| |
| if (priv->authp) |
| g_object_unref (priv->authp); |
| |
| if (priv->authp_cancellable) |
| g_object_unref (priv->authp_cancellable); |
| |
| G_OBJECT_CLASS (mm_base_manager_parent_class)->finalize (object); |
| } |
| |
| static void |
| initable_iface_init (GInitableIface *iface) |
| { |
| iface->init = initable_init; |
| } |
| |
| static void |
| mm_base_manager_class_init (MMBaseManagerClass *manager_class) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (manager_class); |
| |
| g_type_class_add_private (object_class, sizeof (MMBaseManagerPrivate)); |
| |
| /* Virtual methods */ |
| object_class->set_property = set_property; |
| object_class->get_property = get_property; |
| object_class->finalize = finalize; |
| |
| /* Properties */ |
| |
| g_object_class_install_property |
| (object_class, PROP_CONNECTION, |
| g_param_spec_object (MM_BASE_MANAGER_CONNECTION, |
| "Connection", |
| "GDBus connection to the system bus.", |
| G_TYPE_DBUS_CONNECTION, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); |
| |
| g_object_class_install_property |
| (object_class, PROP_AUTO_SCAN, |
| g_param_spec_boolean (MM_BASE_MANAGER_AUTO_SCAN, |
| "Auto scan", |
| "Automatically look for new devices", |
| 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, |
| "Enable tests", |
| "Enable the Test interface", |
| FALSE, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); |
| |
| g_object_class_install_property |
| (object_class, PROP_PLUGIN_DIR, |
| g_param_spec_string (MM_BASE_MANAGER_PLUGIN_DIR, |
| "Plugin directory", |
| "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)); |
| } |