mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-26 03:00:13 +01:00
```bash
readarray -d '' FILES < <(
git ls-files -z \
':(exclude)po' \
':(exclude)shared/c-rbtree' \
':(exclude)shared/c-list' \
':(exclude)shared/c-siphash' \
':(exclude)shared/c-stdaux' \
':(exclude)shared/n-acd' \
':(exclude)shared/n-dhcp4' \
':(exclude)src/systemd/src' \
':(exclude)shared/systemd/src' \
':(exclude)m4' \
':(exclude)COPYING*'
)
sed \
-e 's/^\(--\|#\| \*\) *\(([cC]) *\)\?Copyright \+\(\(([cC])\) \+\)\?\(\(20\|19\)[0-9][0-9]\) *[-–] *\(\(20\|19\)[0-9][0-9]\) \+\([^ ].*\)$/\1 C1pyright#\5 - \7#\9/' \
-e 's/^\(--\|#\| \*\) *\(([cC]) *\)\?Copyright \+\(\(([cC])\) \+\)\?\(\(20\|19\)[0-9][0-9]\) *[,] *\(\(20\|19\)[0-9][0-9]\) \+\([^ ].*\)$/\1 C2pyright#\5, \7#\9/' \
-e 's/^\(--\|#\| \*\) *\(([cC]) *\)\?Copyright \+\(\(([cC])\) \+\)\?\(\(20\|19\)[0-9][0-9]\) \+\([^ ].*\)$/\1 C3pyright#\5#\7/' \
-e 's/^Copyright \(\(20\|19\)[0-9][0-9]\) \+\([^ ].*\)$/C4pyright#\1#\3/' \
-i \
"${FILES[@]}"
echo ">>> untouched Copyright lines"
git grep Copyright "${FILES[@]}"
echo ">>> Copyright lines with unusual extra"
git grep '\<C[0-9]pyright#' "${FILES[@]}" | grep -i reserved
sed \
-e 's/\<C[0-9]pyright#\([^#]*\)#\(.*\)$/Copyright (C) \1 \2/' \
-i \
"${FILES[@]}"
```
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/298
512 lines
15 KiB
C
512 lines
15 KiB
C
// SPDX-License-Identifier: LGPL-2.1+
|
|
/*
|
|
* Copyright (C) 2018 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nm-keep-alive.h"
|
|
|
|
#include "settings/nm-settings-connection.h"
|
|
#include "nm-glib-aux/nm-dbus-aux.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
NM_GOBJECT_PROPERTIES_DEFINE (NMKeepAlive,
|
|
PROP_ALIVE,
|
|
);
|
|
|
|
typedef struct {
|
|
GObject *owner;
|
|
|
|
NMSettingsConnection *connection;
|
|
GDBusConnection *dbus_connection;
|
|
char *dbus_client;
|
|
|
|
GCancellable *dbus_client_confirm_cancellable;
|
|
guint subscription_id;
|
|
|
|
bool armed:1;
|
|
bool disarmed:1;
|
|
|
|
bool alive:1;
|
|
bool dbus_client_confirmed:1;
|
|
bool dbus_client_watching:1;
|
|
bool connection_was_visible:1;
|
|
} 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__)
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean _is_alive_dbus_client (NMKeepAlive *self);
|
|
static void cleanup_dbus_watch (NMKeepAlive *self);
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_is_alive (NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
nm_assert (!priv->disarmed);
|
|
|
|
if (!priv->armed) {
|
|
/* before arming, the instance is always alive. */
|
|
return TRUE;
|
|
}
|
|
|
|
if (priv->dbus_client_watching) {
|
|
if (_is_alive_dbus_client (self)) {
|
|
/* no matter what, the keep-alive is alive, because there is a D-Bus client
|
|
* still around keeping it alive. */
|
|
return TRUE;
|
|
}
|
|
/* the D-Bus client is gone. The only other binding (below) for the connection's
|
|
* visibility cannot keep the instance alive.
|
|
*
|
|
* As such, a D-Bus client watch is authoritative and overrules other conditions (that
|
|
* we have so far). */
|
|
return FALSE;
|
|
}
|
|
|
|
if ( priv->connection
|
|
&& priv->connection_was_visible
|
|
&& !NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->connection),
|
|
NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) {
|
|
/* note that we only declare the keep-alive as dead due to invisible
|
|
* connection, if
|
|
* (1) we monitor a connection, obviously
|
|
* (2) the connection was visible earlier and is no longer. It was
|
|
* was invisible all the time, it does not suffice.
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
/* by default, the instance is alive. */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_notify_alive (NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
if (priv->disarmed) {
|
|
/* once disarmed, the alive state is frozen. */
|
|
return;
|
|
}
|
|
|
|
if (priv->alive == _is_alive (self))
|
|
return;
|
|
priv->alive = !priv->alive;
|
|
_LOGD ("instance is now %s", priv->alive ? "alive" : "dead");
|
|
_notify (self, PROP_ALIVE);
|
|
}
|
|
|
|
gboolean
|
|
nm_keep_alive_is_alive (NMKeepAlive *self)
|
|
{
|
|
return NM_KEEP_ALIVE_GET_PRIVATE (self)->alive;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
connection_flags_changed (NMSettingsConnection *connection,
|
|
NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
if ( !priv->connection_was_visible
|
|
&& NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->connection),
|
|
NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) {
|
|
/* the profile was never visible but now it becomes visible.
|
|
* Remember that.
|
|
*
|
|
* Before this happens (that is, if the device was invisible all along),
|
|
* the keep alive instance is considered alive (w.r.t. watching the connection).
|
|
*
|
|
* The reason is to allow a user to manually activate an invisible profile and keep
|
|
* it alive. At least, as long until the user logs out the first time (which is the
|
|
* first time, the profiles changes from visible to invisible).
|
|
*
|
|
* Yes, that is odd. How to improve? */
|
|
priv->connection_was_visible = TRUE;
|
|
}
|
|
_notify_alive (self);
|
|
}
|
|
|
|
static void
|
|
_set_settings_connection_watch_visible (NMKeepAlive *self,
|
|
NMSettingsConnection *connection,
|
|
gboolean emit_signal)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
gs_unref_object NMSettingsConnection *old_connection = NULL;
|
|
|
|
if (priv->connection == connection)
|
|
return;
|
|
|
|
if (priv->connection) {
|
|
g_signal_handlers_disconnect_by_func (priv->connection,
|
|
G_CALLBACK (connection_flags_changed),
|
|
self);
|
|
old_connection = g_steal_pointer (&priv->connection);
|
|
}
|
|
|
|
if ( connection
|
|
&& !priv->disarmed) {
|
|
priv->connection = g_object_ref (connection);
|
|
priv->connection_was_visible = NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->connection),
|
|
NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE);
|
|
g_signal_connect (priv->connection,
|
|
NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
|
|
G_CALLBACK (connection_flags_changed),
|
|
self);
|
|
}
|
|
|
|
if (emit_signal)
|
|
_notify_alive (self);
|
|
}
|
|
|
|
void
|
|
nm_keep_alive_set_settings_connection_watch_visible (NMKeepAlive *self,
|
|
NMSettingsConnection *connection)
|
|
{
|
|
_set_settings_connection_watch_visible (self, connection, TRUE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
get_name_owner_cb (const char *name_owner,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
NMKeepAlive *self;
|
|
NMKeepAlivePrivate *priv;
|
|
|
|
if ( !name_owner
|
|
&& g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
return;
|
|
|
|
self = user_data;
|
|
priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
if ( name_owner
|
|
&& nm_streq (name_owner, priv->dbus_client)) {
|
|
/* all good, the name is confirmed. */
|
|
return;
|
|
}
|
|
|
|
_LOGD ("DBus client for keep alive is not on the bus");
|
|
cleanup_dbus_watch (self);
|
|
_notify_alive (self);
|
|
}
|
|
|
|
static gboolean
|
|
_is_alive_dbus_client (NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
if (!priv->dbus_client)
|
|
return FALSE;
|
|
|
|
if (!priv->dbus_client_confirmed) {
|
|
/* it's unconfirmed that the D-Bus client is really alive.
|
|
* It looks like it is, but as we are claiming that to be
|
|
* the case, issue an async GetNameOwner call to make sure. */
|
|
priv->dbus_client_confirmed = TRUE;
|
|
priv->dbus_client_confirm_cancellable = g_cancellable_new ();
|
|
|
|
nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
|
|
priv->dbus_client,
|
|
-1,
|
|
priv->dbus_client_confirm_cancellable,
|
|
get_name_owner_cb,
|
|
self);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
cleanup_dbus_watch (NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
if (!priv->dbus_client)
|
|
return;
|
|
|
|
_LOGD ("Cleanup DBus client watch");
|
|
|
|
nm_clear_g_cancellable (&priv->dbus_client_confirm_cancellable);
|
|
nm_clear_g_free (&priv->dbus_client);
|
|
if (priv->dbus_connection) {
|
|
g_dbus_connection_signal_unsubscribe (priv->dbus_connection,
|
|
nm_steal_int (&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 (!nm_streq0 (new_owner, ""))
|
|
return;
|
|
|
|
_LOGD ("DBus client for keep alive disappeared from bus");
|
|
cleanup_dbus_watch (self);
|
|
_notify_alive (self);
|
|
}
|
|
|
|
void
|
|
nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self,
|
|
GDBusConnection *connection,
|
|
const char *client_address)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
if (priv->disarmed)
|
|
return;
|
|
|
|
cleanup_dbus_watch (self);
|
|
|
|
if (client_address) {
|
|
_LOGD ("Registering dbus client watch for keep alive");
|
|
|
|
priv->dbus_client = g_strdup (client_address);
|
|
priv->dbus_client_watching = TRUE;
|
|
priv->dbus_client_confirmed = FALSE;
|
|
priv->dbus_connection = g_object_ref (connection);
|
|
priv->subscription_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
|
|
priv->dbus_client,
|
|
name_owner_changed_cb,
|
|
self,
|
|
NULL);
|
|
} else
|
|
priv->dbus_client_watching = FALSE;
|
|
|
|
_notify_alive (self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_keep_alive_arm:
|
|
* @self: the #NMKeepAlive
|
|
*
|
|
* A #NMKeepAlive instance is unarmed by default. That means, it's
|
|
* alive and stays alive until being armed. Arming means, that the conditions
|
|
* start to be actively evaluated, that the alive state may change, and
|
|
* that property changed signals are emitted.
|
|
*
|
|
* The opposite is nm_keep_alive_disarm() which freezes the alive state
|
|
* for good. Once disarmed, the instance cannot be armed again. Arming an
|
|
* instance multiple times has no effect. Arming an already disarmed instance
|
|
* also has no effect. */
|
|
void
|
|
nm_keep_alive_arm (NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
if (!priv->armed) {
|
|
priv->armed = TRUE;
|
|
_notify_alive (self);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* nm_keep_alive_disarm:
|
|
* @self: the #NMKeepAlive instance
|
|
*
|
|
* Once the instance is disarmed, it will not change its alive state
|
|
* anymore and will not emit anymore property changed signals about
|
|
* alive state changed.
|
|
*
|
|
* As such, it will also free internal resources (since they no longer
|
|
* affect the externally visible state).
|
|
*
|
|
* Once disarmed, the instance is frozen and cannot change anymore.
|
|
*/
|
|
void
|
|
nm_keep_alive_disarm (NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
priv->disarmed = TRUE;
|
|
|
|
/* release internal data. */
|
|
_set_settings_connection_watch_visible (self, NULL, FALSE);
|
|
cleanup_dbus_watch (self);
|
|
}
|
|
|
|
/**
|
|
* nm_keep_alive_destroy:
|
|
* @self: (allow-none): the #NMKeepAlive instance to destroy.
|
|
*
|
|
* This does 3 things in one:
|
|
*
|
|
* - set owner to %NULL
|
|
* - disarm the instance.
|
|
* - unref @self.
|
|
*/
|
|
void
|
|
nm_keep_alive_destroy (NMKeepAlive *self)
|
|
{
|
|
if (!self)
|
|
return;
|
|
_nm_keep_alive_set_owner (self, NULL);
|
|
nm_keep_alive_disarm (self);
|
|
g_object_unref (self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_keep_alive_get_owner:
|
|
* @self: the #NMKeepAlive
|
|
*
|
|
* Returns: the owner instance associated with this @self. This commonly
|
|
* is set to be the target instance, which @self guards for being alive.
|
|
* Returns a gpointer, but of course it's some GObject instance. */
|
|
gpointer /* GObject * */
|
|
nm_keep_alive_get_owner (NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
nm_assert (!priv->owner || G_IS_OBJECT (priv->owner));
|
|
|
|
return priv->owner;
|
|
}
|
|
|
|
/**
|
|
* _nm_keep_alive_set_owner:
|
|
* @self: the #NMKeepAlive
|
|
* @owner: the owner to set or unset.
|
|
*
|
|
* Sets or unsets the owner instance. Think of the owner the target
|
|
* instance that is guarded by @self. It's the responsibility of the
|
|
* owner to set and properly unset this pointer. As the owner also
|
|
* controls the lifetime of the NMKeepAlive instance.
|
|
*
|
|
* This API is not to be called by everybody, but only the owner of
|
|
* @self.
|
|
*/
|
|
void
|
|
_nm_keep_alive_set_owner (NMKeepAlive *self,
|
|
GObject *owner)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
nm_assert (!owner || G_IS_OBJECT (owner));
|
|
|
|
/* it's bad style to reset the owner object. You are supposed to
|
|
* set it once, and clear it once. That's it. */
|
|
nm_assert (!owner || !priv->owner);
|
|
|
|
/* optimally, we would take a reference to @owner. But the
|
|
* owner already owns a reference to the keep-alive, so we cannot
|
|
* just own a reference back.
|
|
*
|
|
* We could register a weak-pointer here. But instead, declare that
|
|
* owner is required to set itself as owner when creating the
|
|
* keep-alive instance, and unset itself when it lets go of the
|
|
* keep-alive instance (at latest, when the owner itself gets destroyed).
|
|
*/
|
|
priv->owner = owner;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
nm_keep_alive_init (NMKeepAlive *self)
|
|
{
|
|
NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
|
|
|
|
priv->alive = TRUE;
|
|
|
|
nm_assert (priv->alive == _is_alive (self));
|
|
}
|
|
|
|
NMKeepAlive *
|
|
nm_keep_alive_new (void)
|
|
{
|
|
return g_object_new (NM_TYPE_KEEP_ALIVE, NULL);
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
NMKeepAlive *self = NM_KEEP_ALIVE (object);
|
|
|
|
nm_assert (!NM_KEEP_ALIVE_GET_PRIVATE (self)->owner);
|
|
|
|
/* disarm also happens to free all resources. */
|
|
nm_keep_alive_disarm (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);
|
|
}
|