summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-11-19 11:00:37 +0100
committerThomas Haller <thaller@redhat.com>2018-11-19 11:00:37 +0100
commitda05a6b5e5c3db070be3ef459db0e9c2bb9503ac (patch)
tree709b7385d24081b8ca8b25267861e690d0768a23
parentfaba9b12def20151ced42de28d183ebc3420ebcf (diff)
parent26eaca89b84082d25e1b73a4c6c2c1fffdf26f02 (diff)
downloadNetworkManager-da05a6b5e5c3db070be3ef459db0e9c2bb9503ac.tar.gz
all: merge branch 'benzea/dbus-service-specific-connection'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/37
-rw-r--r--Makefile.am2
-rw-r--r--introspection/org.freedesktop.NetworkManager.xml31
-rw-r--r--libnm/libnm.ver2
-rw-r--r--libnm/nm-client.c110
-rw-r--r--libnm/nm-client.h14
-rw-r--r--libnm/nm-manager.c18
-rw-r--r--libnm/nm-manager.h1
-rw-r--r--src/meson.build1
-rw-r--r--src/nm-active-connection.c58
-rw-r--r--src/nm-active-connection.h7
-rw-r--r--src/nm-keep-alive.c313
-rw-r--r--src/nm-keep-alive.h55
-rw-r--r--src/nm-manager.c101
-rw-r--r--src/nm-policy.c31
-rw-r--r--src/nm-types.h1
-rwxr-xr-xtools/test-networkmanager-service.py6
16 files changed, 734 insertions, 17 deletions
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/introspection/org.freedesktop.NetworkManager.xml b/introspection/org.freedesktop.NetworkManager.xml
index 6c6ec282a6..f78a989a4b 100644
--- a/introspection/org.freedesktop.NetworkManager.xml
+++ b/introspection/org.freedesktop.NetworkManager.xml
@@ -100,6 +100,8 @@
(automatically filling in missing settings with the capabilities of the
given device and specific object), then activate the new connection.
Cannot be used for VPN connections at this time.
+
+ See also AddAndActivateConnection2.
-->
<method name="AddAndActivateConnection">
<arg name="connection" type="a{sa{sv}}" direction="in"/>
@@ -110,6 +112,35 @@
</method>
<!--
+ AddAndActivateConnection2:
+ @connection: Connection settings and properties; if incomplete missing settings will be automatically completed using the given device and specific object.
+ @device: The object path of device to be activated using the given connection.
+ @specific_object: The path of a connection-type-specific object this activation should use. This parameter is currently ignored for wired and mobile broadband connections, and the value of "/" should be used (ie, no specific object). For WiFi connections, pass the object path of a specific AP from the card's scan list, which will be used to complete the details of the newly added connection.
+ @options: Further options for the method call.
+ @path: Object path of the new connection that was just added.
+ @active_connection: The path of the active connection object representing this active connection.
+
+ Adds a new connection using the given details (if any) as a template
+ (automatically filling in missing settings with the capabilities of the
+ given device and specific object), then activate the new connection.
+ Cannot be used for VPN connections at this time.
+
+ This method extends AddAndActivateConnection to allow passing further
+ parameters. At this time the following options are supported:
+
+ * persist: A string value of either "disk" (default), "memory" or "volatile". If "memory" is passed, the connection will not be saved to disk. If "volatile" is passed, the connection will not be saved to disk and will be destroyed when disconnected.
+ * bind: Bind the activation lifetime. Set to "dbus-name" to automatically disconnect when the requesting process disappears from the bus. The default of "none" means the connection is kept activated normally.
+ -->
+ <method name="AddAndActivateConnection2">
+ <arg name="connection" type="a{sa{sv}}" direction="in"/>
+ <arg name="device" type="o" direction="in"/>
+ <arg name="specific_object" type="o" direction="in"/>
+ <arg name="options" type="a{sv}" direction="in"/>
+ <arg name="path" type="o" direction="out"/>
+ <arg name="active_connection" type="o" direction="out"/>
+ </method>
+
+ <!--
DeactivateConnection:
@active_connection: The currently active connection to deactivate.
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index cf57b12405..09681e4733 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -1447,5 +1447,7 @@ global:
libnm_1_16_0 {
global:
+ nm_client_add_and_activate_connection_options;
+ nm_client_add_and_activate_connection_options_finish;
nm_device_get_connectivity;
} libnm_1_14_0;
diff --git a/libnm/nm-client.c b/libnm/nm-client.c
index a669f3c5a9..d37feb100f 100644
--- a/libnm/nm-client.c
+++ b/libnm/nm-client.c
@@ -1233,7 +1233,7 @@ nm_client_add_and_activate_connection_async (NMClient *client,
if (cancellable)
g_simple_async_result_set_check_cancellable (simple, cancellable);
nm_manager_add_and_activate_connection_async (NM_CLIENT_GET_PRIVATE (client)->manager,
- partial, device, specific_object,
+ partial, device, specific_object, NULL,
cancellable, add_activate_cb, simple);
}
@@ -1269,6 +1269,114 @@ nm_client_add_and_activate_connection_finish (NMClient *client,
}
/**
+ * nm_client_add_and_activate_connection_options:
+ * @client: a #NMClient
+ * @partial: (allow-none): an #NMConnection to add; the connection may be
+ * partially filled (or even %NULL) and will be completed by NetworkManager
+ * using the given @device and @specific_object before being added
+ * @device: the #NMDevice
+ * @specific_object: (allow-none): the object path of a connection-type-specific
+ * object this activation should use. This parameter is currently ignored for
+ * wired and mobile broadband connections, and the value of %NULL should be used
+ * (ie, no specific object). For Wi-Fi or WiMAX connections, pass the object
+ * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can
+ * get using nm_object_get_path(), and which will be used to complete the
+ * details of the newly added connection.
+ * @options: a #GVariant containing a dictionary with options, or %NULL
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to be called when the activation has started
+ * @user_data: caller-specific data passed to @callback
+ *
+ * Adds a new connection using the given details (if any) as a template,
+ * automatically filling in missing settings with the capabilities of the given
+ * device and specific object. The new connection is then asynchronously
+ * activated as with nm_client_activate_connection_async(). Cannot be used for
+ * VPN connections at this time.
+ *
+ * Note that the callback is invoked when NetworkManager has started activating
+ * the new connection, not when it finishes. You can used the returned
+ * #NMActiveConnection object (in particular, #NMActiveConnection:state) to
+ * track the activation to its completion.
+ *
+ * This is identitcal to nm_client_add_and_activate_connection_async() but takes
+ * a further @options parameter. Currently the following options are supported
+ * by the daemon:
+ * * "persist": A string describing how the connection should be stored.
+ * The default is "disk", but it can be modified to "memory" (until
+ * the daemon quits) or "volatile" (will be deleted on disconnect).
+ * * "bind": Bind the connection lifetime to something. The default is "none",
+ * meaning an explicit disconnect is needed. The value "dbus-client"
+ * means the connection will automatically be closed when the calling
+ * DBus client disappears from the system bus.
+ * A non-default "bind" option must always be used together with
+ * "persist" set to "volatile".
+ *
+ * Since: 1.16
+ **/
+void
+nm_client_add_and_activate_connection_options (NMClient *client,
+ NMConnection *partial,
+ NMDevice *device,
+ const char *specific_object,
+ GVariant *options,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ GError *error = NULL;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+ g_return_if_fail (NM_IS_DEVICE (device));
+ if (partial)
+ g_return_if_fail (NM_IS_CONNECTION (partial));
+
+ if (!_nm_client_check_nm_running (client, &error)) {
+ g_simple_async_report_take_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
+ return;
+ }
+
+ simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
+ nm_client_add_and_activate_connection_options);
+ if (cancellable)
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+ nm_manager_add_and_activate_connection_async (NM_CLIENT_GET_PRIVATE (client)->manager,
+ partial, device, specific_object, options,
+ cancellable, add_activate_cb, simple);
+}
+
+/**
+ * nm_client_add_and_activate_connection_options_finish:
+ * @client: an #NMClient
+ * @result: the result passed to the #GAsyncReadyCallback
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of a call to nm_client_add_and_activate_connection_options().
+ *
+ * You can call nm_active_connection_get_connection() on the returned
+ * #NMActiveConnection to find the path of the created #NMConnection.
+ *
+ * Returns: (transfer full): the new #NMActiveConnection on success, %NULL on
+ * failure, in which case @error will be set.
+ **/
+NMActiveConnection *
+nm_client_add_and_activate_connection_options_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+ else
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+/**
* nm_client_deactivate_connection:
* @client: a #NMClient
* @active: the #NMActiveConnection to deactivate
diff --git a/libnm/nm-client.h b/libnm/nm-client.h
index 6259b1227b..59567c1737 100644
--- a/libnm/nm-client.h
+++ b/libnm/nm-client.h
@@ -339,6 +339,20 @@ NMActiveConnection *nm_client_add_and_activate_connection_finish (NMClient *clie
GAsyncResult *result,
GError **error);
+NM_AVAILABLE_IN_1_16
+void nm_client_add_and_activate_connection_options (NMClient *client,
+ NMConnection *partial,
+ NMDevice *device,
+ const char *specific_object,
+ GVariant *options,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NM_AVAILABLE_IN_1_16
+NMActiveConnection *nm_client_add_and_activate_connection_options_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error);
+
gboolean nm_client_deactivate_connection (NMClient *client,
NMActiveConnection *active,
GCancellable *cancellable,
diff --git a/libnm/nm-manager.c b/libnm/nm-manager.c
index a542512742..235dc4aed1 100644
--- a/libnm/nm-manager.c
+++ b/libnm/nm-manager.c
@@ -1098,6 +1098,7 @@ nm_manager_add_and_activate_connection_async (NMManager *manager,
NMConnection *partial,
NMDevice *device,
const char *specific_object,
+ GVariant *options,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -1127,13 +1128,16 @@ nm_manager_add_and_activate_connection_async (NMManager *manager,
dict = nm_connection_to_dbus (partial, NM_CONNECTION_SERIALIZE_ALL);
if (!dict)
dict = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0);
-
- nmdbus_manager_call_add_and_activate_connection (priv->proxy,
- dict,
- nm_object_get_path (NM_OBJECT (device)),
- specific_object ?: "/",
- cancellable,
- add_activate_cb, info);
+ if (!options)
+ options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
+
+ nmdbus_manager_call_add_and_activate_connection2 (priv->proxy,
+ dict,
+ nm_object_get_path (NM_OBJECT (device)),
+ specific_object ?: "/",
+ options,
+ cancellable,
+ add_activate_cb, info);
}
NMActiveConnection *
diff --git a/libnm/nm-manager.h b/libnm/nm-manager.h
index 0a278aee58..26b7ef412a 100644
--- a/libnm/nm-manager.h
+++ b/libnm/nm-manager.h
@@ -163,6 +163,7 @@ void nm_manager_add_and_activate_connection_async (NMManager *ma
NMConnection *partial,
NMDevice *device,
const char *specific_object,
+ GVariant *options,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
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..ebe911e477 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 {
@@ -174,6 +177,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,
gpointer user_data)
@@ -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
@@ -995,6 +1014,23 @@ nm_active_connection_set_parent (NMActiveConnection *self, NMActiveConnection *p
g_object_weak_ref ((GObject *) priv->parent, parent_destroyed, self);
}
+/**
+ * nm_active_connection_bind_dbus_client:
+ * @self: the #NMActiveConnection
+ * @dbus_client: The dbus client to watch.
+ *
+ * Binds the lifetime of this active connection to the given dbus client. If
+ * the dbus client disappears, then the connection will be disconnected.
+ */
+void
+nm_active_connection_bind_dbus_client (NMActiveConnection *self, GDBusConnection *dbus_con, const char *dbus_client)
+{
+ NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
+
+ nm_keep_alive_set_dbus_client_watch (priv->keep_alive, dbus_con, dbus_client);
+ nm_keep_alive_sink (priv->keep_alive);
+}
+
/*****************************************************************************/
static void
@@ -1293,6 +1329,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 +1439,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 +1476,13 @@ constructed (GObject *object)
g_return_if_fail (priv->subject);
g_return_if_fail (priv->activation_reason != NM_ACTIVATION_REASON_UNSET);
+
+ if (NM_IN_SET ((NMActivationReason) priv->activation_reason,
+ NM_ACTIVATION_REASON_AUTOCONNECT,
+ 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 +1526,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 +1737,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..5b2e421507 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,12 @@ 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);
+void nm_active_connection_bind_dbus_client (NMActiveConnection *self,
+ GDBusConnection *dbus_con,
+ const char *dbus_name);
+
#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..71cd43415c
--- /dev/null
+++ b/src/nm-keep-alive.c
@@ -0,0 +1,313 @@
+/*
+ * 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 <string.h>
+
+#include "settings/nm-settings-connection.h"
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMKeepAlive,
+ PROP_ALIVE,
+);
+
+typedef struct {
+ NMSettingsConnection *connection;
+ GDBusConnection *dbus_connection;
+ char *dbus_client;
+
+ guint subscription_id;
+
+ bool floating:1;
+ bool forced:1;
+ bool alive: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 (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;
+}
+
+static void
+_notify_alive (NMKeepAlive *self)
+{
+ NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
+
+ if (priv->alive == _is_alive (self))
+ return;
+ priv->alive = !priv->alive;
+ _notify (self, PROP_ALIVE);
+}
+
+gboolean
+nm_keep_alive_is_alive (NMKeepAlive *self)
+{
+ return NM_KEEP_ALIVE_GET_PRIVATE (self)->alive;
+}
+
+/*****************************************************************************/
+
+void
+nm_keep_alive_sink (NMKeepAlive *self)
+{
+ NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
+
+ if (priv->floating) {
+ priv->floating = FALSE;
+ _notify_alive (self);
+ }
+}
+
+void
+nm_keep_alive_set_forced (NMKeepAlive *self, gboolean forced)
+{
+ NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
+
+ if (priv->forced != (!!forced)) {
+ priv->forced = forced;
+ _notify_alive (self);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+connection_flags_changed (NMSettingsConnection *connection,
+ NMKeepAlive *self)
+{
+ _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->connection = g_object_ref (connection);
+ 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
+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_free (&priv->dbus_client);
+ 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 (!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);
+
+ cleanup_dbus_watch (self);
+
+ if (client_address) {
+ _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);
+ /* FIXME: is there are race here and is it possible that name-owner is already gone?
+ * Do we need a GetNameOwner first? */
+ }
+
+ _notify_alive (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;
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_keep_alive_init (NMKeepAlive *self)
+{
+ nm_assert (NM_KEEP_ALIVE_GET_PRIVATE (self)->alive == _is_alive (self));
+}
+
+NMKeepAlive *
+nm_keep_alive_new (gboolean floating)
+{
+ NMKeepAlive *self = g_object_new (NM_TYPE_KEEP_ALIVE, NULL);
+ NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self);
+
+ priv->floating = floating;
+ priv->alive = TRUE;
+ nm_assert (priv->alive == _is_alive (self));
+ return self;
+}
+
+static void
+dispose (GObject *object)
+{
+ NMKeepAlive *self = NM_KEEP_ALIVE (object);
+
+ _set_settings_connection_watch_visible (self, NULL, FALSE);
+ 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..b8c26e173d
--- /dev/null
+++ b/src/nm-keep-alive.h
@@ -0,0 +1,55 @@
+/*
+ * 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);
+
+#endif /* __NETWORKMANAGER_KEEP_ALIVE_H__ */
diff --git a/src/nm-manager.c b/src/nm-manager.c
index d2fad95519..c960155fde 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -95,6 +95,7 @@ typedef struct {
struct {
GDBusMethodInvocation *invocation;
NMConnection *connection;
+ NMSettingsConnectionPersistMode persist;
} add_and_activate;
};
} ac_auth;
@@ -370,6 +371,7 @@ static void _add_and_activate_auth_done (NMManager *self,
NMActiveConnection *active,
NMConnection *connection,
GDBusMethodInvocation *invocation,
+ NMSettingsConnectionPersistMode persist,
gboolean success,
const char *error_desc);
static void _activation_auth_done (NMManager *self,
@@ -484,7 +486,8 @@ static AsyncOpData *
_async_op_data_new_ac_auth_add_and_activate (NMManager *self,
NMActiveConnection *active_take,
GDBusMethodInvocation *invocation_take,
- NMConnection *connection_take)
+ NMConnection *connection_take,
+ NMSettingsConnectionPersistMode persist)
{
AsyncOpData *async_op_data;
@@ -494,6 +497,7 @@ _async_op_data_new_ac_auth_add_and_activate (NMManager *self,
async_op_data->ac_auth.active = active_take;
async_op_data->ac_auth.add_and_activate.invocation = invocation_take;
async_op_data->ac_auth.add_and_activate.connection = connection_take;
+ async_op_data->ac_auth.add_and_activate.persist = persist;
c_list_link_tail (&NM_MANAGER_GET_PRIVATE (self)->async_op_lst_head, &async_op_data->async_op_lst);
return async_op_data;
}
@@ -533,6 +537,7 @@ _async_op_complete_ac_auth_cb (NMActiveConnection *active,
async_op_data->ac_auth.active,
async_op_data->ac_auth.add_and_activate.connection,
async_op_data->ac_auth.add_and_activate.invocation,
+ async_op_data->ac_auth.add_and_activate.persist,
success,
error_desc);
g_object_unref (async_op_data->ac_auth.add_and_activate.connection);
@@ -5117,8 +5122,11 @@ activation_add_done (NMSettings *settings,
NMManager *self;
gs_unref_object NMActiveConnection *active = NULL;
gs_free_error GError *local = NULL;
+ gpointer persist_ptr;
+ NMSettingsConnectionPersistMode persist;
- nm_utils_user_data_unpack (user_data, &self, &active);
+ nm_utils_user_data_unpack (user_data, &self, &active, &persist_ptr);
+ persist = GPOINTER_TO_INT (persist_ptr);
if (!error) {
nm_active_connection_set_settings_connection (active, new_connection);
@@ -5126,7 +5134,7 @@ activation_add_done (NMSettings *settings,
if (_internal_activate_generic (self, active, &local)) {
nm_settings_connection_update (new_connection,
NULL,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+ persist,
NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED,
"add-and-activate",
NULL);
@@ -5167,6 +5175,7 @@ _add_and_activate_auth_done (NMManager *self,
NMActiveConnection *active,
NMConnection *connection,
GDBusMethodInvocation *invocation,
+ NMSettingsConnectionPersistMode persist,
gboolean success,
const char *error_desc)
{
@@ -5199,7 +5208,8 @@ _add_and_activate_auth_done (NMManager *self,
invocation,
activation_add_done,
nm_utils_user_data_pack (self,
- g_object_ref (active)));
+ g_object_ref (active),
+ GINT_TO_POINTER (persist)));
}
static void
@@ -5214,17 +5224,74 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
NMManager *self = NM_MANAGER (obj);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
gs_unref_object NMConnection *incompl_conn = NULL;
- NMActiveConnection *active = NULL;
+ gs_unref_object NMActiveConnection *active = NULL;
gs_unref_object NMAuthSubject *subject = NULL;
GError *error = NULL;
NMDevice *device = NULL;
gboolean is_vpn = FALSE;
gs_unref_variant GVariant *settings = NULL;
+ gs_unref_variant GVariant *options = NULL;
const char *device_path;
const char *specific_object_path;
gs_free NMConnection **conns = NULL;
+ NMSettingsConnectionPersistMode persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ gboolean bind_dbus_client = FALSE;
- g_variant_get (parameters, "(@a{sa{sv}}&o&o)", &settings, &device_path, &specific_object_path);
+ if (g_strcmp0 (method_info->parent.name, "AddAndActivateConnection2") == 0)
+ g_variant_get (parameters, "(@a{sa{sv}}&o&o@a{sv})", &settings, &device_path, &specific_object_path, &options);
+ else
+ g_variant_get (parameters, "(@a{sa{sv}}&o&o)", &settings, &device_path, &specific_object_path);
+
+ if (options) {
+ GVariantIter iter;
+ const char *option_name;
+ GVariant *option_value;
+
+ g_variant_iter_init (&iter, options);
+ while (g_variant_iter_next (&iter, "{&sv}", &option_name, &option_value)) {
+ gs_unref_variant GVariant *option_value_free = NULL;
+ const char *s;
+
+ option_value_free = option_value;
+
+ if ( nm_streq (option_name, "persist")
+ && g_variant_is_of_type (option_value, G_VARIANT_TYPE_STRING)) {
+ s = g_variant_get_string (option_value, NULL);
+
+ if (nm_streq (s, "volatile"))
+ persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY;
+ else if (nm_streq (s, "memory"))
+ persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+ else if (nm_streq (s, "disk"))
+ persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ else {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+ "Option \"persist\" must be one of \"volatile\", \"memory\" or \"disk\"");
+ goto error;
+ }
+ } else if ( nm_streq (option_name, "bind")
+ && g_variant_is_of_type (option_value, G_VARIANT_TYPE_STRING)) {
+ s = g_variant_get_string (option_value, NULL);
+
+ if (nm_streq (s, "dbus-client"))
+ bind_dbus_client = TRUE;
+ else if (nm_streq (s, "none"))
+ bind_dbus_client = FALSE;
+ else {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+ "Option \"bind\" must be one of \"dbus-client\" or \"none\"");
+ goto error;
+ }
+ } else {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_INVALID_ARGUMENTS,
+ "Unknown extra option passed");
+ goto error;
+ }
+ }
+ }
specific_object_path = nm_utils_dbus_normalize_object_path (specific_object_path);
device_path = nm_utils_dbus_normalize_object_path (device_path);
@@ -5296,13 +5363,17 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
if (!active)
goto error;
+ if (bind_dbus_client)
+ nm_active_connection_bind_dbus_client (active, dbus_connection, sender);
+
nm_active_connection_authorize (active,
incompl_conn,
_async_op_complete_ac_auth_cb,
_async_op_data_new_ac_auth_add_and_activate (self,
active,
invocation,
- incompl_conn));
+ incompl_conn,
+ persist));
/* we passed the pointers on to _async_op_data_new_ac_auth_add_and_activate() */
g_steal_pointer (&incompl_conn);
@@ -7651,6 +7722,22 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = {
),
NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
NM_DEFINE_GDBUS_METHOD_INFO_INIT (
+ "AddAndActivateConnection2",
+ .in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+ NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"),
+ NM_DEFINE_GDBUS_ARG_INFO ("device", "o"),
+ NM_DEFINE_GDBUS_ARG_INFO ("specific_object", "o"),
+ NM_DEFINE_GDBUS_ARG_INFO ("options", "a{sv}"),
+ ),
+ .out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+ NM_DEFINE_GDBUS_ARG_INFO ("path", "o"),
+ NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"),
+ ),
+ ),
+ .handle = impl_manager_add_and_activate_connection,
+ ),
+ NM_DEFINE_DBUS_METHOD_INFO_EXTENDED (
+ NM_DEFINE_GDBUS_METHOD_INFO_INIT (
"DeactivateConnection",
.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"),
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 5ac40dca53..5199afbf1c 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -2183,6 +2183,30 @@ active_connection_state_changed (NMActiveConnection *active,
}
static void
+active_connection_keep_alive_changed (NMActiveConnection *ac,
+ GParamSpec *pspec,
+ NMPolicy *self)
+{
+ NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+ GError *error = NULL;
+
+ if (nm_active_connection_get_keep_alive (ac))
+ return;
+
+ 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: %s",
+ nm_active_connection_get_settings_connection_id (ac),
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+}
+
+static void
active_connection_added (NMManager *manager,
NMActiveConnection *active,
gpointer user_data)
@@ -2202,6 +2226,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 +2432,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;
diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py
index 9aa44a906e..76601d0172 100755
--- a/tools/test-networkmanager-service.py
+++ b/tools/test-networkmanager-service.py
@@ -1341,6 +1341,12 @@ class NetworkManager(ExportedObj):
@dbus.service.method(dbus_interface=IFACE_NM, in_signature='a{sa{sv}}oo', out_signature='oo')
def AddAndActivateConnection(self, con_hash, devpath, specific_object):
+ return self.AddAndActivateConnection2(con_hash, devpath, specific_object, dict())
+
+ @dbus.service.method(dbus_interface=IFACE_NM, in_signature='a{sa{sv}}ooa{sv}', out_signature='oo')
+ def AddAndActivateConnection2(self, con_hash, devpath, specific_object, options):
+ # TODO: Do some processing of the "options" parameter.
+
device = self.find_device_first(path = devpath, require = BusErr.UnknownDeviceException)
conpath = gl.settings.AddConnection(con_hash)
return (conpath, self.ActivateConnection(conpath, devpath, specific_object))