From 37e8c53eeed579fe34a68819cd12f3295d581394 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 10 Oct 2018 17:10:01 +0200 Subject: [PATCH] core: Introduce helper class to track connection keep alive For P2P connections it makes sense to bind the connection to the status of the operation that is being done. One example is that a wifi display (miracast) P2P connection should be shut down when streaming fails for some reason. This new helper class allows binding a connection to the presence of a DBus path meaning that it will be torn down if the process disappears. --- Makefile.am | 2 + src/meson.build | 1 + src/nm-active-connection.c | 40 ++++++ src/nm-active-connection.h | 3 + src/nm-keep-alive.c | 251 +++++++++++++++++++++++++++++++++++++ src/nm-keep-alive.h | 56 +++++++++ src/nm-policy.c | 33 ++++- src/nm-types.h | 1 + 8 files changed, 385 insertions(+), 2 deletions(-) create mode 100644 src/nm-keep-alive.c create mode 100644 src/nm-keep-alive.h diff --git a/Makefile.am b/Makefile.am index 73e3c7041d..419006499e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1926,6 +1926,8 @@ src_libNetworkManager_la_SOURCES = \ src/nm-rfkill-manager.h \ src/nm-session-monitor.h \ src/nm-session-monitor.c \ + src/nm-keep-alive.c \ + src/nm-keep-alive.h \ src/nm-sleep-monitor.c \ src/nm-sleep-monitor.h \ src/nm-types.h \ diff --git a/src/meson.build b/src/meson.build index d58fb4d1f6..ca9919f65a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -135,6 +135,7 @@ sources = files( 'nm-dispatcher.c', 'nm-firewall-manager.c', 'nm-hostname-manager.c', + 'nm-keep-alive.c', 'nm-manager.c', 'nm-netns.c', 'nm-pacrunner-manager.c', diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index a2f9ae4a2b..d8c886b147 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -30,6 +30,7 @@ #include "nm-auth-utils.h" #include "nm-auth-manager.h" #include "nm-auth-subject.h" +#include "nm-keep-alive.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" @@ -75,6 +76,7 @@ typedef struct _NMActiveConnectionPrivate { gpointer user_data; } auth; + NMKeepAlive *keep_alive; } NMActiveConnectionPrivate; NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection, @@ -103,6 +105,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection, PROP_INT_MASTER_READY, PROP_INT_ACTIVATION_TYPE, PROP_INT_ACTIVATION_REASON, + PROP_INT_KEEP_ALIVE, ); enum { @@ -173,6 +176,14 @@ NM_UTILS_FLAGS2STR_DEFINE_STATIC (_state_flags_to_string, NMActivationStateFlags /*****************************************************************************/ +static void +keep_alive_alive_changed (NMActiveConnection *ac, + GParamSpec *pspec, + NMKeepAlive *keep_alive) +{ + _notify (ac, PROP_INT_KEEP_ALIVE); +} + static void _settings_connection_updated (NMSettingsConnection *sett_conn, gboolean by_user, @@ -904,6 +915,14 @@ nm_active_connection_get_activation_reason (NMActiveConnection *self) return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->activation_reason; } +gboolean +nm_active_connection_get_keep_alive (NMActiveConnection *self) +{ + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + + return nm_keep_alive_is_alive (priv->keep_alive); +} + /*****************************************************************************/ static void @@ -1293,6 +1312,9 @@ get_property (GObject *object, guint prop_id, case PROP_INT_MASTER_READY: g_value_set_boolean (value, priv->master_ready); break; + case PROP_INT_KEEP_ALIVE: + g_value_set_boolean (value, nm_keep_alive_is_alive (priv->keep_alive)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1400,6 +1422,12 @@ nm_active_connection_init (NMActiveConnection *self) priv->activation_type = NM_ACTIVATION_TYPE_MANAGED; priv->version_id = _version_id_new (); + + priv->keep_alive = nm_keep_alive_new (TRUE); + g_signal_connect_object (priv->keep_alive, "notify::" NM_KEEP_ALIVE_ALIVE, + (GCallback) keep_alive_alive_changed, + self, + G_CONNECT_SWAPPED); } static void @@ -1431,6 +1459,12 @@ constructed (GObject *object) g_return_if_fail (priv->subject); g_return_if_fail (priv->activation_reason != NM_ACTIVATION_REASON_UNSET); + + if (priv->activation_reason == NM_ACTIVATION_REASON_AUTOCONNECT || + priv->activation_reason == NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES) { + nm_keep_alive_set_settings_connection_watch_visible (priv->keep_alive, priv->settings_connection.obj); + nm_keep_alive_sink (priv->keep_alive); + } } static void @@ -1474,6 +1508,7 @@ finalize (GObject *object) NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); nm_dbus_track_obj_path_set (&priv->settings_connection, NULL, FALSE); + g_clear_object (&priv->keep_alive); G_OBJECT_CLASS (nm_active_connection_parent_class)->finalize (object); } @@ -1684,6 +1719,11 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_INT_KEEP_ALIVE] = + g_param_spec_boolean (NM_ACTIVE_CONNECTION_INT_KEEP_ALIVE, "", "", + TRUE, G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[DEVICE_CHANGED] = diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index b28c2b02fd..85689a6bfd 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -59,6 +59,7 @@ #define NM_ACTIVE_CONNECTION_INT_MASTER_READY "int-master-ready" #define NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE "int-activation-type" #define NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON "int-activation-reason" +#define NM_ACTIVE_CONNECTION_INT_KEEP_ALIVE "int-keep-alive" /* Signals */ #define NM_ACTIVE_CONNECTION_STATE_CHANGED "state-changed" @@ -185,6 +186,8 @@ NMActivationType nm_active_connection_get_activation_type (NMActiveConnection *s NMActivationReason nm_active_connection_get_activation_reason (NMActiveConnection *self); +gboolean nm_active_connection_get_keep_alive (NMActiveConnection *self); + void nm_active_connection_clear_secrets (NMActiveConnection *self); #endif /* __NETWORKMANAGER_ACTIVE_CONNECTION_H__ */ diff --git a/src/nm-keep-alive.c b/src/nm-keep-alive.c new file mode 100644 index 0000000000..be6dc2304b --- /dev/null +++ b/src/nm-keep-alive.c @@ -0,0 +1,251 @@ +/* + * NetworkManager -- Inhibition management + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-keep-alive.h" +#include "settings/nm-settings-connection.h" + +#include + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMKeepAlive, + PROP_ALIVE, +); + +typedef struct { + gboolean floating; + gboolean forced; + NMSettingsConnection *connection; + GDBusConnection *dbus_connection; + char *dbus_client; + + guint subscription_id; +} NMKeepAlivePrivate; + +struct _NMKeepAlive { + GObject parent; + NMKeepAlivePrivate _priv; +}; + +struct _NMKeepAliveClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE (NMKeepAlive, nm_keep_alive, G_TYPE_OBJECT) + +#define NM_KEEP_ALIVE_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMKeepAlive, NM_IS_KEEP_ALIVE) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_CORE +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "keep-alive", __VA_ARGS__) + +/*****************************************************************************/ + +NMKeepAlive* nm_keep_alive_new (gboolean floating) +{ + NMKeepAlive *res = g_object_new (NM_TYPE_KEEP_ALIVE, NULL); + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (res); + + priv->floating = floating; + + return res; +} + +gboolean nm_keep_alive_is_alive (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if (priv->floating || priv->forced) + return TRUE; + + if (priv->connection && NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->connection), + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) + return TRUE; + + if (priv->dbus_client) + return TRUE; + + return FALSE; +} + +void nm_keep_alive_sink (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + priv->floating = FALSE; + + _notify (self, PROP_ALIVE); +} + +void nm_keep_alive_set_forced (NMKeepAlive *self, gboolean forced) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + priv->forced = forced; + + _notify (self, PROP_ALIVE); +} + +static void +connection_flags_changed (NMSettingsConnection *connection, + NMKeepAlive *self) +{ + _notify (self, PROP_ALIVE); +} + + +void +nm_keep_alive_set_settings_connection_watch_visible (NMKeepAlive *self, + NMSettingsConnection *connection) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if (priv->connection) { + g_signal_handlers_disconnect_by_data (priv->connection, self); + priv->connection = NULL; + } + + priv->connection = g_object_ref (connection); + g_signal_connect_object (priv->connection, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, + G_CALLBACK (connection_flags_changed), self, 0); + + _notify (self, PROP_ALIVE); +} + +static void +cleanup_dbus_watch (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + _LOGD ("Cleanup DBus client watch"); + + g_clear_pointer (&priv->dbus_client, g_free); + + if (priv->dbus_connection) + g_dbus_connection_signal_unsubscribe (priv->dbus_connection, + priv->subscription_id); + g_clear_object (&priv->dbus_connection); +} + +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) +{ + NMKeepAlive *self = NM_KEEP_ALIVE (user_data); + + const char *old_owner; + const char *new_owner; + + g_variant_get (parameters, "(&s&s&s)", NULL, &old_owner, &new_owner); + + if (g_strcmp0 (new_owner, "") != 0) + return; + + _LOGD ("DBus client for keep alive disappeared from bus"); + + cleanup_dbus_watch (self); + + _notify (self, PROP_ALIVE); +} + +void +nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self, + GDBusConnection *connection, + const char *client_address, + GError **error) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + cleanup_dbus_watch (self); + + if (client_address == NULL) + return; + + _LOGD ("Registering dbus client watch for keep alive"); + + priv->dbus_client = g_strdup (client_address); + priv->dbus_connection = g_object_ref (connection); + priv->subscription_id = + g_dbus_connection_signal_subscribe (connection, "org.freedesktop.DBus", + "org.freedesktop.DBus", "NameOwnerChanged", "/org/freedesktop/DBus", + priv->dbus_client, G_DBUS_SIGNAL_FLAGS_NONE, + name_owner_changed_cb, self, NULL); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMKeepAlive *self = NM_KEEP_ALIVE (object); + + switch (prop_id) { + case PROP_ALIVE: + g_value_set_boolean (value, nm_keep_alive_is_alive (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_keep_alive_init (NMKeepAlive *self) +{ + /* Nothing to do */ +} + +static void +dispose (GObject *object) +{ + NMKeepAlive *self = NM_KEEP_ALIVE (object); + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + g_clear_object (&priv->connection); + + cleanup_dbus_watch (self); +} + +static void +nm_keep_alive_class_init (NMKeepAliveClass *keep_alive_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (keep_alive_class); + + object_class->get_property = get_property; + object_class->dispose = dispose; + + obj_properties[PROP_ALIVE] = + g_param_spec_string (NM_KEEP_ALIVE_ALIVE, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/nm-keep-alive.h b/src/nm-keep-alive.h new file mode 100644 index 0000000000..5ca02a3c5a --- /dev/null +++ b/src/nm-keep-alive.h @@ -0,0 +1,56 @@ +/* + * NetworkManager -- Inhibition management + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_KEEP_ALIVE_H__ +#define __NETWORKMANAGER_KEEP_ALIVE_H__ + +#define NM_TYPE_KEEP_ALIVE (nm_keep_alive_get_type ()) +#define NM_KEEP_ALIVE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_KEEP_ALIVE, NMKeepAlive)) +#define NM_KEEP_ALIVE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), NM_TYPE_KEEP_ALIVE, NMKeepAliveClass)) +#define NM_KEEP_ALIVE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_KEEP_ALIVE, NMKeepAliveClass)) +#define NM_IS_KEEP_ALIVE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_KEEP_ALIVE)) +#define NM_IS_KEEP_ALIVE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_KEEP_ALIVE)) + + +#define NM_KEEP_ALIVE_ALIVE "alive" + +typedef struct _NMKeepAliveClass NMKeepAliveClass; + +GType nm_keep_alive_get_type (void) G_GNUC_CONST; + +NMKeepAlive* nm_keep_alive_new (gboolean floating); + +gboolean nm_keep_alive_is_alive (NMKeepAlive *self); + +void nm_keep_alive_sink (NMKeepAlive *self); + +void nm_keep_alive_set_forced (NMKeepAlive *self, + gboolean forced); + +void nm_keep_alive_set_settings_connection_watch_visible (NMKeepAlive *self, + NMSettingsConnection *connection); + +void nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self, + GDBusConnection *connection, + const char *client_address, + GError **error); + +#endif /* __NETWORKMANAGER_KEEP_ALIVE_H__ */ diff --git a/src/nm-policy.c b/src/nm-policy.c index 5ac40dca53..323df112de 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -2182,6 +2182,32 @@ active_connection_state_changed (NMActiveConnection *active, process_secondaries (self, active, FALSE); } +static void +active_connection_keep_alive_changed (NMActiveConnection *ac, + GParamSpec *pspec, + NMPolicy *self) +{ + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + GError *error = NULL; + + /* The connection does not have a reason to stay alive anymore. */ + if (!nm_active_connection_get_keep_alive (ac)) { + if (nm_active_connection_get_state (ac) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + if (!nm_manager_deactivate_connection (priv->manager, + ac, + NM_DEVICE_STATE_REASON_CONNECTION_REMOVED, + &error)) { + _LOGW (LOGD_DEVICE, "connection '%s' is no longer kept alive, but error deactivating it: (%d) %s", + nm_active_connection_get_settings_connection_id (ac), + error ? error->code : -1, + error ? error->message : "(unknown)"); + g_clear_error (&error); + } + } + } + +} + static void active_connection_added (NMManager *manager, NMActiveConnection *active, @@ -2202,6 +2228,10 @@ active_connection_added (NMManager *manager, g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_STATE, G_CALLBACK (active_connection_state_changed), self); + g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_INT_KEEP_ALIVE, + G_CALLBACK (active_connection_keep_alive_changed), + self); + active_connection_keep_alive_changed (active, NULL, self); } static void @@ -2404,8 +2434,7 @@ connection_flags_changed (NMSettings *settings, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) { if (!nm_settings_connection_autoconnect_is_blocked (connection)) schedule_activate_all (self); - } else - _deactivate_if_active (self, connection); + } } static void diff --git a/src/nm-types.h b/src/nm-types.h index 676ca64169..277e0f6ccf 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -51,6 +51,7 @@ typedef struct _NMPolicy NMPolicy; typedef struct _NMRfkillManager NMRfkillManager; typedef struct _NMPacrunnerManager NMPacrunnerManager; typedef struct _NMSessionMonitor NMSessionMonitor; +typedef struct _NMKeepAlive NMKeepAlive; typedef struct _NMSleepMonitor NMSleepMonitor; typedef struct _NMLldpListener NMLldpListener; typedef struct _NMConfigDeviceStateData NMConfigDeviceStateData;