diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 28683c1dd4..aac4d7eadd 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -960,20 +960,25 @@ typedef enum { /*< flags >*/ * @NM_ACTIVATION_STATE_FLAG_IP6_READY: IPv6 setting is completed. * @NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES: The master has any slave devices attached. * This only makes sense if the device is a master. + * @NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY: the lifetime + * of the activation is bound to the visilibity of the connection profile, + * which in turn depends on "connection.permissions" and whether a session + * for the user exists. Since: 1.16 * * Flags describing the current activation state. * * Since: 1.10 **/ typedef enum { /*< flags >*/ - NM_ACTIVATION_STATE_FLAG_NONE = 0, + NM_ACTIVATION_STATE_FLAG_NONE = 0, - NM_ACTIVATION_STATE_FLAG_IS_MASTER = (1LL << 0), - NM_ACTIVATION_STATE_FLAG_IS_SLAVE = (1LL << 1), - NM_ACTIVATION_STATE_FLAG_LAYER2_READY = (1LL << 2), - NM_ACTIVATION_STATE_FLAG_IP4_READY = (1LL << 3), - NM_ACTIVATION_STATE_FLAG_IP6_READY = (1LL << 4), - NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES = (1LL << 5), + NM_ACTIVATION_STATE_FLAG_IS_MASTER = (1LL << 0), + NM_ACTIVATION_STATE_FLAG_IS_SLAVE = (1LL << 1), + NM_ACTIVATION_STATE_FLAG_LAYER2_READY = (1LL << 2), + NM_ACTIVATION_STATE_FLAG_IP4_READY = (1LL << 3), + NM_ACTIVATION_STATE_FLAG_IP6_READY = (1LL << 4), + NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES = (1LL << 5), + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY = (1LL << 6), } NMActivationStateFlags; /** diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index a6c2ea03e5..0f27ae3b01 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -67,6 +67,7 @@ #include "settings/nm-settings.h" #include "nm-setting-ethtool.h" #include "nm-auth-utils.h" +#include "nm-keep-alive.h" #include "nm-netns.h" #include "nm-dispatcher.h" #include "nm-config.h" @@ -2383,10 +2384,27 @@ nm_device_get_act_request (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->act_request.obj; } +NMActivationStateFlags +nm_device_get_activation_state_flags (NMDevice *self) +{ + NMActRequest *ac; + + g_return_val_if_fail (NM_IS_DEVICE (self), NM_ACTIVATION_STATE_FLAG_NONE); + + ac = NM_DEVICE_GET_PRIVATE (self)->act_request.obj; + if (!ac) + return NM_ACTIVATION_STATE_FLAG_NONE; + return nm_active_connection_get_state_flags (NM_ACTIVE_CONNECTION (ac)); +} + NMSettingsConnection * nm_device_get_settings_connection (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); return priv->act_request.obj ? nm_act_request_get_settings_connection (priv->act_request.obj) : NULL; } @@ -11619,14 +11637,15 @@ disconnect_cb (NMDevice *self, } static void -_clear_queued_act_request (NMDevicePrivate *priv) +_clear_queued_act_request (NMDevicePrivate *priv, + NMActiveConnectionStateReason active_reason) { if (priv->queued_act_request) { gs_unref_object NMActRequest *ac = NULL; ac = g_steal_pointer (&priv->queued_act_request); nm_active_connection_set_state_fail ((NMActiveConnection *) ac, - NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED, + active_reason, NULL); } } @@ -11768,7 +11787,8 @@ _carrier_wait_check_queued_act_request (NMDevice *self) priv->queued_act_request_is_waiting_for_carrier = FALSE; if (!priv->carrier) { _LOGD (LOGD_DEVICE, "Cancel queued activation request as we have no carrier after timeout"); - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); } else { gs_unref_object NMActRequest *queued_req = NULL; @@ -11823,7 +11843,9 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req) } void -nm_device_disconnect_active_connection (NMActiveConnection *active) +nm_device_disconnect_active_connection (NMActiveConnection *active, + NMDeviceStateReason device_reason, + NMActiveConnectionStateReason active_reason) { NMDevice *self; NMDevicePrivate *priv; @@ -11831,38 +11853,59 @@ nm_device_disconnect_active_connection (NMActiveConnection *active) g_return_if_fail (NM_IS_ACTIVE_CONNECTION (active)); self = nm_active_connection_get_device (active); - if (!self) { /* hm, no device? Just fail the active connection. */ - nm_active_connection_set_state_fail (active, - NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, - NULL); - return; + goto do_fail; } priv = NM_DEVICE_GET_PRIVATE (self); if (NM_ACTIVE_CONNECTION (priv->queued_act_request) == active) { - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, active_reason); return; } + if (NM_ACTIVE_CONNECTION (priv->act_request.obj) == active) { if (priv->state < NM_DEVICE_STATE_DEACTIVATING) { nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_NEW_ACTIVATION); + device_reason); } else { - /* it's going down already... */ + /* @active is the current ac of @self, but it's going down already. + * Nothing to do. */ } + return; } + + /* the active connection references this device, but it's neither the + * queued_act_request nor the current act_request. Just set it to fail... */ +do_fail: + nm_active_connection_set_state_fail (active, + active_reason, + NULL); } void nm_device_queue_activation (NMDevice *self, NMActRequest *req) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; gboolean must_queue; + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + nm_keep_alive_arm (nm_active_connection_get_keep_alive (NM_ACTIVE_CONNECTION (req))); + + if (nm_active_connection_get_state (NM_ACTIVE_CONNECTION (req)) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { + /* it's already deactivating. Nothing to do. */ + nm_assert (NM_IN_SET (nm_active_connection_get_device (NM_ACTIVE_CONNECTION (req)), NULL, self)); + return; + } + + nm_assert (self == nm_active_connection_get_device (NM_ACTIVE_CONNECTION (req))); + + priv = NM_DEVICE_GET_PRIVATE (self); + must_queue = _carrier_wait_check_act_request_must_queue (self, req); if ( !priv->act_request.obj @@ -11873,7 +11916,8 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) } /* supersede any already-queued request */ - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); priv->queued_act_request = g_object_ref (req); priv->queued_act_request_is_waiting_for_carrier = must_queue; @@ -14742,8 +14786,10 @@ _set_state_full (NMDevice *self, if (state <= NM_DEVICE_STATE_UNAVAILABLE) { if (available_connections_del_all (self)) _notify (self, PROP_AVAILABLE_CONNECTIONS); - if (old_state > NM_DEVICE_STATE_UNAVAILABLE) - _clear_queued_act_request (priv); + if (old_state > NM_DEVICE_STATE_UNAVAILABLE) { + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + } } /* Update the available connections list when a device first becomes available */ @@ -16188,7 +16234,8 @@ dispose (GObject *object) if (nm_clear_g_source (&priv->carrier_wait_id)) nm_device_remove_pending_action (self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); nm_clear_g_source (&priv->device_link_changed_id); nm_clear_g_source (&priv->device_ip_link_changed_id); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 828c22d94b..cd07e24630 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -543,6 +543,7 @@ NMConnection * nm_device_get_settings_connection_get_connection (NMDevice *self NMConnection * nm_device_get_applied_connection (NMDevice *dev); gboolean nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFlags compare_flags); +NMActivationStateFlags nm_device_get_activation_state_flags (NMDevice *self); gpointer /* (NMSetting *) */ nm_device_get_applied_setting (NMDevice *dev, GType setting_type); @@ -769,7 +770,9 @@ void nm_device_queue_state (NMDevice *self, gboolean nm_device_get_firmware_missing (NMDevice *self); -void nm_device_disconnect_active_connection (NMActiveConnection *active); +void nm_device_disconnect_active_connection (NMActiveConnection *active, + NMDeviceStateReason device_reason, + NMActiveConnectionStateReason active_reason); void nm_device_queue_activation (NMDevice *device, NMActRequest *req); diff --git a/src/nm-act-request.c b/src/nm-act-request.c index e4c65e59ea..cd81696490 100644 --- a/src/nm-act-request.c +++ b/src/nm-act-request.c @@ -539,6 +539,7 @@ nm_act_request_init (NMActRequest *req) * @subject: the #NMAuthSubject representing the requestor of the activation * @activation_type: the #NMActivationType * @activation_reason: the reason for activation + * @initial_state_flags: the initial state flags. * @device: the device/interface to configure according to @connection * * Creates a new device-based activation request. If an applied connection is @@ -553,6 +554,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, NMDevice *device) { g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); @@ -567,6 +569,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection, NM_ACTIVE_CONNECTION_INT_SUBJECT, subject, NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE, (int) activation_type, NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, (int) activation_reason, + NM_ACTIVE_CONNECTION_STATE_FLAGS, (guint) initial_state_flags, NULL); } diff --git a/src/nm-act-request.h b/src/nm-act-request.h index a8f09271a1..af2b749055 100644 --- a/src/nm-act-request.h +++ b/src/nm-act-request.h @@ -42,6 +42,7 @@ NMActRequest *nm_act_request_new (NMSettingsConnection *settings_connec NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, NMDevice *device); NMSettingsConnection *nm_act_request_get_settings_connection (NMActRequest *req); diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index ebe911e477..966a274351 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -105,7 +105,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection, PROP_INT_MASTER_READY, PROP_INT_ACTIVATION_TYPE, PROP_INT_ACTIVATION_REASON, - PROP_INT_KEEP_ALIVE, ); enum { @@ -164,26 +163,22 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_state_to_string, NMActiveConnectionState, ); #define state_to_string(state) NM_UTILS_LOOKUP_STR (_state_to_string, state) +/* the maximum required buffer size for _state_flags_to_string(). */ +#define _NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE (255) + NM_UTILS_FLAGS2STR_DEFINE_STATIC (_state_flags_to_string, NMActivationStateFlags, - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_NONE, "none"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_MASTER, "is-master"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_SLAVE, "is-slave"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LAYER2_READY, "layer2-ready"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP4_READY, "ip4-ready"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP6_READY, "ip6-ready"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES, "master-has-slaves"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_NONE, "none"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_MASTER, "is-master"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_SLAVE, "is-slave"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LAYER2_READY, "layer2-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP4_READY, "ip4-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP6_READY, "ip6-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES, "master-has-slaves"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, "lifetime-bound-to-profile-visibility"), ); /*****************************************************************************/ -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, @@ -264,6 +259,12 @@ nm_active_connection_set_state (NMActiveConnection *self, state_to_string (new_state), state_to_string (priv->state)); + if (new_state > NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + /* once we are about to deactivate, we don't need the keep-alive instance + * anymore. Freeze/disarm it. */ + nm_keep_alive_disarm (priv->keep_alive); + } + if ( new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED && priv->activation_type == NM_ACTIVATION_TYPE_ASSUME) { /* assuming connections mean to gracefully take over an externally @@ -364,14 +365,20 @@ nm_active_connection_set_state_flags_full (NMActiveConnection *self, f = (priv->state_flags & ~mask) | (state_flags & mask); if (f != priv->state_flags) { - char buf1[G_N_ELEMENTS (_nm_utils_to_string_buffer)]; - char buf2[G_N_ELEMENTS (_nm_utils_to_string_buffer)]; + char buf1[_NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE]; + char buf2[_NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE]; _LOGD ("set state-flags %s (was %s)", _state_flags_to_string (f, buf1, sizeof (buf1)), _state_flags_to_string (priv->state_flags, buf2, sizeof (buf2))); priv->state_flags = f; _notify (self, PROP_STATE_FLAGS); + + nm_keep_alive_set_settings_connection_watch_visible (priv->keep_alive, + NM_FLAGS_HAS (priv->state_flags, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY) + ? priv->settings_connection.obj + : NULL); } } @@ -915,12 +922,30 @@ nm_active_connection_get_activation_reason (NMActiveConnection *self) return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->activation_reason; } -gboolean +/*****************************************************************************/ + +/** + * nm_active_connection_get_keep_alive: + * @self: the #NMActiveConnection instance + * + * Gives the #NMKeepAlive instance of the active connection. Note that + * @self is guaranteed not to swap the keep-alive instance, so it is + * in particular safe to assume that the keep-alive instance is alive + * as long as @self, and that nm_active_connection_get_keep_alive() + * will return always the same instance. + * + * In particular this means, that it is safe and encouraged, that you + * register to the notify:alive property changed signal of the returned + * instance. + * + * Returns: the #NMKeepAlive instance. + */ +NMKeepAlive * nm_active_connection_get_keep_alive (NMActiveConnection *self) { - NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NULL); - return nm_keep_alive_is_alive (priv->keep_alive); + return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->keep_alive; } /*****************************************************************************/ @@ -1014,23 +1039,6 @@ 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 @@ -1329,9 +1337,6 @@ 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; @@ -1389,6 +1394,12 @@ set_property (GObject *object, guint prop_id, g_return_if_reached (); _set_activation_type (self, (NMActivationType) i); break; + case PROP_STATE_FLAGS: + /* construct-only */ + priv->state_flags = g_value_get_uint (value); + nm_assert ((guint) priv->state_flags == g_value_get_uint (value)); + nm_assert (!NM_FLAGS_ANY (priv->state_flags, ~NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY)); + break; case PROP_INT_ACTIVATION_REASON: /* construct-only */ i = g_value_get_int (value); @@ -1440,11 +1451,9 @@ 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); + /* the keep-alive instance must never change. Callers rely on that. */ + priv->keep_alive = nm_keep_alive_new (); + _nm_keep_alive_set_owner (priv->keep_alive, G_OBJECT (self)); } static void @@ -1474,15 +1483,12 @@ constructed (GObject *object) g_steal_pointer (&priv->applied_connection)); } + if (NM_FLAGS_HAS (priv->state_flags, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY)) + nm_keep_alive_set_settings_connection_watch_visible (priv->keep_alive, priv->settings_connection.obj); + 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 @@ -1526,6 +1532,8 @@ finalize (GObject *object) NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); nm_dbus_track_obj_path_set (&priv->settings_connection, NULL, FALSE); + + _nm_keep_alive_set_owner (priv->keep_alive, NULL); g_clear_object (&priv->keep_alive); G_OBJECT_CLASS (nm_active_connection_parent_class)->finalize (object); @@ -1632,7 +1640,7 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) obj_properties[PROP_STATE_FLAGS] = g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE_FLAGS, "", "", 0, G_MAXUINT32, NM_ACTIVATION_STATE_FLAG_NONE, - G_PARAM_READABLE | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_properties[PROP_DEFAULT] = @@ -1737,11 +1745,6 @@ 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 5b2e421507..1d4ce29e4e 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -59,7 +59,6 @@ #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" @@ -164,6 +163,13 @@ nm_active_connection_set_state_flags (NMActiveConnection *self, nm_active_connection_set_state_flags_full (self, state_flags, state_flags); } +static inline void +nm_active_connection_set_state_flags_clear (NMActiveConnection *self, + NMActivationStateFlags state_flags) +{ + nm_active_connection_set_state_flags_full (self, NM_ACTIVATION_STATE_FLAG_NONE, state_flags); +} + NMDevice * nm_active_connection_get_device (NMActiveConnection *self); gboolean nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device); @@ -186,12 +192,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); +NMKeepAlive *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-checkpoint.c b/src/nm-checkpoint.c index b0cf1f5117..5489ed4956 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -46,6 +46,7 @@ typedef struct { guint64 ac_version_id; NMDeviceState state; bool realized:1; + bool activation_lifetime_bound_to_profile_visiblity:1; NMUnmanFlagOp unmanaged_explicit; NMActivationReason activation_reason; } DeviceCheckpoint; @@ -335,6 +336,9 @@ activate: subject, NM_ACTIVATION_TYPE_MANAGED, dev_checkpoint->activation_reason, + dev_checkpoint->activation_lifetime_bound_to_profile_visiblity + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, &local_error)) { _LOGW ("rollback: reactivation of connection %s/%s failed: %s", nm_settings_connection_get_id (connection), @@ -439,6 +443,8 @@ device_checkpoint_create (NMDevice *device) dev_checkpoint->settings_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (settings_connection)); dev_checkpoint->ac_version_id = nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request)); dev_checkpoint->activation_reason = nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (act_request)); + dev_checkpoint->activation_lifetime_bound_to_profile_visiblity = NM_FLAGS_HAS (nm_active_connection_get_state_flags (NM_ACTIVE_CONNECTION (act_request)), + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); } return dev_checkpoint; diff --git a/src/nm-keep-alive.c b/src/nm-keep-alive.c index 6949682571..e601483b96 100644 --- a/src/nm-keep-alive.c +++ b/src/nm-keep-alive.c @@ -34,6 +34,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMKeepAlive, ); typedef struct { + GObject *owner; + NMSettingsConnection *connection; GDBusConnection *dbus_connection; char *dbus_client; @@ -41,10 +43,13 @@ typedef struct { GCancellable *dbus_client_confirm_cancellable; guint subscription_id; - bool floating:1; - bool forced:1; + 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 { @@ -77,22 +82,42 @@ _is_alive (NMKeepAlive *self) { NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); - if ( priv->floating - || priv->forced) + 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 authorative and overrules other conditions (that + * we have so far). */ + return FALSE; + } if ( priv->connection - && NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->connection), - NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) - return TRUE; + && 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; + } - /* Perform this check as last. We want to confirm whether the dbus-client - * is alive lazyly, so if we already decided above that the keep-alive - * is good, we don't rely on the outcome of this check. */ - if (_is_alive_dbus_client (self)) - return TRUE; - - return FALSE; + /* by default, the instance is alive. */ + return TRUE; } static void @@ -100,9 +125,15 @@ _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); } @@ -114,34 +145,28 @@ nm_keep_alive_is_alive (NMKeepAlive *self) /*****************************************************************************/ -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) { + 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); } @@ -163,8 +188,11 @@ _set_settings_connection_watch_visible (NMKeepAlive *self, old_connection = g_steal_pointer (&priv->connection); } - if (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), @@ -298,12 +326,16 @@ nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self, { 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 = g_dbus_connection_signal_subscribe (connection, @@ -316,13 +348,65 @@ nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self, 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); +} + +/*****************************************************************************/ + static void get_property (GObject *object, guint prop_id, @@ -343,22 +427,76 @@ get_property (GObject *object, /*****************************************************************************/ +/** + * 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 refrence 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) { - nm_assert (NM_KEEP_ALIVE_GET_PRIVATE (self)->alive == _is_alive (self)); + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + priv->alive = TRUE; + + nm_assert (priv->alive == _is_alive (self)); } NMKeepAlive * -nm_keep_alive_new (gboolean floating) +nm_keep_alive_new (void) { - 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; + return g_object_new (NM_TYPE_KEEP_ALIVE, NULL); } static void @@ -366,8 +504,10 @@ dispose (GObject *object) { NMKeepAlive *self = NM_KEEP_ALIVE (object); - _set_settings_connection_watch_visible (self, NULL, FALSE); - cleanup_dbus_watch (self); + nm_assert (!NM_KEEP_ALIVE_GET_PRIVATE (self)->owner); + + /* disarm also happens to free all resources. */ + nm_keep_alive_disarm (self); } static void diff --git a/src/nm-keep-alive.h b/src/nm-keep-alive.h index b8c26e173d..160b2adb58 100644 --- a/src/nm-keep-alive.h +++ b/src/nm-keep-alive.h @@ -36,14 +36,12 @@ typedef struct _NMKeepAliveClass NMKeepAliveClass; GType nm_keep_alive_get_type (void) G_GNUC_CONST; -NMKeepAlive* nm_keep_alive_new (gboolean floating); +NMKeepAlive* nm_keep_alive_new (void); 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_arm (NMKeepAlive *self); +void nm_keep_alive_disarm (NMKeepAlive *self); void nm_keep_alive_set_settings_connection_watch_visible (NMKeepAlive *self, NMSettingsConnection *connection); @@ -52,4 +50,10 @@ void nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self, GDBusConnection *connection, const char *client_address); +gpointer /* GObject * */ nm_keep_alive_get_owner (NMKeepAlive *self); + +/* _nm_keep_alive_set_owner() is reserved for the owner to set/unset itself. */ +void _nm_keep_alive_set_owner (NMKeepAlive *self, + GObject *owner); + #endif /* __NETWORKMANAGER_KEEP_ALIVE_H__ */ diff --git a/src/nm-manager.c b/src/nm-manager.c index 9200b24fd1..8bd5a3acc0 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -39,6 +39,7 @@ #include "platform/nm-platform.h" #include "platform/nmp-object.h" #include "nm-hostname-manager.h" +#include "nm-keep-alive.h" #include "nm-rfkill-manager.h" #include "dhcp/nm-dhcp-manager.h" #include "settings/nm-settings.h" @@ -319,6 +320,7 @@ static NMActiveConnection *_new_active_connection (NMManager *self, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, GError **error); static void policy_activating_ac_changed (GObject *object, GParamSpec *pspec, gpointer user_data); @@ -2697,6 +2699,18 @@ recheck_assume_connection (NMManager *self, GError *error = NULL; subject = nm_auth_subject_new_internal (); + + /* Note: the lifetime of the activation connection is always bound to the profiles visiblity + * via NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY. + * + * This only makes a difference, if the profile actually has "connection.permissions" + * set to limit visibility (which is not the case for externally managed, generated profiles). + * + * If we assume a previously active connection whose lifetime was unbound, we now bind it + * after restart. That is not correct, and can mean that the profile becomes subject to + * deactivation after restart (if the user logs out). + * + * This should be improved, but it's unclear how. */ active = _new_active_connection (self, FALSE, sett_conn, @@ -2707,6 +2721,7 @@ recheck_assume_connection (NMManager *self, subject, generated ? NM_ACTIVATION_TYPE_EXTERNAL : NM_ACTIVATION_TYPE_ASSUME, generated ? NM_ACTIVATION_REASON_EXTERNAL : NM_ACTIVATION_REASON_ASSUME, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, &error); if (!active) { @@ -3891,11 +3906,16 @@ ensure_master_active_connection (NMManager *self, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMActiveConnection *ac; NMActiveConnection *master_ac = NULL; NMDeviceState master_state; + gboolean bind_lifetime_to_profile_visibility; - g_assert (connection); - g_assert (master_connection || master_device); + g_return_val_if_fail (connection, NULL); + g_return_val_if_fail (master_connection || master_device, FALSE); + + bind_lifetime_to_profile_visibility = NM_FLAGS_HAS (nm_device_get_activation_state_flags (device), + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); /* If the master device isn't activated then we need to activate it using * compatible connection. If it's already activating we can just proceed. @@ -3920,8 +3940,16 @@ ensure_master_active_connection (NMManager *self, if ( (master_state == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (master_device)) { /* Device already using master_connection */ - g_assert (device_connection); - return NM_ACTIVE_CONNECTION (nm_device_get_act_request (master_device)); + ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (master_device)); + g_return_val_if_fail (device_connection, ac); + + if (!bind_lifetime_to_profile_visibility) { + /* unbind the lifetime. */ + nm_active_connection_set_state_flags_clear (ac, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); + } + + return ac; } /* If the device is disconnected, find a compatible connection and @@ -3958,6 +3986,9 @@ ensure_master_active_connection (NMManager *self, subject, NM_ACTIVATION_TYPE_MANAGED, activation_reason, + bind_lifetime_to_profile_visibility + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, error); return master_ac; } @@ -4006,6 +4037,9 @@ ensure_master_active_connection (NMManager *self, subject, NM_ACTIVATION_TYPE_MANAGED, activation_reason, + bind_lifetime_to_profile_visibility + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, error); return master_ac; } @@ -4167,6 +4201,7 @@ autoconnect_slaves (NMManager *self, master_device)) { gs_free SlaveConnectionInfo *slaves = NULL; guint i, n_slaves = 0; + gboolean bind_lifetime_to_profile_visibility; slaves = find_slaves (self, master_connection, master_device, &n_slaves); if (n_slaves > 1) { @@ -4181,6 +4216,10 @@ autoconnect_slaves (NMManager *self, GINT_TO_POINTER (!nm_streq0 (value, "index"))); } + bind_lifetime_to_profile_visibility = n_slaves > 0 + && NM_FLAGS_HAS (nm_device_get_activation_state_flags (master_device), + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); + for (i = 0; i < n_slaves; i++) { SlaveConnectionInfo *slave = &slaves[i]; const char *uuid; @@ -4235,6 +4274,9 @@ autoconnect_slaves (NMManager *self, subject, NM_ACTIVATION_TYPE_MANAGED, NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES, + bind_lifetime_to_profile_visibility + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, &local_err); if (local_err) { _LOGW (LOGD_CORE, "Slave connection activation failed: %s", local_err->message); @@ -4295,6 +4337,40 @@ unmanaged_to_disconnected (NMDevice *device) } } +static NMActivationStateFlags +_activation_bind_lifetime_to_profile_visibility (NMAuthSubject *subject) +{ + if ( nm_auth_subject_is_internal (subject) + || nm_auth_subject_get_unix_process_uid (subject) == 0) { + /* internal requests and requests from root are always unbound. */ + return NM_ACTIVATION_STATE_FLAG_NONE; + } + + /* if the activation was not done by internal decision nor root, there + * are the following cases: + * + * - the connection has "connection.permissions" unset and the profile + * is not restricted to a user and commonly always visible. It does + * not hurt to bind the lifetime, because we expect the profile to be + * visible at the moment. If the profile changes (while still being active), + * we want to pick-up changes to the visibility and possibly disconnect. + * + * - the connection has "connection.permissions" set, and the current user + * is the owner: + * + * - Usually, we would expect that the profile is visible at the moment, + * and of course we want to bind the lifetime. The moment the user + * logs out, the connection becomes invisible and disconnects. + * + * - the profile at this time could already be invisible (e.g. if the + * user didn't ceate a proper session (sudo) and manually activates + * an invisible profile. In this case, we still want to bind the + * lifetime, and it will disconnect after the user logs in and logs + * out again. NMKeepAlive takes care of that. + */ + return NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY; +} + /* The parent connection is ready; we can proceed realizing the device and * progressing the device to disconencted state. */ @@ -4424,6 +4500,8 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * subject, NM_ACTIVATION_TYPE_MANAGED, nm_active_connection_get_activation_reason (active), + nm_active_connection_get_state_flags (active) + & NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, error); if (!parent_ac) { g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device)); @@ -4549,7 +4627,9 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * for (i = 0; i < n_all; i++) { nm_device_disconnect_active_connection ( all_ac_arr ? all_ac_arr->pdata[i] - : ac); + : ac, + NM_DEVICE_STATE_REASON_NEW_ACTIVATION, + NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN); } } } @@ -4622,6 +4702,7 @@ _new_active_connection (NMManager *self, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); @@ -4693,6 +4774,7 @@ _new_active_connection (NMManager *self, parent_device, nm_dbus_object_get_path (NM_DBUS_OBJECT (parent)), activation_reason, + initial_state_flags, subject); } @@ -4702,6 +4784,7 @@ _new_active_connection (NMManager *self, subject, activation_type, activation_reason, + initial_state_flags, device); } @@ -4765,6 +4848,7 @@ fail: * @activation_type: whether to assume the connection. That is, take over gracefully, * non-destructible. * @activation_reason: the reason for activation + * @initial_state_flags: the inital state flags for the activation. * @error: return location for an error * * Begins a new internally-initiated activation of @sett_conn on @device. @@ -4786,6 +4870,7 @@ nm_manager_activate_connection (NMManager *self, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, GError **error) { NMManagerPrivate *priv; @@ -4839,6 +4924,7 @@ nm_manager_activate_connection (NMManager *self, subject, activation_type, activation_reason, + initial_state_flags, error); if (!active) return NULL; @@ -5098,6 +5184,7 @@ impl_manager_activate_connection (NMDBusObject *obj, subject, NM_ACTIVATION_TYPE_MANAGED, NM_ACTIVATION_REASON_USER_REQUEST, + _activation_bind_lifetime_to_profile_visibility (subject), &error); if (!active) goto error; @@ -5372,12 +5459,18 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj, subject, NM_ACTIVATION_TYPE_MANAGED, NM_ACTIVATION_REASON_USER_REQUEST, + _activation_bind_lifetime_to_profile_visibility (subject), &error); if (!active) goto error; - if (bind_dbus_client) - nm_active_connection_bind_dbus_client (active, dbus_connection, sender); + if (bind_dbus_client) { + NMKeepAlive *keep_alive; + + keep_alive = nm_active_connection_get_keep_alive (active); + nm_keep_alive_set_dbus_client_watch (keep_alive, dbus_connection, sender); + nm_keep_alive_arm (keep_alive); + } nm_active_connection_authorize (active, incompl_conn, @@ -5406,31 +5499,26 @@ nm_manager_deactivate_connection (NMManager *manager, NMDeviceStateReason reason, GError **error) { - gboolean success = FALSE; - if (NM_IS_VPN_CONNECTION (active)) { NMActiveConnectionStateReason vpn_reason = NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED; if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED) vpn_reason = NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED; - if (nm_vpn_connection_deactivate (NM_VPN_CONNECTION (active), vpn_reason, FALSE)) - success = TRUE; - else + if (!nm_vpn_connection_deactivate (NM_VPN_CONNECTION (active), vpn_reason, FALSE)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, "The VPN connection was not active."); + return FALSE; + } } else { - g_assert (NM_IS_ACT_REQUEST (active)); - nm_device_state_changed (nm_active_connection_get_device (active), - NM_DEVICE_STATE_DEACTIVATING, - reason); - success = TRUE; + nm_assert (NM_IS_ACT_REQUEST (active)); + nm_device_disconnect_active_connection (active, + reason, + NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN); } - if (success) - _notify (manager, PROP_ACTIVE_CONNECTIONS); - - return success; + _notify (manager, PROP_ACTIVE_CONNECTIONS); + return TRUE; } static void diff --git a/src/nm-manager.h b/src/nm-manager.h index 06ca633de8..ecb4b0172d 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -97,6 +97,19 @@ const CList * nm_manager_get_active_connections (NMManager *manager); }); \ iter = c_list_entry (iter->active_connections_lst.next, NMActiveConnection, active_connections_lst)) +#define nm_manager_for_each_active_connection_safe(manager, iter, tmp_list, iter_safe) \ + for (tmp_list = nm_manager_get_active_connections (manager), \ + iter_safe = tmp_list->next; \ + ({ \ + if (iter_safe != tmp_list) { \ + iter = c_list_entry (iter_safe, NMActiveConnection, active_connections_lst); \ + iter_safe = iter_safe->next; \ + } else \ + iter = NULL; \ + (iter != NULL); \ + }); \ + ) + NMSettingsConnection **nm_manager_get_activatable_connections (NMManager *manager, gboolean for_auto_activation, gboolean sort, @@ -121,6 +134,19 @@ const CList * nm_manager_get_devices (NMManager *manager); }); \ iter = c_list_entry (iter->devices_lst.next, NMDevice, devices_lst)) +#define nm_manager_for_each_device_safe(manager, iter, tmp_list, iter_safe) \ + for (tmp_list = nm_manager_get_devices (manager), \ + iter_safe = tmp_list->next; \ + ({ \ + if (iter_safe != tmp_list) { \ + iter = c_list_entry (iter_safe, NMDevice, devices_lst); \ + iter_safe = iter_safe->next; \ + } else \ + iter = NULL; \ + (iter != NULL); \ + }); \ + ) + NMDevice * nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex); NMDevice * nm_manager_get_device_by_path (NMManager *manager, @@ -149,6 +175,7 @@ NMActiveConnection *nm_manager_activate_connection (NMManager *manager, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, GError **error); gboolean nm_manager_deactivate_connection (NMManager *manager, diff --git a/src/nm-policy.c b/src/nm-policy.c index 5199afbf1c..b9e26f604e 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -30,6 +30,7 @@ #include "NetworkManagerUtils.h" #include "nm-act-request.h" +#include "nm-keep-alive.h" #include "devices/nm-device.h" #include "nm-setting-ip4-config.h" #include "nm-setting-connection.h" @@ -1283,6 +1284,7 @@ auto_activate_device (NMPolicy *self, subject, NM_ACTIVATION_TYPE_MANAGED, NM_ACTIVATION_REASON_AUTOCONNECT, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, &error); if (!ac) { _LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: %s", @@ -1673,9 +1675,14 @@ activate_secondary_connections (NMPolicy *self, GError *error = NULL; guint32 i; gboolean success = TRUE; + NMActivationStateFlags initial_state_flags; s_con = nm_connection_get_setting_connection (connection); - nm_assert (s_con); + nm_assert (NM_IS_SETTING_CONNECTION (s_con)); + + /* we propagate the activation's state flags. */ + initial_state_flags = nm_device_get_activation_state_flags (device) + & NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY; for (i = 0; i < nm_setting_connection_get_num_secondaries (s_con); i++) { NMSettingsConnection *sett_conn; @@ -1699,7 +1706,6 @@ activate_secondary_connections (NMPolicy *self, } req = nm_device_get_act_request (device); - g_assert (req); _LOGD (LOGD_DEVICE, "activating secondary connection '%s (%s)' for base connection '%s (%s)'", nm_settings_connection_get_id (sett_conn), sec_uuid, @@ -1712,6 +1718,7 @@ activate_secondary_connections (NMPolicy *self, nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)), NM_ACTIVATION_TYPE_MANAGED, nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (req)), + initial_state_flags, &error); if (ac) secondary_ac_list = g_slist_append (secondary_ac_list, g_object_ref (ac)); @@ -2161,6 +2168,8 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *self) nm_active_connection_get_subject (ac), NM_ACTIVATION_TYPE_MANAGED, nm_active_connection_get_activation_reason (ac), + ( nm_active_connection_get_state_flags (ac) + & NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY), &error)) { _LOGW (LOGD_DEVICE, "VPN '%s' reconnect failed: %s", nm_settings_connection_get_id (connection), @@ -2183,26 +2192,36 @@ active_connection_state_changed (NMActiveConnection *active, } static void -active_connection_keep_alive_changed (NMActiveConnection *ac, +active_connection_keep_alive_changed (NMKeepAlive *keep_alive, GParamSpec *pspec, NMPolicy *self) { - NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + NMPolicyPrivate *priv; + NMActiveConnection *ac; GError *error = NULL; - if (nm_active_connection_get_keep_alive (ac)) + nm_assert (NM_IS_POLICY (self)); + nm_assert (NM_IS_KEEP_ALIVE (keep_alive)); + nm_assert (NM_IS_ACTIVE_CONNECTION (nm_keep_alive_get_owner (keep_alive))); + + if (nm_keep_alive_is_alive (keep_alive)) 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); - } + ac = nm_keep_alive_get_owner (keep_alive); + + if (nm_active_connection_get_state (ac) > NM_ACTIVE_CONNECTION_STATE_ACTIVATED) + return; + + priv = NM_POLICY_GET_PRIVATE (self); + + 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); } } @@ -2213,6 +2232,7 @@ active_connection_added (NMManager *manager, { NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); + NMKeepAlive *keep_alive; if (NM_IS_VPN_CONNECTION (active)) { g_signal_connect (active, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, @@ -2223,13 +2243,18 @@ active_connection_added (NMManager *manager, self); } + keep_alive = nm_active_connection_get_keep_alive (active); + + nm_keep_alive_arm (keep_alive); + 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_signal_connect (keep_alive, + "notify::" NM_KEEP_ALIVE_ALIVE, G_CALLBACK (active_connection_keep_alive_changed), self); - active_connection_keep_alive_changed (active, NULL, self); + active_connection_keep_alive_changed (keep_alive, NULL, self); } static void @@ -2249,6 +2274,9 @@ active_connection_removed (NMManager *manager, g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self); + g_signal_handlers_disconnect_by_func (nm_active_connection_get_keep_alive (active), + active_connection_keep_alive_changed, + self); } /*****************************************************************************/ @@ -2386,12 +2414,12 @@ _deactivate_if_active (NMPolicy *self, NMSettingsConnection *connection) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); NMActiveConnection *ac; - const CList *tmp_list; + const CList *tmp_list, *tmp_safe; GError *error = NULL; nm_assert (NM_IS_SETTINGS_CONNECTION (connection)); - nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) { + nm_manager_for_each_active_connection_safe (priv->manager, ac, tmp_list, tmp_safe) { if ( nm_active_connection_get_settings_connection (ac) == connection && (nm_active_connection_get_state (ac) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) { diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 7f606c2554..d4f9a5d54b 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -851,6 +851,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection, NMDevice *parent_device, const char *specific_object, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, NMAuthSubject *subject) { g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); @@ -864,6 +865,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection, NM_ACTIVE_CONNECTION_INT_SUBJECT, subject, NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, activation_reason, NM_ACTIVE_CONNECTION_VPN, TRUE, + NM_ACTIVE_CONNECTION_STATE_FLAGS, (guint) initial_state_flags, NULL); } diff --git a/src/vpn/nm-vpn-connection.h b/src/vpn/nm-vpn-connection.h index e409cc314f..5482fb0d8b 100644 --- a/src/vpn/nm-vpn-connection.h +++ b/src/vpn/nm-vpn-connection.h @@ -53,6 +53,7 @@ NMVpnConnection * nm_vpn_connection_new (NMSettingsConnection *settings_connecti NMDevice *parent_device, const char *specific_object, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, NMAuthSubject *subject); void nm_vpn_connection_activate (NMVpnConnection *self,