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.
This commit is contained in:
Benjamin Berg 2018-10-10 17:10:01 +02:00 committed by Thomas Haller
parent faba9b12de
commit 37e8c53eee
8 changed files with 385 additions and 2 deletions

View file

@ -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 \

View file

@ -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',

View file

@ -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] =

View file

@ -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__ */

251
src/nm-keep-alive.c Normal file
View file

@ -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 <string.h>
/*****************************************************************************/
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);
}

56
src/nm-keep-alive.h Normal file
View file

@ -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__ */

View file

@ -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

View file

@ -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;