/* -*- 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 */ #include #include #include #include #if defined WITH_QMI # include #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 #include #include #include "mm-error-helpers.h" #include #if defined WITH_TESTS # include #endif #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-provider.h" #include "mm-plugin.h" #include "mm-filter.h" #include "mm-log-object.h" #include "mm-base-modem.h" 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 (MM_TYPE_LOG_OBJECT, log_object_iface_init)) enum { PROP_0, PROP_CONNECTION, PROP_AUTO_SCAN, PROP_FILTER_POLICY, #if !defined WITH_BUILTIN_PLUGINS PROP_PLUGIN_DIR, #endif PROP_INITIAL_KERNEL_EVENTS, #if defined WITH_TESTS PROP_ENABLE_TEST, #endif 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; #if !defined WITH_BUILTIN_PLUGINS /* Path to look for plugins */ gchar *plugin_dir; #endif /* 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; #if defined WITH_TESTS /* Whether the test interface is enabled */ gboolean enable_test; /* The Test interface support */ MmGdbusTest *test_skeleton; #endif #if defined WITH_UDEV /* The UDev client */ GUdevClient *udev; #endif #if defined WITH_QRTR /* The Qrtr Bus Watcher */ MMQrtrBusWatcher *qrtr_bus_watcher; #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_port_name (MMBaseManager *manager, const gchar *subsystem, const gchar *name) { 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_physdev_uid (MMBaseManager *self, const gchar *physdev_uid) { return g_hash_table_lookup (self->priv->devices, physdev_uid); } /*****************************************************************************/ 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_obj_msg (ctx->self, "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, &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); 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_msg (ctx->self, "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, const gchar *subsystem, const gchar *name); static void device_removed (MMBaseManager *self, const gchar *subsystem, const gchar *name) { g_autoptr(MMDevice) device = NULL; 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; } /* 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_msg (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 *self, MMKernelDevice *port, gboolean hotplugged, gboolean manual_scan) { MMDevice *device; const gchar *physdev_uid; const gchar *name; g_return_if_fail (port != NULL); name = mm_kernel_device_get_name (port); 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 * 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 (self, mm_kernel_device_get_subsystem (port), name); mm_obj_dbg (self, "port %s not candidate", 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 (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; } /* Run port filter */ if (!mm_filter_port (self->priv->filter, port, manual_scan)) return; /* 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_physdev_uid (self, physdev_uid); if (!device) { const gchar *physdev; FindDeviceSupportContext *ctx; mm_obj_dbg (self, "port %s is first in device %s", name, physdev_uid); physdev = mm_kernel_device_get_physdev_sysfs_path (port); /* Keep the device listed in the Manager */ device = mm_device_new (physdev_uid, physdev, 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 (self); ctx->device = g_object_ref (device); mm_plugin_manager_device_support_check ( self->priv->plugin_manager, device, (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); } #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 handle_qrtr_device_removed (MMBaseManager *self, guint node_id) { 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; 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; } 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; } 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 (MMBaseManager *self, const gchar *action, GUdevDevice *device) { const gchar *subsystem; const gchar *name; 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 { MMBaseManager *self; GUdevDevice *device; gboolean manual_scan; } StartDeviceAdded; static gboolean start_device_added_idle (StartDeviceAdded *ctx) { 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 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) { 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); } } #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)) { mm_obj_warn (self, "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) { 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); } 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 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"); } /*****************************************************************************/ 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; } /*****************************************************************************/ /* Quick resume synchronization */ #if defined WITH_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_msg (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 { 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)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else if (!mm_log_set_level (ctx->level, &error)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { mm_obj_msg (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); } 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 = MM_BASE_MANAGER (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)) mm_dbus_method_invocation_take_error (ctx->invocation, error); else { #if defined WITH_UDEV if (!mm_context_get_test_no_udev ()) { mm_obj_info (ctx->self, "processing user request to launch 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 mm_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); } static gboolean handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager, GDBusMethodInvocation *invocation) { ScanDevicesContext *ctx; ctx = g_new (ScanDevicesContext, 1); ctx->self = MM_BASE_MANAGER (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 (!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); mm_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 any port info exists, we require explicit port probing that will be * triggered via the artificial port notifications emitted with the * device_added() calls */ if (port_infos) { GList *l; /* A device may exist at this point if e.g. not all ports were * removed during the inhibition (i.e. the MMDevice was never fully * removed) and new ports were then added while inhibited. In this * case, we must fake all ports going away so that the MMDevice gets * completely removed, otherwise the plugin manager won't start a new * device probing task and therefore no port probing tasks. */ if (device) { GList *readded_port_infos = NULL; GList *leftover_ports; /* Create a new list of inhibited device port infos from the existing * port probes */ leftover_ports = mm_device_peek_port_probe_list (device); for (l = leftover_ports; l; l = g_list_next (l)) { InhibitedDevicePortInfo *port_info; MMPortProbe *port_probe; port_probe = MM_PORT_PROBE (l->data); port_info = g_slice_new0 (InhibitedDevicePortInfo); port_info->kernel_port = mm_port_probe_get_port (port_probe); port_info->manual_scan = TRUE; readded_port_infos = g_list_append (readded_port_infos, port_info); } /* Now, explicitly request to remove all ports, the device should go * away as well while doing so. */ for (l = readded_port_infos; l; l = g_list_next (l)) { InhibitedDevicePortInfo *port_info; port_info = (InhibitedDevicePortInfo *)(l->data); mm_obj_msg (self, "fake releasing port %s/%s during uninhibition...", mm_kernel_device_get_subsystem (port_info->kernel_port), mm_kernel_device_get_name (port_info->kernel_port)); device_removed (self, mm_kernel_device_get_subsystem (port_info->kernel_port), mm_kernel_device_get_name (port_info->kernel_port)); } /* At this point, the device should have gone completely */ g_assert (!find_device_by_physdev_uid (self, uid)); /* Added the ports to re-add in the pending list */ port_infos = g_list_concat (port_infos, readded_port_infos); } /* 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); return; } /* The device may be totally gone from the system while we were * keeping the inhibition, so do not error out if not found. */ 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_msg (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)) { mm_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_msg (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) { mm_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)) { mm_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_obj_info (ctx->self, "processing user request to inhibit uid '%s'", ctx->uid); 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)) { mm_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, "processing user request to uninhibit uid '%s'", ctx->uid); remove_device_inhibition (ctx->self, ctx->uid); mm_obj_msg (ctx->self, "device inhibition teardown 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 inhibit_device_auth_ready (MMAuthProvider *authp, GAsyncResult *res, InhibitDeviceContext *ctx) { GError *error = NULL; if (!mm_auth_provider_authorize_finish (authp, res, &error)) { mm_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 */ #if defined WITH_TESTS 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; gchar *physdev = NULL; GError *error = NULL; mm_obj_msg (self, "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, physdev, 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); /* 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_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, &error)) { mm_obj_warn (self, "couldn't create modem for virtual device '%s': %s", mm_device_get_uid (device), error->message); goto out; } mm_obj_msg (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_uid (device)); mm_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); } else mm_gdbus_test_complete_set_profile (skeleton, invocation); return TRUE; } #endif /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup ("base-manager"); } /*****************************************************************************/ MMBaseManager * mm_base_manager_new (GDBusConnection *connection, #if !defined WITH_BUILTIN_PLUGINS const gchar *plugin_dir, #endif gboolean auto_scan, MMFilterRule filter_policy, const gchar *initial_kernel_events, #if defined WITH_TESTS gboolean enable_test, #endif 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, #if !defined WITH_BUILTIN_PLUGINS MM_BASE_MANAGER_PLUGIN_DIR, plugin_dir, #endif MM_BASE_MANAGER_AUTO_SCAN, auto_scan, MM_BASE_MANAGER_FILTER_POLICY, filter_policy, MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS, initial_kernel_events, "version", MM_DIST_VERSION, #if defined WITH_TESTS MM_BASE_MANAGER_ENABLE_TEST, enable_test, #endif NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBaseManager *self = MM_BASE_MANAGER (object); switch (prop_id) { case PROP_CONNECTION: { gboolean had_connection = FALSE; if (self->priv->connection) { had_connection = TRUE; g_object_unref (self->priv->connection); } self->priv->connection = g_value_dup_object (value); /* Propagate connection loss to subobjects */ 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 defined WITH_TESTS 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)); } #endif } break; } case PROP_AUTO_SCAN: self->priv->auto_scan = g_value_get_boolean (value); break; case PROP_FILTER_POLICY: self->priv->filter_policy = g_value_get_flags (value); break; #if !defined WITH_BUILTIN_PLUGINS case PROP_PLUGIN_DIR: g_free (self->priv->plugin_dir); self->priv->plugin_dir = g_value_dup_string (value); break; #endif case PROP_INITIAL_KERNEL_EVENTS: g_free (self->priv->initial_kernel_events); self->priv->initial_kernel_events = g_value_dup_string (value); break; #if defined WITH_TESTS case PROP_ENABLE_TEST: self->priv->enable_test = g_value_get_boolean (value); break; #endif 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) { MMBaseManager *self = MM_BASE_MANAGER (object); switch (prop_id) { case PROP_CONNECTION: g_value_set_object (value, self->priv->connection); break; case PROP_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; #if !defined WITH_BUILTIN_PLUGINS case PROP_PLUGIN_DIR: g_value_set_string (value, self->priv->plugin_dir); break; #endif case PROP_INITIAL_KERNEL_EVENTS: g_value_set_string (value, self->priv->initial_kernel_events); break; #if defined WITH_TESTS case PROP_ENABLE_TEST: g_value_set_boolean (value, self->priv->enable_test); break; #endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_base_manager_init (MMBaseManager *self) { /* Setup private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_MANAGER, MMBaseManagerPrivate); /* Setup authorization provider */ self->priv->authp = mm_auth_provider_get (); self->priv->authp_cancellable = g_cancellable_new (); /* Setup internal lists of device objects */ self->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); /* 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 */ self->priv->auto_scan = TRUE; #if defined WITH_TESTS /* By default, no test interface */ self->priv->enable_test = FALSE; #endif /* Setup Object Manager Server */ self->priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH); /* Enable processing of input DBus messages */ 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) { MMBaseManager *self = MM_BASE_MANAGER (initable); /* Create filter */ self->priv->filter = mm_filter_new (self->priv->filter_policy, error); if (!self->priv->filter) return FALSE; /* Create plugin manager */ self->priv->plugin_manager = mm_plugin_manager_new (self->priv->filter, #if !defined WITH_BUILTIN_PLUGINS self->priv->plugin_dir, #endif 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), self->priv->connection, MM_DBUS_PATH, error)) return FALSE; /* Export the Object Manager interface */ g_dbus_object_manager_server_set_connection (self->priv->object_manager, self->priv->connection); #if defined WITH_TESTS /* Setup the Test skeleton and export the interface */ 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 (self->priv->test_skeleton), self->priv->connection, MM_DBUS_PATH, error)) return FALSE; } #endif /* All good */ return TRUE; } static void finalize (GObject *object) { MMBaseManager *self = MM_BASE_MANAGER (object); g_free (self->priv->initial_kernel_events); #if !defined WITH_BUILTIN_PLUGINS g_free (self->priv->plugin_dir); #endif g_hash_table_destroy (self->priv->inhibited_devices); g_hash_table_destroy (self->priv->devices); #if defined WITH_UDEV if (self->priv->udev) g_object_unref (self->priv->udev); #endif #if defined WITH_QRTR if (self->priv->qrtr_bus_watcher) g_object_unref (self->priv->qrtr_bus_watcher); #endif if (self->priv->filter) g_object_unref (self->priv->filter); if (self->priv->plugin_manager) g_object_unref (self->priv->plugin_manager); if (self->priv->object_manager) g_object_unref (self->priv->object_manager); #if defined WITH_TESTS if (self->priv->test_skeleton) g_object_unref (self->priv->test_skeleton); #endif 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); } static void initable_iface_init (GInitableIface *iface) { iface->init = initable_init; } 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); 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)); #if !defined WITH_BUILTIN_PLUGINS 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)); #endif 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)); #if defined WITH_TESTS 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)); #endif }