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;