mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-24 19:18:14 +02:00
Avoid g_ascii_strtoull() calling directly. It has subtle issues, which is why we have a wrapper for it.
2911 lines
94 KiB
C
2911 lines
94 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2013 - 2014 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nm-bluez-manager.h"
|
|
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <gmodule.h>
|
|
|
|
#include "nm-glib-aux/nm-dbus-aux.h"
|
|
#include "nm-glib-aux/nm-c-list.h"
|
|
#include "nm-dbus-manager.h"
|
|
#include "devices/nm-device-factory.h"
|
|
#include "devices/nm-device-bridge.h"
|
|
#include "nm-setting-bluetooth.h"
|
|
#include "settings/nm-settings.h"
|
|
#include "nm-bluez-common.h"
|
|
#include "nm-device-bt.h"
|
|
#include "nm-manager.h"
|
|
#include "nm-bluez5-dun.h"
|
|
#include "nm-core-internal.h"
|
|
#include "platform/nm-platform.h"
|
|
#include "nm-std-aux/nm-dbus-compat.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
#if WITH_BLUEZ5_DUN
|
|
#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_DUN
|
|
#else
|
|
#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_NONE
|
|
#endif
|
|
#define _NM_BT_CAPABILITY_SUPPORTED (NM_BT_CAPABILITY_NAP | _NM_BT_CAPABILITY_SUPPORTED_DUN)
|
|
|
|
typedef struct {
|
|
const char *bdaddr;
|
|
CList lst_head;
|
|
NMBluetoothCapabilities bt_type:8;
|
|
char bdaddr_data[];
|
|
} ConnDataHead;
|
|
|
|
typedef struct {
|
|
NMSettingsConnection *sett_conn;
|
|
ConnDataHead *cdata_hd;
|
|
CList lst;
|
|
} ConnDataElem;
|
|
|
|
typedef struct {
|
|
GCancellable *ext_cancellable;
|
|
GCancellable *int_cancellable;
|
|
NMBtVTableRegisterCallback callback;
|
|
gpointer callback_user_data;
|
|
gulong ext_cancelled_id;
|
|
} NetworkServerRegisterReqData;
|
|
|
|
typedef struct {
|
|
GCancellable *ext_cancellable;
|
|
GCancellable *int_cancellable;
|
|
NMBluezManagerConnectCb callback;
|
|
gpointer callback_user_data;
|
|
char *device_name;
|
|
gulong ext_cancelled_id;
|
|
guint timeout_id;
|
|
guint timeout_wait_connect_id;
|
|
} DeviceConnectReqData;
|
|
|
|
typedef struct {
|
|
const char *object_path;
|
|
|
|
NMBluezManager *self;
|
|
|
|
/* Fields name with "d_" prefix are purely cached values from BlueZ's
|
|
* ObjectManager D-Bus interface. There is no logic whatsoever about
|
|
* them.
|
|
*/
|
|
|
|
CList process_change_lst;
|
|
|
|
struct {
|
|
char *address;
|
|
} d_adapter;
|
|
|
|
struct {
|
|
char *address;
|
|
char *name;
|
|
char *adapter;
|
|
} d_device;
|
|
|
|
struct {
|
|
char *interface;
|
|
} d_network;
|
|
|
|
struct {
|
|
CList lst;
|
|
char *adapter_address;
|
|
NMDevice *device_br;
|
|
NetworkServerRegisterReqData *r_req_data;
|
|
} x_network_server;
|
|
|
|
struct {
|
|
NMSettingsConnection *panu_connection;
|
|
NMDeviceBt *device_bt;
|
|
DeviceConnectReqData *c_req_data;
|
|
NMBluez5DunContext *connect_dun_context;
|
|
gulong device_bt_signal_id;
|
|
} x_device;
|
|
|
|
/* indicate whether the D-Bus object has the particular D-Bus interface. */
|
|
bool d_has_adapter_iface:1;
|
|
bool d_has_device_iface:1;
|
|
bool d_has_network_iface:1;
|
|
bool d_has_network_server_iface:1;
|
|
|
|
/* cached D-Bus properties for Device1 ("d_device*"). */
|
|
NMBluetoothCapabilities d_device_capabilities:6;
|
|
bool d_device_connected:1;
|
|
bool d_device_paired:1;
|
|
|
|
/* cached D-Bus properties for Network1 ("d_network*"). */
|
|
bool d_network_connected:1;
|
|
|
|
/* cached D-Bus properties for Adapter1 ("d_adapter*"). */
|
|
bool d_adapter_powered:1;
|
|
|
|
/* properties related to device ("x_device*"). */
|
|
NMBluetoothCapabilities x_device_connect_bt_type:6;
|
|
bool x_device_is_usable:1;
|
|
bool x_device_is_connected:1;
|
|
|
|
bool x_device_panu_connection_allow_create:1;
|
|
|
|
/* flag to remember last time when we checked wether the object
|
|
* was a suitable adapter that is usable to a device. */
|
|
bool was_usable_adapter_for_device_before:1;
|
|
|
|
char _object_path_intern[];
|
|
} BzDBusObj;
|
|
|
|
typedef struct {
|
|
NMManager *manager;
|
|
NMSettings *settings;
|
|
|
|
GDBusConnection *dbus_connection;
|
|
|
|
NMBtVTableNetworkServer vtable_network_server;
|
|
|
|
GCancellable *name_owner_get_cancellable;
|
|
GCancellable *get_managed_objects_cancellable;
|
|
|
|
GHashTable *bzobjs;
|
|
|
|
char *name_owner;
|
|
|
|
GHashTable *conn_data_heads;
|
|
GHashTable *conn_data_elems;
|
|
|
|
CList network_server_lst_head;
|
|
|
|
CList process_change_lst_head;
|
|
|
|
guint name_owner_changed_id;
|
|
|
|
guint managed_objects_changed_id;
|
|
|
|
guint properties_changed_id;
|
|
|
|
guint process_change_idle_id;
|
|
|
|
bool settings_registered:1;
|
|
} NMBluezManagerPrivate;
|
|
|
|
struct _NMBluezManager {
|
|
NMDeviceFactory parent;
|
|
NMBluezManagerPrivate _priv;
|
|
};
|
|
|
|
struct _NMBluezManagerClass {
|
|
NMDeviceFactoryClass parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, NM_TYPE_DEVICE_FACTORY);
|
|
|
|
#define NM_BLUEZ_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluezManager, NM_IS_BLUEZ_MANAGER)
|
|
|
|
/*****************************************************************************/
|
|
|
|
NM_DEVICE_FACTORY_DECLARE_TYPES (
|
|
NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_BNEP)
|
|
NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BLUETOOTH_SETTING_NAME)
|
|
)
|
|
|
|
G_MODULE_EXPORT NMDeviceFactory *
|
|
nm_device_factory_create (GError **error)
|
|
{
|
|
return (NMDeviceFactory *) g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define _NMLOG_DOMAIN LOGD_BT
|
|
#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez", __VA_ARGS__)
|
|
|
|
/*****************************************************************************/
|
|
|
|
static NMBluetoothCapabilities
|
|
convert_uuids_to_capabilities (const char *const*strv)
|
|
{
|
|
NMBluetoothCapabilities capabilities = NM_BT_CAPABILITY_NONE;
|
|
|
|
if (strv) {
|
|
for (; strv[0]; strv++) {
|
|
gs_free char *s_part1 = NULL;
|
|
const char *str = strv[0];
|
|
const char *s;
|
|
|
|
s = strchr (str, '-');
|
|
if (!s)
|
|
continue;
|
|
|
|
s_part1 = g_strndup (str, s - str);
|
|
switch (_nm_utils_ascii_str_to_int64 (s_part1, 16, 0, G_MAXINT, -1)) {
|
|
case 0x1103:
|
|
capabilities |= NM_BT_CAPABILITY_DUN;
|
|
break;
|
|
case 0x1116:
|
|
capabilities |= NM_BT_CAPABILITY_NAP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return capabilities;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void _cleanup_for_name_owner (NMBluezManager *self);
|
|
static void _connect_disconnect (NMBluezManager *self,
|
|
BzDBusObj *bzobj,
|
|
const char *reason);
|
|
static gboolean _bzobjs_network_server_is_usable (const BzDBusObj *bzobj,
|
|
gboolean require_powered);
|
|
static gboolean _bzobjs_is_dead (const BzDBusObj *bzobj);
|
|
static gboolean _bzobjs_device_is_usable (const BzDBusObj *bzobj,
|
|
BzDBusObj **out_adapter_bzobj,
|
|
gboolean *out_create_panu_connection);
|
|
static gboolean _bzobjs_adapter_is_usable_for_device (const BzDBusObj *bzobj);
|
|
static ConnDataHead *_conn_track_find_head (NMBluezManager *self,
|
|
NMBluetoothCapabilities bt_type,
|
|
const char *bdaddr);
|
|
static void _process_change_idle_schedule (NMBluezManager *self,
|
|
BzDBusObj *bzobj);
|
|
static void _network_server_unregister_bridge (NMBluezManager *self,
|
|
BzDBusObj *bzobj,
|
|
const char *reason);
|
|
static gboolean _connect_timeout_wait_connected_cb (gpointer user_data);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_dbus_call_complete_cb_nop (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
/* we don't do anything at all. The only reason to register this
|
|
* callback is so that GDBusConnection keeps the cancellable alive
|
|
* long enough until the call completes.
|
|
*
|
|
* Note that this cancellable in turn is registered via
|
|
* nm_shutdown_wait_obj_register_*(), to block shutdown until
|
|
* we are done. */
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_network_server_register_req_data_complete (NetworkServerRegisterReqData *r_req_data,
|
|
GError *error)
|
|
{
|
|
nm_clear_g_signal_handler (r_req_data->ext_cancellable, &r_req_data->ext_cancelled_id);
|
|
|
|
nm_clear_g_cancellable (&r_req_data->int_cancellable);
|
|
|
|
if (r_req_data->callback) {
|
|
gs_free_error GError *error_cancelled = NULL;
|
|
|
|
if (g_cancellable_set_error_if_cancelled (r_req_data->ext_cancellable, &error_cancelled))
|
|
error = error_cancelled;
|
|
|
|
r_req_data->callback (error, r_req_data->callback_user_data);
|
|
}
|
|
|
|
g_object_unref (r_req_data->ext_cancellable);
|
|
nm_g_slice_free (r_req_data);
|
|
}
|
|
|
|
static void
|
|
_device_connect_req_data_complete (DeviceConnectReqData *c_req_data,
|
|
NMBluezManager *self,
|
|
const char *device_name,
|
|
GError *error)
|
|
{
|
|
nm_assert ((!!device_name) != (!!error));
|
|
|
|
nm_clear_g_signal_handler (c_req_data->ext_cancellable, &c_req_data->ext_cancelled_id);
|
|
|
|
nm_clear_g_cancellable (&c_req_data->int_cancellable);
|
|
nm_clear_g_source (&c_req_data->timeout_id);
|
|
nm_clear_g_source (&c_req_data->timeout_wait_connect_id);
|
|
|
|
if (c_req_data->callback) {
|
|
gs_free_error GError *error_cancelled = NULL;
|
|
|
|
if (g_cancellable_set_error_if_cancelled (c_req_data->ext_cancellable, &error_cancelled)) {
|
|
error = error_cancelled;
|
|
device_name = NULL;
|
|
}
|
|
|
|
c_req_data->callback (self, TRUE, device_name, error, c_req_data->callback_user_data);
|
|
}
|
|
|
|
g_object_unref (c_req_data->ext_cancellable);
|
|
nm_clear_g_free (&c_req_data->device_name);
|
|
nm_g_slice_free (c_req_data);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static BzDBusObj *
|
|
_bz_dbus_obj_new (NMBluezManager *self,
|
|
const char *object_path)
|
|
{
|
|
BzDBusObj *bzobj;
|
|
gsize l;
|
|
|
|
nm_assert (NM_IS_BLUEZ_MANAGER (self));
|
|
|
|
l = strlen (object_path) + 1;
|
|
|
|
bzobj = g_malloc (sizeof (BzDBusObj) + l);
|
|
*bzobj = (BzDBusObj) {
|
|
.object_path = bzobj->_object_path_intern,
|
|
.self = self,
|
|
.x_network_server.lst = C_LIST_INIT (bzobj->x_network_server.lst),
|
|
.process_change_lst = C_LIST_INIT (bzobj->process_change_lst),
|
|
.x_device_panu_connection_allow_create = TRUE,
|
|
};
|
|
memcpy (bzobj->_object_path_intern, object_path, l);
|
|
|
|
return bzobj;
|
|
}
|
|
|
|
static void
|
|
_bz_dbus_obj_free (BzDBusObj *bzobj)
|
|
{
|
|
nm_assert (bzobj);
|
|
nm_assert (NM_IS_BLUEZ_MANAGER (bzobj->self));
|
|
nm_assert (!bzobj->x_network_server.device_br);
|
|
nm_assert (!bzobj->x_network_server.r_req_data);
|
|
nm_assert (!bzobj->x_device.c_req_data);
|
|
|
|
c_list_unlink_stale (&bzobj->process_change_lst);
|
|
c_list_unlink_stale (&bzobj->x_network_server.lst);
|
|
g_free (bzobj->x_network_server.adapter_address);
|
|
g_free (bzobj->d_adapter.address);
|
|
g_free (bzobj->d_network.interface);
|
|
g_free (bzobj->d_device.address);
|
|
g_free (bzobj->d_device.name);
|
|
g_free (bzobj->d_device.adapter);
|
|
g_free (bzobj);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static const char *
|
|
_bzobj_to_string (const BzDBusObj *bzobj, char *buf, gsize len)
|
|
{
|
|
char *buf0 = buf;
|
|
const char *prefix = "";
|
|
gboolean device_is_usable;
|
|
gboolean create_panu_connection = FALSE;
|
|
gboolean network_server_is_usable;
|
|
char sbuf_cap[100];
|
|
|
|
if (len > 0)
|
|
buf[0] = '\0';
|
|
|
|
if (bzobj->d_has_adapter_iface) {
|
|
nm_utils_strbuf_append_str (&buf, &len, prefix);
|
|
prefix = ", ";
|
|
nm_utils_strbuf_append_str (&buf, &len, "Adapter1 {");
|
|
if (bzobj->d_adapter.address) {
|
|
nm_utils_strbuf_append (&buf, &len, " d.address: \"%s\"", bzobj->d_adapter.address);
|
|
if (bzobj->d_adapter_powered)
|
|
nm_utils_strbuf_append_str (&buf, &len, ",");
|
|
}
|
|
if (bzobj->d_adapter_powered)
|
|
nm_utils_strbuf_append (&buf, &len, " d.powered: 1");
|
|
nm_utils_strbuf_append_str (&buf, &len, " }");
|
|
}
|
|
|
|
if (bzobj->d_has_device_iface) {
|
|
const char *prefix1 = "";
|
|
|
|
nm_utils_strbuf_append_str (&buf, &len, prefix);
|
|
prefix = ", ";
|
|
nm_utils_strbuf_append_str (&buf, &len, "Device1 {");
|
|
if (bzobj->d_device.address) {
|
|
nm_utils_strbuf_append (&buf, &len, "%s d.address: \"%s\"", prefix1, bzobj->d_device.address);
|
|
prefix1 = ",";
|
|
}
|
|
if (bzobj->d_device.name) {
|
|
nm_utils_strbuf_append (&buf, &len, "%s d.name: \"%s\"", prefix1, bzobj->d_device.name);
|
|
prefix1 = ",";
|
|
}
|
|
if (bzobj->d_device.adapter) {
|
|
nm_utils_strbuf_append (&buf, &len, "%s d.adapter: \"%s\"", prefix1, bzobj->d_device.adapter);
|
|
prefix1 = ",";
|
|
}
|
|
if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
|
|
nm_utils_strbuf_append (&buf, &len, "%s d.capabilities: \"%s\"",
|
|
prefix1,
|
|
nm_bluetooth_capability_to_string (bzobj->d_device_capabilities, sbuf_cap, sizeof (sbuf_cap)));
|
|
prefix1 = ",";
|
|
}
|
|
if (bzobj->d_device_connected) {
|
|
nm_utils_strbuf_append (&buf, &len, "%s d.connected: 1", prefix1);
|
|
prefix1 = ",";
|
|
}
|
|
if (bzobj->d_device_paired) {
|
|
nm_utils_strbuf_append (&buf, &len, "%s d.paired: 1", prefix1);
|
|
prefix1 = ",";
|
|
}
|
|
nm_utils_strbuf_append_str (&buf, &len, " }");
|
|
}
|
|
|
|
network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
|
|
|
|
if ( bzobj->d_has_network_server_iface
|
|
|| network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst))
|
|
|| !c_list_is_empty (&bzobj->x_network_server.lst)
|
|
|| !nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL, bzobj->x_network_server.adapter_address)
|
|
|| bzobj->x_network_server.device_br
|
|
|| bzobj->x_network_server.r_req_data) {
|
|
|
|
nm_utils_strbuf_append_str (&buf, &len, prefix);
|
|
prefix = ", ";
|
|
|
|
nm_utils_strbuf_append (&buf, &len, "NetworkServer1 { ");
|
|
|
|
if (!bzobj->d_has_network_server_iface)
|
|
nm_utils_strbuf_append (&buf, &len, " has-d-iface: 0, ");
|
|
|
|
if (network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst)))
|
|
nm_utils_strbuf_append (&buf, &len, "usable: %d, used: %d", !!network_server_is_usable, !network_server_is_usable);
|
|
else if (network_server_is_usable)
|
|
nm_utils_strbuf_append (&buf, &len, "used: 1");
|
|
else
|
|
nm_utils_strbuf_append (&buf, &len, "usable: 0");
|
|
|
|
if (!nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL, bzobj->x_network_server.adapter_address)) {
|
|
if (bzobj->x_network_server.adapter_address)
|
|
nm_utils_strbuf_append (&buf, &len, ", adapter-address: \"%s\"", bzobj->x_network_server.adapter_address);
|
|
else
|
|
nm_utils_strbuf_append (&buf, &len, ", adapter-address: <NULL>");
|
|
}
|
|
|
|
if (bzobj->x_network_server.device_br)
|
|
nm_utils_strbuf_append (&buf, &len, ", bridge-device: 1");
|
|
|
|
if (bzobj->x_network_server.r_req_data)
|
|
nm_utils_strbuf_append (&buf, &len, ", register-in-progress: 1");
|
|
|
|
nm_utils_strbuf_append_str (&buf, &len, " }");
|
|
}
|
|
|
|
device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, &create_panu_connection);
|
|
|
|
if ( bzobj->d_has_network_iface
|
|
|| bzobj->d_network.interface
|
|
|| bzobj->d_network_connected
|
|
|| create_panu_connection
|
|
|| bzobj->x_device.panu_connection
|
|
|| device_is_usable != bzobj->x_device_is_usable
|
|
|| bzobj->x_device.device_bt
|
|
|| bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
|
|
|| bzobj->x_device.connect_dun_context
|
|
|| bzobj->x_device.c_req_data
|
|
|| bzobj->x_device_is_connected != bzobj->d_network_connected) {
|
|
|
|
nm_utils_strbuf_append_str (&buf, &len, prefix);
|
|
prefix = ", ";
|
|
nm_utils_strbuf_append_str (&buf, &len, "Network1 {");
|
|
if (bzobj->d_network.interface)
|
|
nm_utils_strbuf_append (&buf, &len, " d.interface: \"%s\", ", bzobj->d_network.interface);
|
|
if (bzobj->d_network_connected)
|
|
nm_utils_strbuf_append (&buf, &len, " d.connected: %d, ", !!bzobj->d_network_connected);
|
|
if (!bzobj->d_has_network_iface)
|
|
nm_utils_strbuf_append (&buf, &len, " has-d-iface: 0, ");
|
|
if (device_is_usable != bzobj->x_device_is_usable)
|
|
nm_utils_strbuf_append (&buf, &len, " usable: %d, used: %d", !!device_is_usable, !device_is_usable);
|
|
else if (device_is_usable)
|
|
nm_utils_strbuf_append (&buf, &len, " used: 1");
|
|
else
|
|
nm_utils_strbuf_append (&buf, &len, " usable: 0");
|
|
|
|
if (create_panu_connection)
|
|
nm_utils_strbuf_append (&buf, &len, ", create-panu-connection: 1");
|
|
|
|
if (bzobj->x_device.panu_connection)
|
|
nm_utils_strbuf_append (&buf, &len, ", has-panu-connection: 1");
|
|
|
|
if (bzobj->x_device.device_bt)
|
|
nm_utils_strbuf_append (&buf, &len, ", has-device: 1");
|
|
|
|
if ( bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
|
|
|| bzobj->x_device.connect_dun_context) {
|
|
nm_utils_strbuf_append (&buf, &len, ", connect: %s%s",
|
|
nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
|
|
bzobj->x_device.connect_dun_context ? ",with-dun-context" : "");
|
|
}
|
|
|
|
if (bzobj->x_device.c_req_data)
|
|
nm_utils_strbuf_append (&buf, &len, ", connecting: 1");
|
|
|
|
if (bzobj->x_device_is_connected != bzobj->d_network_connected)
|
|
nm_utils_strbuf_append (&buf, &len, ", connected: %d", !!bzobj->x_device_is_connected);
|
|
|
|
nm_utils_strbuf_append_str (&buf, &len, " }");
|
|
}
|
|
|
|
if (_bzobjs_is_dead (bzobj)) {
|
|
nm_utils_strbuf_append_str (&buf, &len, prefix);
|
|
prefix = ", ";
|
|
nm_utils_strbuf_append_str (&buf, &len, "dead: 1");
|
|
}
|
|
|
|
if (!c_list_is_empty (&bzobj->process_change_lst)) {
|
|
nm_utils_strbuf_append_str (&buf, &len, prefix);
|
|
prefix = ", ";
|
|
nm_utils_strbuf_append (&buf, &len, "change-pending-on-idle: 1");
|
|
}
|
|
|
|
if (_bzobjs_adapter_is_usable_for_device (bzobj) != bzobj->was_usable_adapter_for_device_before) {
|
|
nm_utils_strbuf_append_str (&buf, &len, prefix);
|
|
prefix = ", ";
|
|
nm_utils_strbuf_append (&buf, &len, "change-usable-adapter-for-device: 1");
|
|
}
|
|
|
|
return buf0;
|
|
}
|
|
|
|
#define _LOG_bzobj(bzobj, context) \
|
|
G_STMT_START { \
|
|
const BzDBusObj *const _bzobj = (bzobj); \
|
|
char _buf[500]; \
|
|
\
|
|
_LOGT ("change %-21s %s : { %s }", \
|
|
(context), \
|
|
_bzobj->object_path, \
|
|
_bzobj_to_string (_bzobj, _buf, sizeof (_buf))); \
|
|
} G_STMT_END
|
|
|
|
static gboolean
|
|
_bzobjs_is_dead (const BzDBusObj *bzobj)
|
|
{
|
|
return !bzobj->d_has_adapter_iface
|
|
&& !bzobj->d_has_device_iface
|
|
&& !bzobj->d_has_network_iface
|
|
&& !bzobj->d_has_network_server_iface
|
|
&& c_list_is_empty (&bzobj->process_change_lst);
|
|
}
|
|
|
|
static BzDBusObj *
|
|
_bzobjs_get (NMBluezManager *self, const char *object_path)
|
|
{
|
|
return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->bzobjs, &object_path);
|
|
}
|
|
|
|
static BzDBusObj *
|
|
_bzobjs_add (NMBluezManager *self,
|
|
const char *object_path)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
BzDBusObj *bzobj;
|
|
|
|
bzobj = _bz_dbus_obj_new (self, object_path);
|
|
if (!g_hash_table_add (priv->bzobjs, bzobj))
|
|
nm_assert_not_reached ();
|
|
return bzobj;
|
|
}
|
|
|
|
static void
|
|
_bzobjs_del (BzDBusObj *bzobj)
|
|
{
|
|
nm_assert (bzobj);
|
|
nm_assert (bzobj == _bzobjs_get (bzobj->self, bzobj->object_path));
|
|
|
|
if (!g_hash_table_remove (NM_BLUEZ_MANAGER_GET_PRIVATE (bzobj->self)->bzobjs, bzobj))
|
|
nm_assert_not_reached ();
|
|
}
|
|
|
|
static void
|
|
_bzobjs_del_if_dead (BzDBusObj *bzobj)
|
|
{
|
|
if (_bzobjs_is_dead (bzobj))
|
|
_bzobjs_del (bzobj);
|
|
}
|
|
|
|
static BzDBusObj *
|
|
_bzobjs_init (NMBluezManager *self, BzDBusObj **inout, const char *object_path)
|
|
{
|
|
nm_assert (NM_IS_BLUEZ_MANAGER (self));
|
|
nm_assert (object_path);
|
|
nm_assert (inout);
|
|
|
|
if (!*inout) {
|
|
*inout = _bzobjs_get (self, object_path);
|
|
if (!*inout)
|
|
*inout = _bzobjs_add (self, object_path);
|
|
}
|
|
|
|
nm_assert (nm_streq ((*inout)->object_path, object_path));
|
|
nm_assert (*inout == _bzobjs_get (self, object_path));
|
|
return *inout;
|
|
}
|
|
|
|
static gboolean
|
|
_bzobjs_adapter_is_usable_for_device (const BzDBusObj *bzobj)
|
|
{
|
|
return bzobj->d_has_adapter_iface
|
|
&& bzobj->d_adapter.address
|
|
&& bzobj->d_adapter_powered;
|
|
}
|
|
|
|
static gboolean
|
|
_bzobjs_device_is_usable (const BzDBusObj *bzobj,
|
|
BzDBusObj **out_adapter_bzobj,
|
|
gboolean *out_create_panu_connection)
|
|
{
|
|
NMBluezManager *self;
|
|
NMBluezManagerPrivate *priv;
|
|
gboolean usable_dun = FALSE;
|
|
gboolean usable_nap = FALSE;
|
|
BzDBusObj *bzobj_adapter;
|
|
gboolean create_panu_connection = FALSE;
|
|
|
|
if ( !bzobj->d_has_device_iface
|
|
|| !NM_FLAGS_ANY ((NMBluetoothCapabilities) bzobj->d_device_capabilities, _NM_BT_CAPABILITY_SUPPORTED)
|
|
|| !bzobj->d_device.name
|
|
|| !bzobj->d_device.address
|
|
|| !bzobj->d_device_paired
|
|
|| !bzobj->d_device.adapter)
|
|
goto out_unusable;
|
|
|
|
self = bzobj->self;
|
|
|
|
priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
if (!priv->settings_registered)
|
|
goto out_unusable;
|
|
|
|
bzobj_adapter = _bzobjs_get (self, bzobj->d_device.adapter);
|
|
if ( !bzobj_adapter
|
|
|| !_bzobjs_adapter_is_usable_for_device (bzobj_adapter))
|
|
goto out_unusable;
|
|
|
|
#if WITH_BLUEZ5_DUN
|
|
if (NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_DUN)) {
|
|
if (_conn_track_find_head (self, NM_BT_CAPABILITY_DUN, bzobj->d_device.address))
|
|
usable_dun = TRUE;
|
|
}
|
|
#endif
|
|
|
|
if (NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_NAP)) {
|
|
if (!bzobj->d_has_network_iface)
|
|
usable_nap = FALSE;
|
|
else if (_conn_track_find_head (self, NM_BT_CAPABILITY_NAP, bzobj->d_device.address))
|
|
usable_nap = TRUE;
|
|
else if (bzobj->x_device_panu_connection_allow_create) {
|
|
/* We didn't yet try to create a connection. Presume we are going to create
|
|
* it when the time comes... */
|
|
usable_nap = TRUE;
|
|
create_panu_connection = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( !usable_dun
|
|
&& !usable_nap) {
|
|
if ( bzobj->x_device.device_bt
|
|
&& nm_device_get_state (NM_DEVICE (bzobj->x_device.device_bt)) > NM_DEVICE_STATE_DISCONNECTED) {
|
|
/* The device is still activated... the absence of a profile does not
|
|
* render it unusable (yet). But since there is no more profile, the
|
|
* device is probably about to disconnect. */
|
|
} else
|
|
goto out_unusable;
|
|
}
|
|
|
|
NM_SET_OUT (out_create_panu_connection, create_panu_connection);
|
|
NM_SET_OUT (out_adapter_bzobj, bzobj_adapter);
|
|
return TRUE;
|
|
|
|
out_unusable:
|
|
NM_SET_OUT (out_create_panu_connection, FALSE);
|
|
NM_SET_OUT (out_adapter_bzobj, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
_bzobjs_device_is_connected (const BzDBusObj *bzobj)
|
|
{
|
|
nm_assert (_bzobjs_device_is_usable (bzobj, NULL, NULL));
|
|
|
|
if ( !bzobj->d_has_device_iface
|
|
|| !bzobj->d_device_connected)
|
|
return FALSE;
|
|
|
|
if ( bzobj->d_has_network_iface
|
|
&& bzobj->d_network_connected)
|
|
return TRUE;
|
|
if (bzobj->x_device.connect_dun_context) {
|
|
/* As long as we have a dun-context, we consider it connected.
|
|
*
|
|
* We require NMDeviceBt to try to connect to the modem, and if that fails,
|
|
* it will disconnect. */
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
_bzobjs_network_server_is_usable (const BzDBusObj *bzobj,
|
|
gboolean require_powered)
|
|
{
|
|
return bzobj->d_has_network_server_iface
|
|
&& bzobj->d_has_adapter_iface
|
|
&& bzobj->d_adapter.address
|
|
&& ( !require_powered
|
|
|| bzobj->d_adapter_powered);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static ConnDataHead *
|
|
_conn_data_head_new (NMBluetoothCapabilities bt_type,
|
|
const char *bdaddr)
|
|
{
|
|
ConnDataHead *cdata_hd;
|
|
gsize l;
|
|
|
|
nm_assert (NM_IN_SET (bt_type, NM_BT_CAPABILITY_DUN,
|
|
NM_BT_CAPABILITY_NAP));
|
|
nm_assert (bdaddr);
|
|
|
|
l = strlen (bdaddr) + 1;
|
|
cdata_hd = g_malloc (sizeof (ConnDataHead) + l);
|
|
*cdata_hd = (ConnDataHead) {
|
|
.bdaddr = cdata_hd->bdaddr_data,
|
|
.lst_head = C_LIST_INIT (cdata_hd->lst_head),
|
|
.bt_type = bt_type,
|
|
};
|
|
memcpy (cdata_hd->bdaddr_data, bdaddr, l);
|
|
|
|
nm_assert (cdata_hd->bt_type == bt_type);
|
|
|
|
return cdata_hd;
|
|
}
|
|
|
|
static guint
|
|
_conn_data_head_hash (gconstpointer ptr)
|
|
{
|
|
const ConnDataHead *cdata_hd = ptr;
|
|
NMHashState h;
|
|
|
|
nm_hash_init (&h, 520317467u);
|
|
nm_hash_update_val (&h, (NMBluetoothCapabilities) cdata_hd->bt_type);
|
|
nm_hash_update_str (&h, cdata_hd->bdaddr);
|
|
return nm_hash_complete (&h);
|
|
}
|
|
|
|
static gboolean
|
|
_conn_data_head_equal (gconstpointer a, gconstpointer b)
|
|
{
|
|
const ConnDataHead *cdata_hd_a = a;
|
|
const ConnDataHead *cdata_hd_b = b;
|
|
|
|
return cdata_hd_a->bt_type == cdata_hd_b->bt_type
|
|
&& nm_streq (cdata_hd_a->bdaddr, cdata_hd_b->bdaddr);
|
|
}
|
|
|
|
static ConnDataHead *
|
|
_conn_track_find_head (NMBluezManager *self,
|
|
NMBluetoothCapabilities bt_type,
|
|
const char *bdaddr)
|
|
{
|
|
ConnDataHead cdata_hd = {
|
|
.bt_type = bt_type,
|
|
.bdaddr = bdaddr,
|
|
};
|
|
|
|
return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->conn_data_heads, &cdata_hd);
|
|
}
|
|
|
|
static ConnDataElem *
|
|
_conn_track_find_elem (NMBluezManager *self,
|
|
NMSettingsConnection *sett_conn)
|
|
{
|
|
G_STATIC_ASSERT (G_STRUCT_OFFSET (ConnDataElem, sett_conn) == 0);
|
|
|
|
return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->conn_data_elems, &sett_conn);
|
|
}
|
|
|
|
static gboolean
|
|
_conn_track_is_relevant_connection (NMConnection *connection,
|
|
NMBluetoothCapabilities *out_bt_type,
|
|
const char **out_bdaddr)
|
|
{
|
|
NMSettingBluetooth *s_bt;
|
|
NMBluetoothCapabilities bt_type;
|
|
const char *bdaddr;
|
|
const char *b_type;
|
|
|
|
s_bt = nm_connection_get_setting_bluetooth (connection);
|
|
if (!s_bt)
|
|
return FALSE;
|
|
|
|
if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
|
|
return FALSE;
|
|
|
|
bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
|
|
if (!bdaddr)
|
|
return FALSE;
|
|
|
|
b_type = nm_setting_bluetooth_get_connection_type (s_bt);
|
|
|
|
if (nm_streq (b_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
|
|
bt_type = NM_BT_CAPABILITY_DUN;
|
|
else if (nm_streq (b_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
|
|
bt_type = NM_BT_CAPABILITY_NAP;
|
|
else
|
|
return FALSE;
|
|
|
|
NM_SET_OUT (out_bt_type, bt_type);
|
|
NM_SET_OUT (out_bdaddr, bdaddr);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_conn_track_is_relevant_sett_conn (NMSettingsConnection *sett_conn,
|
|
NMBluetoothCapabilities *out_bt_type,
|
|
const char **out_bdaddr)
|
|
{
|
|
NMConnection *connection;
|
|
|
|
connection = nm_settings_connection_get_connection (sett_conn);
|
|
if (!connection)
|
|
return FALSE;
|
|
|
|
return _conn_track_is_relevant_connection (connection, out_bt_type, out_bdaddr);
|
|
}
|
|
|
|
static gboolean
|
|
_conn_track_is_relevant_for_sett_conn (NMSettingsConnection *sett_conn,
|
|
NMBluetoothCapabilities bt_type,
|
|
const char *bdaddr)
|
|
{
|
|
NMBluetoothCapabilities x_bt_type;
|
|
const char *x_bdaddr;
|
|
|
|
return bdaddr
|
|
&& _conn_track_is_relevant_sett_conn (sett_conn, &x_bt_type, &x_bdaddr)
|
|
&& x_bt_type == bt_type
|
|
&& nm_streq (x_bdaddr, bdaddr);
|
|
}
|
|
|
|
static void
|
|
_conn_track_schedule_notify (NMBluezManager *self,
|
|
NMBluetoothCapabilities bt_type,
|
|
const char *bdaddr)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
GHashTableIter iter;
|
|
BzDBusObj *bzobj;
|
|
|
|
g_hash_table_iter_init (&iter, priv->bzobjs);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
|
|
gboolean device_is_usable;
|
|
|
|
device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
|
|
if (bzobj->x_device_is_usable != device_is_usable)
|
|
_process_change_idle_schedule (self, bzobj);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_conn_track_update (NMBluezManager *self,
|
|
NMSettingsConnection *sett_conn,
|
|
gboolean track,
|
|
gboolean *out_changed,
|
|
gboolean *out_changed_usable,
|
|
ConnDataElem **out_conn_data_elem)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
ConnDataHead *cdata_hd;
|
|
ConnDataElem *cdata_el;
|
|
ConnDataElem *cdata_el_remove = NULL;
|
|
NMBluetoothCapabilities bt_type;
|
|
const char *bdaddr;
|
|
gboolean changed = FALSE;
|
|
gboolean changed_usable = FALSE;
|
|
char sbuf_cap[100];
|
|
|
|
nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
|
|
|
|
cdata_el = _conn_track_find_elem (self, sett_conn);
|
|
|
|
if (track)
|
|
track = _conn_track_is_relevant_sett_conn (sett_conn, &bt_type, &bdaddr);
|
|
|
|
if (!track) {
|
|
cdata_el_remove = g_steal_pointer (&cdata_el);
|
|
goto out_remove;
|
|
}
|
|
|
|
if (cdata_el) {
|
|
cdata_hd = cdata_el->cdata_hd;
|
|
if ( cdata_hd->bt_type != bt_type
|
|
|| !nm_streq (cdata_hd->bdaddr, bdaddr))
|
|
cdata_el_remove = g_steal_pointer (&cdata_el);
|
|
}
|
|
|
|
if (!cdata_el) {
|
|
_LOGT ("connecton: track for %s, %s: %s (%s)",
|
|
nm_bluetooth_capability_to_string (bt_type, sbuf_cap, sizeof (sbuf_cap)),
|
|
bdaddr,
|
|
nm_settings_connection_get_uuid (sett_conn),
|
|
nm_settings_connection_get_id (sett_conn));
|
|
changed = TRUE;
|
|
cdata_hd = _conn_track_find_head (self, bt_type, bdaddr);
|
|
if (!cdata_hd) {
|
|
changed_usable = TRUE;
|
|
cdata_hd = _conn_data_head_new (bt_type, bdaddr);
|
|
if (!g_hash_table_add (priv->conn_data_heads, cdata_hd))
|
|
nm_assert_not_reached ();
|
|
_conn_track_schedule_notify (self, bt_type, bdaddr);
|
|
}
|
|
cdata_el = g_slice_new (ConnDataElem);
|
|
cdata_el->sett_conn = sett_conn;
|
|
cdata_el->cdata_hd = cdata_hd;
|
|
c_list_link_tail (&cdata_hd->lst_head, &cdata_el->lst);
|
|
if (!g_hash_table_add (priv->conn_data_elems, cdata_el))
|
|
nm_assert_not_reached ();
|
|
}
|
|
|
|
out_remove:
|
|
if (cdata_el_remove) {
|
|
GHashTableIter iter;
|
|
BzDBusObj *bzobj;
|
|
|
|
_LOGT ("connecton: untrack for %s, %s: %s (%s)",
|
|
nm_bluetooth_capability_to_string (cdata_el_remove->cdata_hd->bt_type, sbuf_cap, sizeof (sbuf_cap)),
|
|
cdata_el_remove->cdata_hd->bdaddr,
|
|
nm_settings_connection_get_uuid (sett_conn),
|
|
nm_settings_connection_get_id (sett_conn));
|
|
|
|
g_hash_table_iter_init (&iter, priv->bzobjs);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
|
|
if (bzobj->x_device.panu_connection == sett_conn)
|
|
bzobj->x_device.panu_connection = NULL;
|
|
}
|
|
|
|
changed = TRUE;
|
|
cdata_hd = cdata_el_remove->cdata_hd;
|
|
c_list_unlink_stale (&cdata_el_remove->lst);
|
|
if (!g_hash_table_remove (priv->conn_data_elems, cdata_el_remove))
|
|
nm_assert_not_reached ();
|
|
if (c_list_is_empty (&cdata_hd->lst_head)) {
|
|
changed_usable = TRUE;
|
|
_conn_track_schedule_notify (self, cdata_hd->bt_type, cdata_hd->bdaddr);
|
|
if (!g_hash_table_remove (priv->conn_data_heads, cdata_hd))
|
|
nm_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
NM_SET_OUT (out_changed, changed);
|
|
NM_SET_OUT (out_changed_usable, changed_usable);
|
|
NM_SET_OUT (out_conn_data_elem, cdata_el);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
cp_connection_added (NMSettings *settings,
|
|
NMSettingsConnection *sett_conn,
|
|
NMBluezManager *self)
|
|
{
|
|
_conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
cp_connection_updated (NMSettings *settings,
|
|
NMSettingsConnection *sett_conn,
|
|
guint update_reason_u,
|
|
NMBluezManager *self)
|
|
{
|
|
_conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
cp_connection_removed (NMSettings *settings,
|
|
NMSettingsConnection *sett_conn,
|
|
NMBluezManager *self)
|
|
{
|
|
_conn_track_update (self, sett_conn, FALSE, NULL, NULL, NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static NMBluezManager *
|
|
_network_server_get_bluez_manager (const NMBtVTableNetworkServer *vtable_network_server)
|
|
{
|
|
NMBluezManager *self;
|
|
|
|
self = (NMBluezManager *) (((char *) vtable_network_server) - G_STRUCT_OFFSET (NMBluezManager, _priv.vtable_network_server));
|
|
|
|
g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (self), NULL);
|
|
|
|
return self;
|
|
}
|
|
|
|
static BzDBusObj *
|
|
_network_server_find_has_device (NMBluezManagerPrivate *priv,
|
|
NMDevice *device)
|
|
{
|
|
BzDBusObj *bzobj;
|
|
|
|
c_list_for_each_entry (bzobj, &priv->network_server_lst_head, x_network_server.lst) {
|
|
if (bzobj->x_network_server.device_br == device)
|
|
return bzobj;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static BzDBusObj *
|
|
_network_server_find_available (NMBluezManagerPrivate *priv,
|
|
const char *addr,
|
|
NMDevice *device_accept_busy)
|
|
{
|
|
BzDBusObj *bzobj;
|
|
|
|
c_list_for_each_entry (bzobj, &priv->network_server_lst_head, x_network_server.lst) {
|
|
if (bzobj->x_network_server.device_br) {
|
|
if (bzobj->x_network_server.device_br != device_accept_busy)
|
|
continue;
|
|
}
|
|
if ( addr
|
|
&& !nm_streq (addr, bzobj->d_adapter.address))
|
|
continue;
|
|
nm_assert (!bzobj->x_network_server.r_req_data);
|
|
return bzobj;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
_network_server_vt_is_available (const NMBtVTableNetworkServer *vtable,
|
|
const char *addr,
|
|
NMDevice *device_accept_busy)
|
|
{
|
|
NMBluezManager *self = _network_server_get_bluez_manager (vtable);
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
return !!_network_server_find_available (priv, addr, device_accept_busy);
|
|
}
|
|
|
|
static void
|
|
_network_server_register_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
gs_unref_variant GVariant *ret = NULL;
|
|
gs_free_error GError *error = NULL;
|
|
BzDBusObj *bzobj;
|
|
|
|
ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
|
|
if ( !ret
|
|
&& nm_utils_error_is_cancelled (error))
|
|
return;
|
|
|
|
bzobj = user_data;
|
|
|
|
if (!ret) {
|
|
_LOGT ("NAP: [%s]: registering failed: %s", bzobj->object_path, error->message);
|
|
} else
|
|
_LOGT ("NAP: [%s]: registration successful", bzobj->object_path);
|
|
|
|
g_clear_object (&bzobj->x_network_server.r_req_data->int_cancellable);
|
|
_network_server_register_req_data_complete (g_steal_pointer (&bzobj->x_network_server.r_req_data), error);
|
|
}
|
|
|
|
static void
|
|
_network_server_register_cancelled_cb (GCancellable *cancellable,
|
|
BzDBusObj *bzobj)
|
|
{
|
|
_network_server_unregister_bridge (bzobj->self, bzobj, "registration cancelled");
|
|
}
|
|
|
|
static gboolean
|
|
_network_server_vt_register_bridge (const NMBtVTableNetworkServer *vtable,
|
|
const char *addr,
|
|
NMDevice *device,
|
|
GCancellable *cancellable,
|
|
NMBtVTableRegisterCallback callback,
|
|
gpointer callback_user_data,
|
|
GError **error)
|
|
{
|
|
NMBluezManager *self = _network_server_get_bluez_manager (vtable);
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
NetworkServerRegisterReqData *r_req_data;
|
|
BzDBusObj *bzobj;
|
|
const char *ifname;
|
|
|
|
g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
|
|
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
|
|
|
|
nm_assert (!g_cancellable_is_cancelled (cancellable));
|
|
nm_assert (!_network_server_find_has_device (priv, device));
|
|
|
|
ifname = nm_device_get_iface (device);
|
|
g_return_val_if_fail (ifname, FALSE);
|
|
|
|
g_return_val_if_fail (ifname, FALSE);
|
|
|
|
bzobj = _network_server_find_available (priv, addr, NULL);
|
|
if (!bzobj) {
|
|
/* The device checked that a network server is available, before
|
|
* starting the activation, but for some reason it no longer is.
|
|
* Indicate that the activation should not proceed. */
|
|
if (addr) {
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
|
|
"adapter %s is not available for %s",
|
|
addr, ifname);
|
|
} else {
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
|
|
"no adapter available for %s",
|
|
ifname);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
_LOGD ("NAP: [%s]: registering \"%s\" on adapter %s",
|
|
bzobj->object_path,
|
|
ifname,
|
|
bzobj->d_adapter.address);
|
|
|
|
r_req_data = g_slice_new (NetworkServerRegisterReqData);
|
|
*r_req_data = (NetworkServerRegisterReqData) {
|
|
.int_cancellable = g_cancellable_new (),
|
|
.ext_cancellable = g_object_ref (cancellable),
|
|
.callback = callback,
|
|
.callback_user_data = callback_user_data,
|
|
.ext_cancelled_id = g_signal_connect (cancellable,
|
|
"cancelled",
|
|
G_CALLBACK (_network_server_register_cancelled_cb),
|
|
bzobj),
|
|
};
|
|
|
|
bzobj->x_network_server.device_br = g_object_ref (device);
|
|
bzobj->x_network_server.r_req_data = r_req_data;
|
|
|
|
g_dbus_connection_call (priv->dbus_connection,
|
|
priv->name_owner,
|
|
bzobj->object_path,
|
|
NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
|
|
"Register",
|
|
g_variant_new ("(ss)",
|
|
BLUETOOTH_CONNECT_NAP,
|
|
ifname),
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
-1,
|
|
bzobj->x_network_server.r_req_data->int_cancellable,
|
|
_network_server_register_cb,
|
|
bzobj);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_network_server_unregister_bridge_complete_on_idle_cb (gpointer user_data,
|
|
GCancellable *cancellable)
|
|
{
|
|
gs_free_error GError *error = NULL;
|
|
gs_free char *reason = NULL;
|
|
NetworkServerRegisterReqData *r_req_data;
|
|
|
|
nm_utils_user_data_unpack (user_data, &r_req_data, &reason);
|
|
|
|
nm_utils_error_set (&error, NM_UTILS_ERROR_UNKNOWN,
|
|
"registration was aborted due to %s",
|
|
reason);
|
|
_network_server_register_req_data_complete (r_req_data, error);
|
|
}
|
|
|
|
static void
|
|
_network_server_unregister_bridge (NMBluezManager *self,
|
|
BzDBusObj *bzobj,
|
|
const char *reason)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
_nm_unused gs_unref_object NMDevice *device = NULL;
|
|
NetworkServerRegisterReqData *r_req_data;
|
|
|
|
nm_assert (NM_IS_DEVICE (bzobj->x_network_server.device_br));
|
|
|
|
_LOGD ("NAP: [%s]: unregistering \"%s\" (%s)",
|
|
bzobj->object_path,
|
|
nm_device_get_iface (bzobj->x_network_server.device_br),
|
|
reason);
|
|
|
|
device = g_steal_pointer (&bzobj->x_network_server.device_br);
|
|
|
|
r_req_data = g_steal_pointer (&bzobj->x_network_server.r_req_data);
|
|
|
|
if (priv->name_owner) {
|
|
gs_unref_object GCancellable *cancellable = NULL;
|
|
|
|
cancellable = g_cancellable_new ();
|
|
|
|
nm_shutdown_wait_obj_register_cancellable_full (cancellable,
|
|
g_strdup_printf ("bt-unregister-nap[%s]", bzobj->object_path),
|
|
TRUE);
|
|
|
|
g_dbus_connection_call (priv->dbus_connection,
|
|
priv->name_owner,
|
|
bzobj->object_path,
|
|
NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
|
|
"Unregister",
|
|
g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
-1,
|
|
cancellable,
|
|
_dbus_call_complete_cb_nop,
|
|
NULL);
|
|
}
|
|
|
|
if (r_req_data) {
|
|
nm_clear_g_cancellable (&r_req_data->int_cancellable);
|
|
nm_utils_invoke_on_idle (_network_server_unregister_bridge_complete_on_idle_cb,
|
|
nm_utils_user_data_pack (r_req_data, g_strdup (reason)),
|
|
r_req_data->ext_cancellable);
|
|
}
|
|
|
|
_nm_device_bridge_notify_unregister_bt_nap (device, reason);
|
|
}
|
|
|
|
static gboolean
|
|
_network_server_vt_unregister_bridge (const NMBtVTableNetworkServer *vtable,
|
|
NMDevice *device)
|
|
{
|
|
NMBluezManager *self = _network_server_get_bluez_manager (vtable);
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
BzDBusObj *bzobj;
|
|
|
|
g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
|
|
|
|
bzobj = _network_server_find_has_device (priv, device);
|
|
if (bzobj)
|
|
_network_server_unregister_bridge (self, bzobj, "disconnecting");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_network_server_process_change (BzDBusObj *bzobj,
|
|
gboolean *out_emit_device_availability_changed)
|
|
{
|
|
NMBluezManager *self = bzobj->self;
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
gboolean network_server_is_usable;
|
|
gboolean emit_device_availability_changed = FALSE;
|
|
|
|
network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
|
|
|
|
if (!network_server_is_usable) {
|
|
|
|
if (!c_list_is_empty (&bzobj->x_network_server.lst)) {
|
|
emit_device_availability_changed = TRUE;
|
|
c_list_unlink (&bzobj->x_network_server.lst);
|
|
}
|
|
|
|
nm_clear_g_free (&bzobj->x_network_server.adapter_address);
|
|
|
|
if (bzobj->x_network_server.device_br) {
|
|
_network_server_unregister_bridge (self,
|
|
bzobj,
|
|
_bzobjs_network_server_is_usable (bzobj, FALSE)
|
|
? "adapter disabled"
|
|
: "adapter disappeared");
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!nm_streq0 (bzobj->x_network_server.adapter_address, bzobj->d_adapter.address)) {
|
|
emit_device_availability_changed = TRUE;
|
|
g_free (bzobj->x_network_server.adapter_address);
|
|
bzobj->x_network_server.adapter_address = g_strdup (bzobj->d_adapter.address);
|
|
}
|
|
|
|
if (c_list_is_empty (&bzobj->x_network_server.lst)) {
|
|
emit_device_availability_changed = TRUE;
|
|
c_list_link_tail (&priv->network_server_lst_head, &bzobj->x_network_server.lst);
|
|
}
|
|
|
|
}
|
|
|
|
if (emit_device_availability_changed)
|
|
NM_SET_OUT (out_emit_device_availability_changed, TRUE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_conn_create_panu_connection (NMBluezManager *self,
|
|
BzDBusObj *bzobj)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
gs_unref_object NMConnection *connection = NULL;
|
|
NMSettingsConnection *added;
|
|
NMSetting *setting;
|
|
gs_free char *id = NULL;
|
|
char uuid[37];
|
|
gs_free_error GError *error = NULL;
|
|
|
|
nm_utils_uuid_generate_buf (uuid);
|
|
id = g_strdup_printf (_("%s Network"), bzobj->d_device.name);
|
|
|
|
connection = nm_simple_connection_new ();
|
|
|
|
setting = nm_setting_connection_new ();
|
|
g_object_set (setting,
|
|
NM_SETTING_CONNECTION_ID, id,
|
|
NM_SETTING_CONNECTION_UUID, uuid,
|
|
NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
|
|
NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
|
|
NULL);
|
|
nm_connection_add_setting (connection, setting);
|
|
|
|
setting = nm_setting_bluetooth_new ();
|
|
g_object_set (setting,
|
|
NM_SETTING_BLUETOOTH_BDADDR, bzobj->d_device.address,
|
|
NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
|
|
NULL);
|
|
nm_connection_add_setting (connection, setting);
|
|
|
|
if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
|
|
_LOGE ("connection: couldn't generate a connection for NAP device: %s",
|
|
error->message);
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
nm_assert (_conn_track_is_relevant_connection (connection, NULL, NULL));
|
|
|
|
_LOGT ("connection: create in-memory PANU connection %s (%s) for device \"%s\" (%s)",
|
|
uuid,
|
|
id,
|
|
bzobj->d_device.name,
|
|
bzobj->d_device.address);
|
|
|
|
nm_settings_add_connection (priv->settings,
|
|
connection,
|
|
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
|
|
NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
|
|
NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
|
|
&added,
|
|
&error);
|
|
if (!added) {
|
|
_LOGW ("connection: couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
|
|
id, uuid, error->message);
|
|
return;
|
|
}
|
|
|
|
if ( !_conn_track_is_relevant_for_sett_conn (added, NM_BT_CAPABILITY_NAP, bzobj->d_device.address)
|
|
|| !_conn_track_find_elem (self, added)
|
|
|| bzobj->x_device.panu_connection) {
|
|
_LOGE ("connection: something went wrong creating PANU connection %s (%s) for device '%s'",
|
|
uuid, id, bzobj->d_device.address);
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
bzobj->x_device.panu_connection = added;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_device_state_changed_cb (NMDevice *device,
|
|
guint new_state_u,
|
|
guint old_state_u,
|
|
guint reason_u,
|
|
gpointer user_data)
|
|
{
|
|
BzDBusObj *bzobj = user_data;
|
|
|
|
if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) {
|
|
/* the device got unusable? Need to revisit it... */
|
|
_process_change_idle_schedule (bzobj->self, bzobj);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_device_process_change (BzDBusObj *bzobj)
|
|
{
|
|
NMBluezManager *self = bzobj->self;
|
|
gs_unref_object NMDeviceBt *device_added = NULL;
|
|
gs_unref_object NMDeviceBt *device_deleted = NULL;
|
|
gboolean device_is_usable;
|
|
gboolean create_panu_connection = FALSE;
|
|
|
|
device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, &create_panu_connection);
|
|
|
|
if (create_panu_connection) {
|
|
bzobj->x_device_panu_connection_allow_create = FALSE;
|
|
_conn_create_panu_connection (self, bzobj);
|
|
device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
|
|
} else {
|
|
if ( device_is_usable
|
|
&& bzobj->x_device_panu_connection_allow_create
|
|
&& NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_NAP)
|
|
&& _conn_track_find_head (self, NM_BT_CAPABILITY_NAP, bzobj->d_device.address) ) {
|
|
/* We have a useable device and also a panu-connection. We block future attemps
|
|
* to generate a connection. */
|
|
bzobj->x_device_panu_connection_allow_create = FALSE;
|
|
}
|
|
if (bzobj->x_device.panu_connection) {
|
|
if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (bzobj->x_device.panu_connection),
|
|
NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) {
|
|
/* the connection that we generated earlier still exists, but it's not longer the same
|
|
* as it was when we created it. Forget about it, so that we don't delete the profile later... */
|
|
bzobj->x_device.panu_connection = NULL;
|
|
} else {
|
|
if ( !device_is_usable
|
|
|| !_conn_track_is_relevant_for_sett_conn (bzobj->x_device.panu_connection,
|
|
NM_BT_CAPABILITY_NAP,
|
|
bzobj->d_device.address)) {
|
|
_LOGT ("connection: delete in-memory PANU connection %s (%s) as device %s",
|
|
nm_settings_connection_get_uuid (bzobj->x_device.panu_connection),
|
|
nm_settings_connection_get_id (bzobj->x_device.panu_connection),
|
|
!device_is_usable ? "is now unusable" : "no longer matches");
|
|
bzobj->x_device_panu_connection_allow_create = TRUE;
|
|
nm_settings_connection_delete (g_steal_pointer (&bzobj->x_device.panu_connection), FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bzobj->x_device_is_connected = device_is_usable
|
|
&& _bzobjs_device_is_connected (bzobj);
|
|
|
|
bzobj->x_device_is_usable = device_is_usable;
|
|
|
|
if (bzobj->x_device.device_bt) {
|
|
const char *device_to_delete_msg;
|
|
|
|
if (!device_is_usable)
|
|
device_to_delete_msg = "device became unusable";
|
|
else if (!_nm_device_bt_for_same_device (bzobj->x_device.device_bt,
|
|
bzobj->object_path,
|
|
bzobj->d_device.address,
|
|
NULL,
|
|
bzobj->d_device_capabilities))
|
|
device_to_delete_msg = "device is no longer compatible";
|
|
else
|
|
device_to_delete_msg = NULL;
|
|
|
|
if (device_to_delete_msg) {
|
|
nm_clear_g_signal_handler (bzobj->x_device.device_bt, &bzobj->x_device.device_bt_signal_id);
|
|
|
|
device_deleted = g_steal_pointer (&bzobj->x_device.device_bt);
|
|
|
|
_LOGD ("[%s]: drop device because %s",
|
|
bzobj->object_path,
|
|
device_to_delete_msg);
|
|
|
|
_connect_disconnect (self, bzobj, device_to_delete_msg);
|
|
}
|
|
}
|
|
|
|
if (device_is_usable) {
|
|
if (!bzobj->x_device.device_bt) {
|
|
bzobj->x_device.device_bt = nm_device_bt_new (self,
|
|
bzobj->object_path,
|
|
bzobj->d_device.address,
|
|
bzobj->d_device.name,
|
|
bzobj->d_device_capabilities);
|
|
device_added = g_object_ref (bzobj->x_device.device_bt);
|
|
bzobj->x_device.device_bt_signal_id = g_signal_connect (device_added,
|
|
NM_DEVICE_STATE_CHANGED,
|
|
G_CALLBACK (_device_state_changed_cb),
|
|
bzobj);
|
|
} else
|
|
_nm_device_bt_notify_set_name (bzobj->x_device.device_bt, bzobj->d_device.name);
|
|
|
|
_nm_device_bt_notify_set_connected (bzobj->x_device.device_bt, bzobj->x_device_is_connected);
|
|
}
|
|
|
|
if ( bzobj->x_device.c_req_data
|
|
&& !bzobj->x_device.c_req_data->int_cancellable
|
|
&& bzobj->x_device_is_connected) {
|
|
gs_free char *device_name = g_steal_pointer (&bzobj->x_device.c_req_data->device_name);
|
|
|
|
_device_connect_req_data_complete (g_steal_pointer (&bzobj->x_device.c_req_data),
|
|
self,
|
|
device_name,
|
|
NULL);
|
|
}
|
|
|
|
if (device_added)
|
|
g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device_added);
|
|
|
|
if (device_deleted)
|
|
_nm_device_bt_notify_removed (device_deleted);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_process_change_idle_all (NMBluezManager *self,
|
|
gboolean *out_emit_device_availability_changed)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
BzDBusObj *bzobj;
|
|
|
|
while ((bzobj = c_list_first_entry (&priv->process_change_lst_head, BzDBusObj, process_change_lst))) {
|
|
|
|
c_list_unlink (&bzobj->process_change_lst);
|
|
|
|
_LOG_bzobj (bzobj, "before-processing");
|
|
|
|
_device_process_change (bzobj);
|
|
|
|
_network_server_process_change (bzobj, out_emit_device_availability_changed);
|
|
|
|
_LOG_bzobj (bzobj, "after-processing");
|
|
|
|
_bzobjs_del_if_dead (bzobj);
|
|
}
|
|
|
|
nm_clear_g_source (&priv->process_change_idle_id);
|
|
}
|
|
|
|
static gboolean
|
|
_process_change_idle_cb (gpointer user_data)
|
|
{
|
|
NMBluezManager *self = user_data;
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
gboolean emit_device_availability_changed = FALSE;
|
|
|
|
_process_change_idle_all (self, &emit_device_availability_changed);
|
|
|
|
if (emit_device_availability_changed)
|
|
nm_manager_notify_device_availibility_maybe_changed (priv->manager);
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
}
|
|
|
|
static void
|
|
_process_change_idle_schedule (NMBluezManager *self,
|
|
BzDBusObj *bzobj)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj->process_change_lst);
|
|
if (priv->process_change_idle_id == 0)
|
|
priv->process_change_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, _process_change_idle_cb, self, NULL);
|
|
}
|
|
|
|
static void
|
|
_dbus_process_changes (NMBluezManager *self,
|
|
BzDBusObj *bzobj,
|
|
const char *log_reason)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
gboolean network_server_is_usable;
|
|
gboolean adapter_is_usable_for_device;
|
|
gboolean device_is_usable;
|
|
gboolean changes = FALSE;
|
|
gboolean recheck_devices_for_adapter = FALSE;
|
|
|
|
nm_assert (bzobj);
|
|
|
|
_LOG_bzobj (bzobj, log_reason);
|
|
|
|
device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
|
|
|
|
if (bzobj->x_device_is_usable != device_is_usable)
|
|
changes = TRUE;
|
|
else if (bzobj->x_device.device_bt) {
|
|
if (!device_is_usable)
|
|
changes = TRUE;
|
|
else {
|
|
if ( bzobj->x_device_is_connected != _bzobjs_device_is_connected (bzobj)
|
|
|| !_nm_device_bt_for_same_device (bzobj->x_device.device_bt,
|
|
bzobj->object_path,
|
|
bzobj->d_device.address,
|
|
bzobj->d_device.name,
|
|
bzobj->d_device_capabilities))
|
|
changes = TRUE;
|
|
}
|
|
}
|
|
|
|
adapter_is_usable_for_device = _bzobjs_adapter_is_usable_for_device (bzobj);
|
|
if (adapter_is_usable_for_device != bzobj->was_usable_adapter_for_device_before) {
|
|
/* this function does not modify bzobj in any other cases except here.
|
|
* Usually changes are processed delayed, in the idle handler.
|
|
*
|
|
* But the bzobj->was_usable_adapter_for_device_before only exists to know whether
|
|
* we need to re-check device availability. It is correct to set the flag
|
|
* here, right before we checked. */
|
|
bzobj->was_usable_adapter_for_device_before = adapter_is_usable_for_device;
|
|
recheck_devices_for_adapter = TRUE;
|
|
changes = TRUE;
|
|
}
|
|
|
|
if (!changes) {
|
|
network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
|
|
|
|
if (network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst)))
|
|
changes = TRUE;
|
|
else if ( bzobj->x_network_server.device_br
|
|
&& !network_server_is_usable)
|
|
changes = TRUE;
|
|
else if (!nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL,
|
|
bzobj->x_network_server.adapter_address))
|
|
changes = TRUE;
|
|
}
|
|
|
|
if (changes)
|
|
_process_change_idle_schedule (self, bzobj);
|
|
|
|
if (recheck_devices_for_adapter) {
|
|
GHashTableIter iter;
|
|
BzDBusObj *bzobj2;
|
|
|
|
/* we got a change to the availability of an adapter. We might need to recheck
|
|
* all devices that use this adapter... */
|
|
g_hash_table_iter_init (&iter, priv->bzobjs);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj2, NULL)) {
|
|
if (bzobj2 == bzobj)
|
|
continue;
|
|
if (!nm_streq0 (bzobj2->d_device.adapter, bzobj->object_path))
|
|
continue;
|
|
if (c_list_is_empty (&bzobj2->process_change_lst))
|
|
_dbus_process_changes (self, bzobj2, "adapter-changed");
|
|
else
|
|
nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj2->process_change_lst);
|
|
}
|
|
}
|
|
|
|
_bzobjs_del_if_dead (bzobj);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#define ALL_RELEVANT_INTERFACE_NAMES NM_MAKE_STRV (NM_BLUEZ5_ADAPTER_INTERFACE, \
|
|
NM_BLUEZ5_DEVICE_INTERFACE, \
|
|
NM_BLUEZ5_NETWORK_INTERFACE, \
|
|
NM_BLUEZ5_NETWORK_SERVER_INTERFACE)
|
|
|
|
static gboolean
|
|
_dbus_handle_properties_changed (NMBluezManager *self,
|
|
const char *object_path,
|
|
const char *interface_name,
|
|
GVariant *changed_properties,
|
|
const char *const*invalidated_properties,
|
|
BzDBusObj **inout_bzobj)
|
|
{
|
|
BzDBusObj *bzobj = NULL;
|
|
gboolean changed = FALSE;
|
|
const char *property_name;
|
|
GVariant *property_value;
|
|
GVariantIter iter_prop;
|
|
gsize i;
|
|
|
|
if (!invalidated_properties)
|
|
invalidated_properties = NM_PTRARRAY_EMPTY (const char *);
|
|
|
|
nm_assert (g_variant_is_of_type (changed_properties, G_VARIANT_TYPE ("a{sv}")));
|
|
|
|
if (inout_bzobj) {
|
|
bzobj = *inout_bzobj;
|
|
nm_assert (!bzobj || nm_streq (object_path, bzobj->object_path));
|
|
}
|
|
|
|
if (changed_properties)
|
|
g_variant_iter_init (&iter_prop, changed_properties);
|
|
|
|
if (nm_streq (interface_name, NM_BLUEZ5_ADAPTER_INTERFACE)) {
|
|
_bzobjs_init (self, &bzobj, object_path);
|
|
if (!bzobj->d_has_adapter_iface) {
|
|
changed = TRUE;
|
|
bzobj->d_has_adapter_iface = TRUE;
|
|
}
|
|
|
|
while ( changed_properties
|
|
&& g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
|
|
_nm_unused gs_unref_variant GVariant *property_value_free = property_value;
|
|
|
|
if (nm_streq (property_name, "Address")) {
|
|
gs_free char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
|
|
? nm_utils_hwaddr_canonical (g_variant_get_string (property_value, NULL), ETH_ALEN)
|
|
: NULL;
|
|
|
|
if (!nm_streq0 (bzobj->d_adapter.address, s)) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_adapter.address);
|
|
bzobj->d_adapter.address = g_steal_pointer (&s);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Powered")) {
|
|
bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
|
|
&& g_variant_get_boolean (property_value);
|
|
|
|
if (bzobj->d_adapter_powered != v) {
|
|
changed = TRUE;
|
|
bzobj->d_adapter_powered = v;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
for (i = 0; (property_name = invalidated_properties[i]); i++) {
|
|
if (nm_streq (property_name, "Address")) {
|
|
if (bzobj->d_adapter.address) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_adapter.address);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Powered")) {
|
|
if (bzobj->d_adapter_powered) {
|
|
changed = TRUE;
|
|
bzobj->d_adapter_powered = FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
} else if (nm_streq (interface_name, NM_BLUEZ5_DEVICE_INTERFACE)) {
|
|
_bzobjs_init (self, &bzobj, object_path);
|
|
if (!bzobj->d_has_device_iface) {
|
|
changed = TRUE;
|
|
bzobj->d_has_device_iface = TRUE;
|
|
}
|
|
|
|
while ( changed_properties
|
|
&& g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
|
|
_nm_unused gs_unref_variant GVariant *property_value_free = property_value;
|
|
|
|
if (nm_streq (property_name, "Address")) {
|
|
gs_free char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
|
|
? nm_utils_hwaddr_canonical (g_variant_get_string (property_value, NULL), ETH_ALEN)
|
|
: NULL;
|
|
|
|
if (!nm_streq0 (bzobj->d_device.address, s)) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.address);
|
|
bzobj->d_device.address = g_steal_pointer (&s);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Name")) {
|
|
const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
|
|
? g_variant_get_string (property_value, NULL)
|
|
: NULL;
|
|
|
|
if (!nm_streq0 (bzobj->d_device.name, s)) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.name);
|
|
bzobj->d_device.name = g_strdup (s);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Adapter")) {
|
|
const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_OBJECT_PATH)
|
|
? g_variant_get_string (property_value, NULL)
|
|
: NULL;
|
|
|
|
if (!nm_streq0 (bzobj->d_device.adapter, s)) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.adapter);
|
|
bzobj->d_device.adapter = g_strdup (s);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "UUIDs")) {
|
|
NMBluetoothCapabilities capabilities = NM_BT_CAPABILITY_NONE;
|
|
|
|
if (g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING_ARRAY)) {
|
|
gs_free const char **s = g_variant_get_strv (property_value, NULL);
|
|
|
|
capabilities = convert_uuids_to_capabilities (s);
|
|
}
|
|
if (bzobj->d_device_capabilities != capabilities) {
|
|
changed = TRUE;
|
|
bzobj->d_device_capabilities = capabilities;
|
|
nm_assert (bzobj->d_device_capabilities == capabilities);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Connected")) {
|
|
bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
|
|
&& g_variant_get_boolean (property_value);
|
|
|
|
if (bzobj->d_device_connected != v) {
|
|
changed = TRUE;
|
|
bzobj->d_device_connected = v;
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Paired")) {
|
|
bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
|
|
&& g_variant_get_boolean (property_value);
|
|
|
|
if (bzobj->d_device_paired != v) {
|
|
changed = TRUE;
|
|
bzobj->d_device_paired = v;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
for (i = 0; (property_name = invalidated_properties[i]); i++) {
|
|
if (nm_streq (property_name, "Address")) {
|
|
if (bzobj->d_device.address) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.address);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Name")) {
|
|
if (bzobj->d_device.name) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.name);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Adapter")) {
|
|
if (bzobj->d_device.adapter) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.adapter);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "UUIDs")) {
|
|
if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
|
|
changed = TRUE;
|
|
bzobj->d_device_capabilities = NM_BT_CAPABILITY_NONE;
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Connected")) {
|
|
if (bzobj->d_device_connected) {
|
|
changed = TRUE;
|
|
bzobj->d_device_connected = FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Paired")) {
|
|
if (bzobj->d_device_paired) {
|
|
changed = TRUE;
|
|
bzobj->d_device_paired = FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
} else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_INTERFACE)) {
|
|
_bzobjs_init (self, &bzobj, object_path);
|
|
if (!bzobj->d_has_network_iface) {
|
|
changed = TRUE;
|
|
bzobj->d_has_network_iface = TRUE;
|
|
}
|
|
|
|
while ( changed_properties
|
|
&& g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
|
|
_nm_unused gs_unref_variant GVariant *property_value_free = property_value;
|
|
|
|
if (nm_streq (property_name, "Interface")) {
|
|
const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
|
|
? g_variant_get_string (property_value, NULL)
|
|
: NULL;
|
|
|
|
if (!nm_streq0 (bzobj->d_network.interface, s)) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_network.interface);
|
|
bzobj->d_network.interface = g_strdup (s);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Connected")) {
|
|
bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
|
|
&& g_variant_get_boolean (property_value);
|
|
|
|
if (bzobj->d_network_connected != v) {
|
|
changed = TRUE;
|
|
bzobj->d_network_connected = v;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
for (i = 0; (property_name = invalidated_properties[i]); i++) {
|
|
if (nm_streq (property_name, "Interface")) {
|
|
if (bzobj->d_network.interface) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_network.interface);
|
|
}
|
|
continue;
|
|
}
|
|
if (nm_streq (property_name, "Connected")) {
|
|
if (bzobj->d_network_connected) {
|
|
changed = TRUE;
|
|
bzobj->d_network_connected = FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
} else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) {
|
|
_bzobjs_init (self, &bzobj, object_path);
|
|
if (!bzobj->d_has_network_server_iface) {
|
|
changed = TRUE;
|
|
bzobj->d_has_network_server_iface = TRUE;
|
|
}
|
|
}
|
|
|
|
nm_assert (!changed || bzobj);
|
|
|
|
if (inout_bzobj)
|
|
*inout_bzobj = bzobj;
|
|
|
|
return changed;
|
|
}
|
|
|
|
static void
|
|
_dbus_handle_interface_added (NMBluezManager *self,
|
|
const char *object_path,
|
|
GVariant *ifaces,
|
|
gboolean initial_get_managed_objects)
|
|
{
|
|
BzDBusObj *bzobj = NULL;
|
|
gboolean changed = FALSE;
|
|
const char *interface_name;
|
|
GVariant *changed_properties;
|
|
GVariantIter iter_ifaces;
|
|
|
|
nm_assert (g_variant_is_of_type (ifaces, G_VARIANT_TYPE ("a{sa{sv}}")));
|
|
|
|
g_variant_iter_init (&iter_ifaces, ifaces);
|
|
while (g_variant_iter_next (&iter_ifaces, "{&s@a{sv}}", &interface_name, &changed_properties)) {
|
|
_nm_unused gs_unref_variant GVariant *changed_properties_free = changed_properties;
|
|
|
|
if (_dbus_handle_properties_changed (self, object_path, interface_name, changed_properties, NULL, &bzobj))
|
|
changed = TRUE;
|
|
}
|
|
|
|
if (changed) {
|
|
_dbus_process_changes (self,
|
|
bzobj,
|
|
initial_get_managed_objects
|
|
? "dbus-init"
|
|
: "dbus-iface-added");
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
_dbus_handle_interface_removed (NMBluezManager *self,
|
|
const char *object_path,
|
|
BzDBusObj **inout_bzobj,
|
|
const char *const*removed_interfaces)
|
|
{
|
|
gboolean changed = FALSE;
|
|
BzDBusObj *bzobj;
|
|
gsize i;
|
|
|
|
if ( inout_bzobj
|
|
&& *inout_bzobj) {
|
|
bzobj = *inout_bzobj;
|
|
nm_assert (bzobj == _bzobjs_get (self, object_path));
|
|
} else {
|
|
bzobj = _bzobjs_get (self, object_path);
|
|
if (!bzobj)
|
|
return FALSE;
|
|
NM_SET_OUT (inout_bzobj, bzobj);
|
|
}
|
|
|
|
for (i = 0; removed_interfaces[i]; i++) {
|
|
const char *interface_name = removed_interfaces[i];
|
|
|
|
if (nm_streq (interface_name, NM_BLUEZ5_ADAPTER_INTERFACE)) {
|
|
if (bzobj->d_has_adapter_iface) {
|
|
changed = TRUE;
|
|
bzobj->d_has_adapter_iface = FALSE;
|
|
}
|
|
if (bzobj->d_adapter.address) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_adapter.address);
|
|
}
|
|
if (bzobj->d_adapter_powered) {
|
|
changed = TRUE;
|
|
bzobj->d_adapter_powered = FALSE;
|
|
}
|
|
} else if (nm_streq (interface_name, NM_BLUEZ5_DEVICE_INTERFACE)) {
|
|
if (bzobj->d_has_device_iface) {
|
|
changed = TRUE;
|
|
bzobj->d_has_device_iface = FALSE;
|
|
}
|
|
if (bzobj->d_device.address) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.address);
|
|
}
|
|
if (bzobj->d_device.name) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.name);
|
|
}
|
|
if (bzobj->d_device.adapter) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_device.adapter);
|
|
}
|
|
if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
|
|
changed = TRUE;
|
|
bzobj->d_device_capabilities = NM_BT_CAPABILITY_NONE;
|
|
}
|
|
if (bzobj->d_device_connected) {
|
|
changed = TRUE;
|
|
bzobj->d_device_connected = FALSE;
|
|
}
|
|
if (bzobj->d_device_paired) {
|
|
changed = TRUE;
|
|
bzobj->d_device_paired = FALSE;
|
|
}
|
|
} else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_INTERFACE)) {
|
|
if (bzobj->d_has_network_iface) {
|
|
changed = TRUE;
|
|
bzobj->d_has_network_iface = FALSE;
|
|
}
|
|
if (bzobj->d_network.interface) {
|
|
changed = TRUE;
|
|
nm_clear_g_free (&bzobj->d_network.interface);
|
|
}
|
|
if (bzobj->d_network_connected) {
|
|
changed = TRUE;
|
|
bzobj->d_network_connected = FALSE;
|
|
}
|
|
} else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) {
|
|
if (bzobj->d_has_network_server_iface) {
|
|
changed = TRUE;
|
|
bzobj->d_has_network_server_iface = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static void
|
|
_dbus_managed_objects_changed_cb (GDBusConnection *connection,
|
|
const char *sender_name,
|
|
const char *arg_object_path,
|
|
const char *interface_name,
|
|
const char *signal_name,
|
|
GVariant *parameters,
|
|
gpointer user_data)
|
|
{
|
|
NMBluezManager *self = user_data;
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
BzDBusObj *bzobj = NULL;
|
|
gboolean changed;
|
|
|
|
nm_assert (nm_streq0 (interface_name, DBUS_INTERFACE_OBJECT_MANAGER));
|
|
|
|
if (priv->get_managed_objects_cancellable) {
|
|
/* we still wait for the initial GetManagedObjects(). Ignore the event. */
|
|
return;
|
|
}
|
|
|
|
if (nm_streq (signal_name, "InterfacesAdded")) {
|
|
gs_unref_variant GVariant *interfaces_and_properties = NULL;
|
|
const char *object_path;
|
|
|
|
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oa{sa{sv}})")))
|
|
return;
|
|
|
|
g_variant_get (parameters,
|
|
"(&o@a{sa{sv}})",
|
|
&object_path,
|
|
&interfaces_and_properties);
|
|
|
|
_dbus_handle_interface_added (self, object_path, interfaces_and_properties, FALSE);
|
|
return;
|
|
}
|
|
|
|
if (nm_streq (signal_name, "InterfacesRemoved")) {
|
|
gs_free const char **interfaces = NULL;
|
|
const char *object_path;
|
|
|
|
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oas)")))
|
|
return;
|
|
|
|
g_variant_get (parameters,
|
|
"(&o^a&s)",
|
|
&object_path,
|
|
&interfaces);
|
|
|
|
changed = _dbus_handle_interface_removed (self, object_path, &bzobj, interfaces);
|
|
if (changed)
|
|
_dbus_process_changes (self, bzobj, "dbus-iface-removed");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_dbus_properties_changed_cb (GDBusConnection *connection,
|
|
const char *sender_name,
|
|
const char *object_path,
|
|
const char *signal_interface_name,
|
|
const char *signal_name,
|
|
GVariant *parameters,
|
|
gpointer user_data)
|
|
{
|
|
NMBluezManager *self = user_data;
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
const char *interface_name;
|
|
gs_unref_variant GVariant *changed_properties = NULL;
|
|
gs_free const char **invalidated_properties = NULL;
|
|
BzDBusObj *bzobj = NULL;
|
|
|
|
if (priv->get_managed_objects_cancellable) {
|
|
/* we still wait for the initial GetManagedObjects(). Ignore the event. */
|
|
return;
|
|
}
|
|
|
|
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
|
|
return;
|
|
|
|
g_variant_get (parameters,
|
|
"(&s@a{sv}^a&s)",
|
|
&interface_name,
|
|
&changed_properties,
|
|
&invalidated_properties);
|
|
|
|
if (_dbus_handle_properties_changed (self, object_path, interface_name, changed_properties, invalidated_properties, &bzobj))
|
|
_dbus_process_changes (self, bzobj, "dbus-property-changed");
|
|
}
|
|
|
|
static void
|
|
_dbus_get_managed_objects_cb (GVariant *result,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
NMBluezManager *self;
|
|
NMBluezManagerPrivate *priv;
|
|
GVariantIter iter;
|
|
const char *object_path;
|
|
GVariant *ifaces;
|
|
|
|
if ( !result
|
|
&& nm_utils_error_is_cancelled (error))
|
|
return;
|
|
|
|
self = user_data;
|
|
priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
g_clear_object (&priv->get_managed_objects_cancellable);
|
|
|
|
if (!result) {
|
|
_LOGT ("initial GetManagedObjects() call failed: %s", error->message);
|
|
_cleanup_for_name_owner (self);
|
|
return;
|
|
}
|
|
|
|
_LOGT ("initial GetManagedObjects call succeeded");
|
|
|
|
g_variant_iter_init (&iter, result);
|
|
while (g_variant_iter_next (&iter, "{&o@a{sa{sv}}}", &object_path, &ifaces)) {
|
|
_nm_unused gs_unref_variant GVariant *ifaces_free = ifaces;
|
|
|
|
_dbus_handle_interface_added (self, object_path, ifaces, TRUE);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_cleanup_for_name_owner (NMBluezManager *self)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
gboolean emit_device_availability_changed = FALSE;
|
|
GHashTableIter iter;
|
|
BzDBusObj *bzobj;
|
|
gboolean first = TRUE;
|
|
|
|
nm_clear_g_cancellable (&priv->get_managed_objects_cancellable);
|
|
|
|
nm_clear_g_dbus_connection_signal (priv->dbus_connection,
|
|
&priv->managed_objects_changed_id);
|
|
nm_clear_g_dbus_connection_signal (priv->dbus_connection,
|
|
&priv->properties_changed_id);
|
|
|
|
nm_clear_g_free (&priv->name_owner);
|
|
|
|
g_hash_table_iter_init (&iter, priv->bzobjs);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
|
|
if (first) {
|
|
first = FALSE;
|
|
_LOGT ("drop all objects form D-Bus cache...");
|
|
}
|
|
_dbus_handle_interface_removed (self,
|
|
bzobj->object_path,
|
|
&bzobj,
|
|
ALL_RELEVANT_INTERFACE_NAMES);
|
|
nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj->process_change_lst);
|
|
}
|
|
_process_change_idle_all (self, &emit_device_availability_changed);
|
|
nm_assert (g_hash_table_size (priv->bzobjs) == 0);
|
|
|
|
if (emit_device_availability_changed)
|
|
nm_manager_notify_device_availibility_maybe_changed (priv->manager);
|
|
}
|
|
|
|
static void
|
|
name_owner_changed (NMBluezManager *self,
|
|
const char *owner)
|
|
{
|
|
_nm_unused gs_unref_object NMBluezManager *self_keep_alive = g_object_ref (self);
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
owner = nm_str_not_empty (owner);
|
|
|
|
if (!owner)
|
|
_LOGT ("D-Bus name for bluez has no owner");
|
|
else
|
|
_LOGT ("D-Bus name for bluez has owner %s", owner);
|
|
|
|
nm_clear_g_cancellable (&priv->name_owner_get_cancellable);
|
|
|
|
if (nm_streq0 (priv->name_owner, owner))
|
|
return;
|
|
|
|
_cleanup_for_name_owner (self);
|
|
|
|
if (!owner)
|
|
return;
|
|
|
|
priv->name_owner = g_strdup (owner);
|
|
|
|
priv->get_managed_objects_cancellable = g_cancellable_new ();
|
|
|
|
priv->managed_objects_changed_id = nm_dbus_connection_signal_subscribe_object_manager (priv->dbus_connection,
|
|
priv->name_owner,
|
|
NM_BLUEZ_MANAGER_PATH,
|
|
NULL,
|
|
_dbus_managed_objects_changed_cb,
|
|
self,
|
|
NULL);
|
|
|
|
priv->properties_changed_id = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection,
|
|
priv->name_owner,
|
|
NULL,
|
|
NULL,
|
|
_dbus_properties_changed_cb,
|
|
self,
|
|
NULL);
|
|
|
|
nm_dbus_connection_call_get_managed_objects (priv->dbus_connection,
|
|
priv->name_owner,
|
|
NM_BLUEZ_MANAGER_PATH,
|
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
20000,
|
|
priv->get_managed_objects_cancellable,
|
|
_dbus_get_managed_objects_cb,
|
|
self);
|
|
}
|
|
|
|
static void
|
|
name_owner_changed_cb (GDBusConnection *connection,
|
|
const char *sender_name,
|
|
const char *object_path,
|
|
const char *interface_name,
|
|
const char *signal_name,
|
|
GVariant *parameters,
|
|
gpointer user_data)
|
|
{
|
|
NMBluezManager *self = user_data;
|
|
const char *new_owner;
|
|
|
|
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
|
|
return;
|
|
|
|
g_variant_get (parameters,
|
|
"(&s&s&s)",
|
|
NULL,
|
|
NULL,
|
|
&new_owner);
|
|
|
|
name_owner_changed (self, new_owner);
|
|
}
|
|
|
|
static void
|
|
name_owner_get_cb (const char *name_owner,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
if ( name_owner
|
|
|| !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
name_owner_changed (user_data, name_owner);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_cleanup_all (NMBluezManager *self)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
priv->settings_registered = FALSE;
|
|
|
|
g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
|
|
g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
|
|
g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self);
|
|
|
|
g_hash_table_remove_all (priv->conn_data_elems);
|
|
g_hash_table_remove_all (priv->conn_data_heads);
|
|
|
|
_cleanup_for_name_owner (self);
|
|
|
|
nm_clear_g_cancellable (&priv->name_owner_get_cancellable);
|
|
|
|
nm_clear_g_dbus_connection_signal (priv->dbus_connection,
|
|
&priv->name_owner_changed_id);
|
|
}
|
|
|
|
static void
|
|
start (NMDeviceFactory *factory)
|
|
{
|
|
NMBluezManager *self;
|
|
NMBluezManagerPrivate *priv;
|
|
NMSettingsConnection *const*sett_conns;
|
|
guint n_sett_conns;
|
|
guint i;
|
|
|
|
g_return_if_fail (NM_IS_BLUEZ_MANAGER (factory));
|
|
|
|
self = NM_BLUEZ_MANAGER (factory);
|
|
priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
_cleanup_all (self);
|
|
|
|
if (!priv->dbus_connection) {
|
|
_LOGI ("no D-Bus connection available");
|
|
return;
|
|
}
|
|
|
|
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, G_CALLBACK (cp_connection_added), self);
|
|
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), self);
|
|
g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
|
|
|
|
priv->settings_registered = TRUE;
|
|
|
|
sett_conns = nm_settings_get_connections (priv->settings, &n_sett_conns);
|
|
for (i = 0; i < n_sett_conns; i++)
|
|
_conn_track_update (self, sett_conns[i], TRUE, NULL, NULL, NULL);
|
|
|
|
priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
|
|
NM_BLUEZ_SERVICE,
|
|
name_owner_changed_cb,
|
|
self,
|
|
NULL);
|
|
|
|
priv->name_owner_get_cancellable = g_cancellable_new ();
|
|
|
|
nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
|
|
NM_BLUEZ_SERVICE,
|
|
10000,
|
|
priv->name_owner_get_cancellable,
|
|
name_owner_get_cb,
|
|
self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
_connect_returned (NMBluezManager *self,
|
|
BzDBusObj *bzobj,
|
|
NMBluetoothCapabilities bt_type,
|
|
const char *device_name,
|
|
NMBluez5DunContext *dun_context,
|
|
GError *error)
|
|
{
|
|
char sbuf_cap[100];
|
|
|
|
if (error) {
|
|
nm_assert (!device_name);
|
|
nm_assert (!dun_context);
|
|
|
|
_LOGI ("%s [%s]: connect failed: %s",
|
|
nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
|
|
bzobj->object_path,
|
|
error->message);
|
|
|
|
_device_connect_req_data_complete (g_steal_pointer (&bzobj->x_device.c_req_data),
|
|
self,
|
|
NULL,
|
|
error);
|
|
_connect_disconnect (self, bzobj, "cleanup after connect failure");
|
|
return;
|
|
}
|
|
|
|
nm_assert (bzobj->x_device_connect_bt_type == bt_type);
|
|
nm_assert (device_name);
|
|
nm_assert ((bt_type == NM_BT_CAPABILITY_DUN) == (!!dun_context));
|
|
nm_assert (bzobj->x_device.c_req_data);
|
|
|
|
g_clear_object (&bzobj->x_device.c_req_data->int_cancellable);
|
|
|
|
bzobj->x_device.connect_dun_context = dun_context;
|
|
|
|
_LOGD ("%s [%s]: connect successful to device %s",
|
|
nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
|
|
bzobj->object_path,
|
|
device_name);
|
|
|
|
/* we already have another over-all timer running. But after we connected the device,
|
|
* we still need to wait for bluez to acknowledge the connected state (via D-Bus, for NAP).
|
|
* For DUN profiles we likely are already fully connected by now.
|
|
*
|
|
* Anyway, schedule another timeout that is possibly shorter than the overall, original
|
|
* timeout. Now this should go down fast. */
|
|
bzobj->x_device.c_req_data->timeout_wait_connect_id = g_timeout_add (5000,
|
|
_connect_timeout_wait_connected_cb,
|
|
bzobj),
|
|
bzobj->x_device.c_req_data->device_name = g_strdup (device_name);
|
|
|
|
if ( _bzobjs_device_is_usable (bzobj, NULL, NULL)
|
|
&& _bzobjs_device_is_connected (bzobj)) {
|
|
/* We are now connected. Schedule the task that completes the state. */
|
|
_process_change_idle_schedule (self, bzobj);
|
|
}
|
|
}
|
|
|
|
#if WITH_BLUEZ5_DUN
|
|
static void
|
|
_connect_dun_notify_tty_hangup_cb (NMBluez5DunContext *context,
|
|
gpointer user_data)
|
|
{
|
|
BzDBusObj *bzobj = user_data;
|
|
|
|
_connect_disconnect (bzobj->self,
|
|
bzobj,
|
|
"DUN connection hung up");
|
|
}
|
|
|
|
static void
|
|
_connect_dun_step2_cb (NMBluez5DunContext *context,
|
|
const char *rfcomm_dev,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
BzDBusObj *bzobj;
|
|
|
|
if (nm_utils_error_is_cancelled (error))
|
|
return;
|
|
|
|
bzobj = user_data;
|
|
|
|
if (rfcomm_dev) {
|
|
/* We want to early notifiy about the rfcomm path. That is because we might still delay
|
|
* to signal full activation longer (asynchronously). But the earliest time the callback
|
|
* is invoked with the rfcomm path, we just created the device synchronously.
|
|
*
|
|
* By already notifying the caller about the path early, it avoids a race where ModemManager
|
|
* would find the modem before the bluetooth code considers the profile fully activated. */
|
|
|
|
nm_assert (!error);
|
|
nm_assert (bzobj->x_device.c_req_data);
|
|
|
|
if (!g_cancellable_is_cancelled (bzobj->x_device.c_req_data->ext_cancellable))
|
|
bzobj->x_device.c_req_data->callback (bzobj->self, FALSE, rfcomm_dev, NULL, bzobj->x_device.c_req_data->callback_user_data);
|
|
|
|
if (!context) {
|
|
/* No context set. This means, we just got notified about the rfcomm path and need to wait
|
|
* longer, for the next callback. */
|
|
return;
|
|
}
|
|
}
|
|
|
|
_connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, rfcomm_dev, context, error);
|
|
}
|
|
|
|
static void
|
|
_connect_dun_step1_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
gs_unref_variant GVariant *ret = NULL;
|
|
gs_free_error GError *error = NULL;
|
|
BzDBusObj *bzobj_adapter;
|
|
BzDBusObj *bzobj;
|
|
|
|
ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
|
|
|
|
if ( !ret
|
|
&& nm_utils_error_is_cancelled (error))
|
|
return;
|
|
|
|
bzobj = user_data;
|
|
|
|
if (error) {
|
|
_LOGT ("DUN: [%s]: bluetooth device connect failed: %s", bzobj->object_path, error->message);
|
|
/* we actually ignore this error. Let's try, maybe we still can connect via DUN. */
|
|
g_clear_error (&error);
|
|
} else
|
|
_LOGT ("DUN: [%s]: bluetooth device connected successfully", bzobj->object_path);
|
|
|
|
if (!_bzobjs_device_is_usable (bzobj, &bzobj_adapter, NULL)) {
|
|
nm_utils_error_set (&error, NM_UTILS_ERROR_UNKNOWN,
|
|
"device %s is not usable for DUN after connect",
|
|
bzobj->object_path);
|
|
_connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, NULL, NULL, error);
|
|
return;
|
|
}
|
|
|
|
if (!nm_bluez5_dun_connect (bzobj_adapter->d_adapter.address,
|
|
bzobj->d_device.address,
|
|
bzobj->x_device.c_req_data->int_cancellable,
|
|
_connect_dun_step2_cb,
|
|
bzobj,
|
|
_connect_dun_notify_tty_hangup_cb,
|
|
bzobj,
|
|
&error)) {
|
|
_connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, NULL, NULL, error);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
_connect_nap_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
gs_unref_variant GVariant *ret = NULL;
|
|
const char *network_iface_name = NULL;
|
|
gs_free_error GError *error = NULL;
|
|
BzDBusObj *bzobj;
|
|
|
|
ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
|
|
|
|
if ( !ret
|
|
&& nm_utils_error_is_cancelled (error))
|
|
return;
|
|
|
|
if (ret)
|
|
g_variant_get (ret, "(&s)", &network_iface_name);
|
|
|
|
bzobj = user_data;
|
|
|
|
_connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_NAP, network_iface_name, NULL, error);
|
|
}
|
|
|
|
static void
|
|
_connect_cancelled_cb (GCancellable *cancellable,
|
|
BzDBusObj *bzobj)
|
|
{
|
|
_connect_disconnect (bzobj->self, bzobj, "connect cancelled");
|
|
}
|
|
|
|
static gboolean
|
|
_connect_timeout_wait_connected_cb (gpointer user_data)
|
|
{
|
|
BzDBusObj *bzobj = user_data;
|
|
|
|
bzobj->x_device.c_req_data->timeout_wait_connect_id = 0;
|
|
_connect_disconnect (bzobj->self, bzobj, "timeout waiting for connected");
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static gboolean
|
|
_connect_timeout_cb (gpointer user_data)
|
|
{
|
|
BzDBusObj *bzobj = user_data;
|
|
|
|
bzobj->x_device.c_req_data->timeout_id = 0;
|
|
_connect_disconnect (bzobj->self, bzobj, "timeout connecting");
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
_connect_disconnect (NMBluezManager *self,
|
|
BzDBusObj *bzobj,
|
|
const char *reason)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
DeviceConnectReqData *c_req_data;
|
|
char sbuf_cap[100];
|
|
gboolean bt_type;
|
|
|
|
if (bzobj->x_device_connect_bt_type == NM_BT_CAPABILITY_NONE) {
|
|
nm_assert (!bzobj->x_device.c_req_data);
|
|
return;
|
|
}
|
|
|
|
bt_type = bzobj->x_device_connect_bt_type;
|
|
nm_assert (NM_IN_SET (bt_type, NM_BT_CAPABILITY_DUN, NM_BT_CAPABILITY_NAP));
|
|
bzobj->x_device_connect_bt_type = NM_BT_CAPABILITY_NONE;
|
|
|
|
c_req_data = g_steal_pointer (&bzobj->x_device.c_req_data);
|
|
|
|
_LOGD ("%s [%s]: disconnect due to %s",
|
|
nm_bluetooth_capability_to_string (bt_type, sbuf_cap, sizeof (sbuf_cap)),
|
|
bzobj->object_path,
|
|
reason);
|
|
|
|
if (c_req_data)
|
|
nm_clear_g_cancellable (&c_req_data->int_cancellable);
|
|
|
|
if (bt_type == NM_BT_CAPABILITY_DUN) {
|
|
/* For DUN devices, we also called org.bluez.Device1.Connect() (because in order
|
|
* for nm_bluez5_dun_connect() to succeed, we need to be already connected *why??).
|
|
*
|
|
* But upon disconnect we don't call Disconnect() because we don't know whether somebody
|
|
* else also uses the bluetooth device for other purposes. During disconnect we only
|
|
* terminate the DUN connection, but don't disconnect entirely. I think that's the
|
|
* best we can do. */
|
|
#if WITH_BLUEZ5_DUN
|
|
nm_clear_pointer (&bzobj->x_device.connect_dun_context, nm_bluez5_dun_disconnect);
|
|
#else
|
|
nm_assert_not_reached ();
|
|
#endif
|
|
} else {
|
|
if (priv->name_owner) {
|
|
gs_unref_object GCancellable *cancellable = NULL;
|
|
|
|
cancellable = g_cancellable_new ();
|
|
|
|
nm_shutdown_wait_obj_register_cancellable_full (cancellable,
|
|
g_strdup_printf ("bt-disconnect-nap[%s]", bzobj->object_path),
|
|
TRUE);
|
|
|
|
g_dbus_connection_call (priv->dbus_connection,
|
|
priv->name_owner,
|
|
bzobj->object_path,
|
|
NM_BLUEZ5_NETWORK_INTERFACE,
|
|
"Disconnect",
|
|
g_variant_new("()"),
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
-1,
|
|
cancellable,
|
|
_dbus_call_complete_cb_nop,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
if (c_req_data) {
|
|
gs_free_error GError *error = NULL;
|
|
|
|
nm_utils_error_set (&error,
|
|
NM_UTILS_ERROR_UNKNOWN,
|
|
"connect aborted due to %s",
|
|
reason);
|
|
_device_connect_req_data_complete (c_req_data, self, NULL, error);
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
nm_bluez_manager_connect (NMBluezManager *self,
|
|
const char *object_path,
|
|
NMBluetoothCapabilities connection_bt_type,
|
|
int timeout_msec,
|
|
GCancellable *cancellable,
|
|
NMBluezManagerConnectCb callback,
|
|
gpointer callback_user_data,
|
|
GError **error)
|
|
{
|
|
gs_unref_object GCancellable *int_cancellable = NULL;
|
|
DeviceConnectReqData *c_req_data;
|
|
NMBluezManagerPrivate *priv;
|
|
BzDBusObj *bzobj;
|
|
char sbuf_cap[100];
|
|
|
|
g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (self), FALSE);
|
|
g_return_val_if_fail (NM_IN_SET (connection_bt_type, NM_BT_CAPABILITY_DUN,
|
|
NM_BT_CAPABILITY_NAP), FALSE);
|
|
g_return_val_if_fail (callback, FALSE);
|
|
|
|
nm_assert (timeout_msec > 0);
|
|
|
|
priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
bzobj = _bzobjs_get (self, object_path);
|
|
|
|
if (!bzobj) {
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
|
|
"device %s does not exist",
|
|
object_path);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) {
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
|
|
"device %s is not usable",
|
|
object_path);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!NM_FLAGS_ALL (bzobj->d_device_capabilities, connection_bt_type)) {
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
|
|
"device %s has not the required capabilities",
|
|
object_path);
|
|
return FALSE;
|
|
}
|
|
|
|
#if !WITH_BLUEZ5_DUN
|
|
if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
|
|
nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
|
|
"DUN is not supported");
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
_connect_disconnect (self, bzobj, "new activation");
|
|
|
|
_LOGD ("%s [%s]: connecting...",
|
|
nm_bluetooth_capability_to_string (connection_bt_type, sbuf_cap, sizeof (sbuf_cap)),
|
|
bzobj->object_path);
|
|
|
|
int_cancellable = g_cancellable_new();
|
|
|
|
#if WITH_BLUEZ5_DUN
|
|
if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
|
|
g_dbus_connection_call (priv->dbus_connection,
|
|
priv->name_owner,
|
|
bzobj->object_path,
|
|
NM_BLUEZ5_DEVICE_INTERFACE,
|
|
"Connect",
|
|
NULL,
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
timeout_msec,
|
|
int_cancellable,
|
|
_connect_dun_step1_cb,
|
|
bzobj);
|
|
} else
|
|
#endif
|
|
{
|
|
nm_assert (connection_bt_type == NM_BT_CAPABILITY_NAP);
|
|
g_dbus_connection_call (priv->dbus_connection,
|
|
priv->name_owner,
|
|
bzobj->object_path,
|
|
NM_BLUEZ5_NETWORK_INTERFACE,
|
|
"Connect",
|
|
g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
|
|
G_VARIANT_TYPE ("(s)"),
|
|
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
|
timeout_msec,
|
|
int_cancellable,
|
|
_connect_nap_cb,
|
|
bzobj);
|
|
}
|
|
|
|
c_req_data = g_slice_new (DeviceConnectReqData);
|
|
*c_req_data = (DeviceConnectReqData) {
|
|
.int_cancellable = g_steal_pointer (&int_cancellable),
|
|
.ext_cancellable = g_object_ref (cancellable),
|
|
.callback = callback,
|
|
.callback_user_data = callback_user_data,
|
|
.ext_cancelled_id = g_signal_connect (cancellable,
|
|
"cancelled",
|
|
G_CALLBACK (_connect_cancelled_cb),
|
|
bzobj),
|
|
.timeout_id = g_timeout_add (timeout_msec,
|
|
_connect_timeout_cb,
|
|
bzobj),
|
|
};
|
|
|
|
bzobj->x_device_connect_bt_type = connection_bt_type;
|
|
bzobj->x_device.c_req_data = c_req_data;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
nm_bluez_manager_disconnect (NMBluezManager *self,
|
|
const char *object_path)
|
|
{
|
|
BzDBusObj *bzobj;
|
|
|
|
g_return_if_fail (NM_IS_BLUEZ_MANAGER (self));
|
|
g_return_if_fail (object_path);
|
|
|
|
bzobj = _bzobjs_get (self, object_path);
|
|
if (!bzobj)
|
|
return;
|
|
|
|
_connect_disconnect (self, bzobj, "disconnected by user");
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static NMDevice *
|
|
create_device (NMDeviceFactory *factory,
|
|
const char *iface,
|
|
const NMPlatformLink *plink,
|
|
NMConnection *connection,
|
|
gboolean *out_ignore)
|
|
{
|
|
*out_ignore = TRUE;
|
|
g_return_val_if_fail (plink->type == NM_LINK_TYPE_BNEP, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
match_connection (NMDeviceFactory *factory,
|
|
NMConnection *connection)
|
|
{
|
|
const char *type = nm_connection_get_connection_type (connection);
|
|
|
|
nm_assert (nm_streq (type, NM_SETTING_BLUETOOTH_SETTING_NAME));
|
|
|
|
if (_nm_connection_get_setting_bluetooth_for_nap (connection))
|
|
return FALSE; /* handled by the bridge factory */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
nm_bluez_manager_init (NMBluezManager *self)
|
|
{
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
priv->vtable_network_server = (NMBtVTableNetworkServer) {
|
|
.is_available = _network_server_vt_is_available,
|
|
.register_bridge = _network_server_vt_register_bridge,
|
|
.unregister_bridge = _network_server_vt_unregister_bridge,
|
|
};
|
|
|
|
c_list_init (&priv->network_server_lst_head);
|
|
c_list_init (&priv->process_change_lst_head);
|
|
|
|
priv->conn_data_heads = g_hash_table_new_full (_conn_data_head_hash, _conn_data_head_equal, g_free, NULL);
|
|
priv->conn_data_elems = g_hash_table_new_full (nm_pdirect_hash, nm_pdirect_equal, nm_g_slice_free_fcn (ConnDataElem), NULL);
|
|
|
|
priv->bzobjs = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, (GDestroyNotify) _bz_dbus_obj_free, NULL);
|
|
|
|
priv->manager = g_object_ref (NM_MANAGER_GET);
|
|
priv->settings = g_object_ref (NM_SETTINGS_GET);
|
|
priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
|
|
|
|
g_atomic_pointer_compare_and_exchange (&nm_bt_vtable_network_server, NULL, &priv->vtable_network_server);
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
NMBluezManager *self = NM_BLUEZ_MANAGER (object);
|
|
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
|
|
|
|
/* FIXME(shutdown): we need a nm_device_factory_stop() hook to first unregister all
|
|
* BzDBusObj instances and do necessary cleanup actions (like disconnecting devices
|
|
* or deleting panu_connection). */
|
|
|
|
nm_assert (c_list_is_empty (&priv->network_server_lst_head));
|
|
nm_assert (c_list_is_empty (&priv->process_change_lst_head));
|
|
nm_assert (priv->process_change_idle_id == 0);
|
|
|
|
g_atomic_pointer_compare_and_exchange (&nm_bt_vtable_network_server, &priv->vtable_network_server, NULL);
|
|
|
|
_cleanup_all (self);
|
|
|
|
G_OBJECT_CLASS (nm_bluez_manager_parent_class)->dispose (object);
|
|
|
|
g_clear_object (&priv->settings);
|
|
g_clear_object (&priv->manager);
|
|
g_clear_object (&priv->dbus_connection);
|
|
|
|
nm_clear_pointer (&priv->bzobjs, g_hash_table_destroy);
|
|
}
|
|
|
|
static void
|
|
nm_bluez_manager_class_init (NMBluezManagerClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS (klass);
|
|
|
|
object_class->dispose = dispose;
|
|
|
|
factory_class->get_supported_types = get_supported_types;
|
|
factory_class->create_device = create_device;
|
|
factory_class->match_connection = match_connection;
|
|
factory_class->start = start;
|
|
}
|