A property preferably only emits a notify-changed signal when
the value actually changes and it caches the value (so that
between property-changed signals the value is guaranteed not to change).
NMSettings and NMManager both already cache the hostname, because
NMHostnameManager didn't guarantee this basic concept.
Implement it and rely on it from NMSettings and NMPolicy.
And remove the copy of the property from NMManager.
Move the call for nm_dispatcher_call_hostname() from NMHostnameManager
to NMManager. Note that NMPolicy also has a call to the dispatcher
when set-transient-hostname returns. This should be cleaned up later.
Hostname management is complicated. At least, how it is implemented currently.
For example, NMPolicy also sets the hostname (NMPolicy calls
nm_settings_set_transient_hostname() to have hostnamed set the hostname,
but then falls back to sethostname() in settings_set_hostname_cb()).
Also, NMManager tracks the hostname in NM_MANAGER_HOSTNAME too, and
NMPolicy listens to changes from there -- instead of changes from
NMSettings.
Eventually, NMHostnameManager should contain the hostname parts from NMSettings
and NMPolicy.
Commit #acf1067a allowed to assume connections on already managed
devices. Anyway, in complex scenario with layered connections, during
the startup of NetworkManager, this could interfere with the connection
assumption based on saved state.
So, avoid to re-assume connections on already managed devices during
startup.
Fixes: acf1067a45
(cherry picked from commit b6b7d909f7)
Commit 850c97795 ("device: track system interface state in NMDevice")
introduced interface states for devices and prevented checking if a
connection should be assumed on already managed devices.
This prevented to properly manage the event of an ip configuration added
externally to NM to a managed but not (yet) activated device.
Fixes: 850c977953
(cherry picked from commit acf1067a45)
NMDeviceGeneric:check_connection_compatible() doesn't check for a
matching interface name. It relies on the parent implementation to
do that.
The parent implementation calls nm_manager_get_connection_iface().
That fails for NM_SETTING_GENERIC_SETTING_NAME, because that one has
no factory. Maybe this imbalance of having no factory for the Generic device
is wrong, but usually factories only match a distinct set of device
types, while the generic factory would handle them all (as last resort).
Without this, activating a generic connection might activate the
wrong interface.
(cherry picked from commit 3876b10a47)
Since commit 2d1b85f (th/assume-vs-unmanaged-bgo746440), we clearly
distinguish between two modes when encountering devices with external
IP configuration:
a) external devices. For those devices we generate a volatile in-memory
connection and pretend it's active. However, the device must not be
touched by NetworkManager in any way.
b) assume, seamless take over. Mostly for restart of NetworkManager,
we activate a connection gracefully without going through an down-up
cycle. After the device reaches activated state, the device is
considered fully managed. For this only an existing, non volatile
connection can be used.
Before 'th/assume-vs-unmanaged-bgo746440', the behaviors were not
clearly separated.
Since then, we only choose to assume a connection (b) when the state
file indicates a matching connection. Now, extend this to also assume
connections when:
- during first-start (not after a restart) when there is no
state file yet.
- and, if we have an existing, non volatile, connection which
matches the device's configuration.
This patch lets NetworkManager assume connection also on first start.
That is for example useful when handing over network configuration from
initrd.
This only applies to existing, permanent, matching(!) connections, so it is a
good guess that the user wants NM to take over this interface. This brings us
closer to the previous behavior before 'th/assume-vs-unmanaged-bgo746440'.
https://bugzilla.redhat.com/show_bug.cgi?id=1439220
(cherry picked from commit 27b2477cb7)
nm_config_device_state_*() always access the file system directly,
they don't cache data in NMConfig. Hence, they don't use the
@self argument.
Maybe those functions don't belong to nm-config.h, anyway. For lack
of a better place they are there.
(cherry picked from commit 1940be410c)
Set the device state as removed when the link disappears, so that in
the call to unrealize() when the device is unmanaged we also perform a
cleanup of it and especially, we terminate any DHCP client instances
running on the device.
If we keep DHCP clients running, we can hit assertions later when we
start another instance on the same interface, because we kill the old
dhclient from the pidfile, and the g_child_watch_add() done by the
first client instance is not able to waitpid() it, complaining with:
GChildWatchSource: Exit status of a child process was requested but
ECHILD was received by waitpid(). Most likely the process is
ignoring SIGCHLD, or some other thread is invoking waitpid() with a
nonpositive first argument; either behavior can break applications
that use g_child_watch_add()/g_spawn_sync() either directly or
indirectly.
https://bugzilla.redhat.com/show_bug.cgi?id=1436602
(cherry picked from commit df537d2eac)
When a VPN connection can't be activated we have to unexport and
dispose it. Commit f2182fbf9b ("core: don't emit double
PropertiesChanged signal for new active connections") removed the call
to nm_exported_object_unexport() in case of failure because the active
connection already gets unreferenced on failure.
However, an exported object can't be disposed until it's explicitly
unexported because GDBus code keeps a reference to it. The result was
that the active connection was kept alive and exported, but without
explicit references to it. As soon as the connection was unexported,
it was also automatically disposed, causing issues like:
(src/nm-exported-object.c:1025):dispose: code should not be reached
#0 _g_log_abort () at /lib64/libglib-2.0.so.0
#1 g_logv () at /lib64/libglib-2.0.so.0
#2 g_log () at /lib64/libglib-2.0.so.0
#3 g_warn_message () at /lib64/libglib-2.0.so.0
#4 dispose (object=0xaaf110) at src/nm-exported-object.c:1025
#5 dispose (object=0xaaf110) at src/nm-active-connection.c:1246
#6 dispose (object=0xaaf110) at src/vpn/nm-vpn-connection.c:2642
#7 g_object_unref () at /lib64/libgobject-2.0.so.0
#8 registration_data_free () at /lib64/libgio-2.0.so.0
#9 g_hash_table_remove_internal () at /lib64/libglib-2.0.so.0
#10 g_dbus_object_manager_server_unexport_unlocked () at /lib64/libgio-2.0.so.0
#11 g_dbus_object_manager_server_unexport () at /lib64/libgio-2.0.so.0
#12 nm_bus_manager_unregister_object (self=0x9069e0, object=object@entry=0xaaf110) at src/nm-bus-manager.c:858
#13 nm_exported_object_unexport (self=0xaaf110) at src/nm-exported-object.c:714
#14 _settings_connection_removed (connection=<optimized out>, user_data=0xaaf110) at src/nm-active-connection.c:184
#15 g_closure_invoke () at /lib64/libgobject-2.0.so.0
#16 signal_emit_unlocked_R () at /lib64/libgobject-2.0.so.0
#17 g_signal_emit_valist () at /lib64/libgobject-2.0.so.0
#18 g_signal_emit_by_name () at /lib64/libgobject-2.0.so.0
#19 nm_settings_connection_signal_remove (self=self@entry=0x9e4a80, allow_reuse=allow_reuse@entry=0) at src/settings/nm-settings-connection.c:2085
#20 do_delete (self=0x9e4a80, callback=0x58106a <con_delete_cb>, user_data=0xa84fa0) at src/settings/nm-settings-connection.c:768
#21 do_delete (connection=0x9e4a80, callback=0x58106a <con_delete_cb>, user_data=0xa84fa0) at src/settings/plugins/keyfile/nms-keyfile-connection.c:127
#22 nm_settings_connection_delete (self=self@entry=0x9e4a80, callback=callback@entry=0x58106a <con_delete_cb>, user_data=0xa84fa0) at src/settings/nm-settings-connection.c:694
#23 delete_auth_cb (self=self@entry=0x9e4a80, context=context@entry=0x7fffd80131e0, subject=0x91fb40, error=<optimized out>, data=data@entry=0x0) at src/settings/nm-settings-connection.c:1879
#24 pk_auth_cb (chain=0x7fffd00024a0, chain_error=<optimized out>, context=0x7fffd80131e0, user_data=<optimized out>) at src/settings/nm-settings-connection.c:1351
#25 auth_chain_finish (user_data=0x7fffd00024a0) at src/nm-auth-utils.c:92
#26 g_idle_dispatch () at /lib64/libglib-2.0.so.0
Restore the unexport upon failure to fix this.
Fixes: f2182fbf9bhttps://bugzilla.redhat.com/show_bug.cgi?id=1440077
(cherry picked from commit 69fd96118e)
For example, when starting without Wi-Fi plugin, a generic device
is created. On stop, we should not store the unmanaged state
on the state file, otherwise after restart the device is unmanaged.
Only store explicit user decisions.
https://bugzilla.redhat.com/show_bug.cgi?id=1440171
(cherry picked from commit 142ebb1037)
This makes it possible to retain Internet connectivity when multiple devices
have a default route, but one with the link type of a higher priority can not
reach the Internet.
This moves tracking of connectivity to NMDevice and makes the NMManager
negotiate the best of known connectivity states of devices. The NMConnectivity
singleton handles its own configuration and scheduling of the permission
checks, but otherwise greatly simplifies it.
This will be useful to determine correct metrics for multiple default routes
depending on actual internet connectivity.
The per-device connection checks is not yet exposed on the D-Bus, since they
probably should be per-address-family as well.
Perform the lookup for a matching device earlier, so that in
autoconnect_slaves() we already know which device a connection is
being activated on. This will be needed to sort the returned
connections by interface name.
When slave connections are autoactivated as dependency to master we
don't check if a compatible device is available before trying to
activate them, leading to the following failed assertion:
nm_act_request_new: assertion 'NM_IS_DEVICE (device)' failed
GLib 2.52 added a G_GNUC_PRINTF attribute to
g_dbus_message_new_method_error(). This triggered warning in
NetworkManager when built with -Wformat, which is an error when built
with -Werror=format-security. It seems that gcc isn't smart enough to
see that (foo = "bar") should be treated as a literal.
Fortunately there is a g_dbus_message_new_method_error_literal()
function which does not take printf-style arguments, and we don't need
them, so we can use that.
This patch was originally by Rico Tzschichholz <ricotz@ubuntu.com>, and
was submitted to Launchpad at
https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1650972https://bugzilla.gnome.org/show_bug.cgi?id=780444
When remove_device() is called on an already unrealized device, we
should release it from master if necessary and clear its IP
configurations to avoid leaks.
https://bugzilla.redhat.com/show_bug.cgi?id=1433303
It includes a reason code that makes it possible for the clients to be
more reasonable about error messages.
The reason code is essentially copied from the VPN, plus three more
reasons that were useful for non-VPN connections.
When deciding whether to touch a device we sometimes look at whether
the active connection is external/assumed. In many cases however,
there is no active connection around (e.g. while moving the device
from state unmanaged to disconnected before assuming).
So in most cases we instead look at the device-state-reason to decide
whether to touch the interface (see nm_device_state_reason_check()).
Often it's desirable to have no state and passing data as function
arguments. However, the state reason has to be passed along several hops
(e.g. a queued state change). Or a change to a master/slave can affect
the slave/master, where we pass on the state reason. Or an intermediate
event might invalidate a previous state reason. Passing the state
whether to touch a device or not as a state-reason is cumbersome
and limited.
Instead, the device should be aware of whats going on. Add a
sys-iface-state with:
- SYS_IFACE_STATE_EXTERNAL: meaning, NM should not touch it
- SYS_IFACE_STATE_ASSUME: meaning, NM is gracefully taking over
- SYS_IFACE_STATE_MANAGED: meaning, the device is managed by NM
- SYS_IFACE_STATE_REMOVED: the device no longer exists
This replaces most checks of nm_device_state_reason_check() and
nm_active_connection_get_activation_type() by instead looking at
the sys-iface-state of the device.
This patch probably has still issues, but the previous behavior was
not very clear either. We will need to identify those issues in future
tests and tweak the behavior. At least, now there is one flag that
describes how to behave.
Now we only search for a candiate with matching UUID. No need to
first lookup all activatable connections, just find the candidate
by UUID and see if it is activatable.
- rename find_ac_for_connection() to
active_connection_find_first_by_connection().
This function has the unexpected(?) behavior to either
search by the @connection using pointer identity, or
by looking up the UUID (depending on whether @connection
is a NMSettingsConnection).
- Split out a active_connection_find_first() part.
The name "find-first" makes it clear that we walk the list until
a matching active-connection is found. That's why I added the
max_state argument. Otherwise, it would have to be called
"find-first-non-disconnected".
- drop nm_manager_get_connection_device(). It only had two callers.
It also used the ambiguity of the @connection argument, but
only one of the two callers cared about that. Meaning,
_internal_activate_device() now explicitly does a lookup by
the NMSettingsConnection.
There is only one caller of assume_connection().
The tasks there are not clearly separate and it is clearer just to
have one large recheck_assume_connection() function which proceeds
step by step, instead of breaking it into separate parts and move
them apart in the source code. The latter implies that -- unless
we forward-declare assume_connection() -- the order of definition
with assume_connection,recheck_assume_connection is contrary the
flow of the code.
Having a separate function in this case is not a simplification
so merge it.
Before, we would have the concept of assumed connections, which is used
for (1) externally configured device that NetworkManager should not
touch and (2) connections that NetworkManager should gracefully take
over after a restart (seamlessly, non-destructively).
The behavior was unclear and mixed. It wasn't clear whether the device
is in no-touch mode (1) or gracefully take-over (2).
Previous commits already introduce separate activation types EXTERNAL (1)
and ASSUME (2).
Also, previously, we would for both (1) and (2) try to find a matching
connection and use it. That doesn't make sense for either.
In the external case (1), we should not pretend that an existing connection
is active. Let's always create a new in-memory connection for these
cases. Note that this means, external devices now will always generate
a connection, instead of pretending an existing one is active.
For the assume case (2), we shall not use nm_utils_match_connection() to
guess which connection might be active. It can only the one that was
active on a previous run of NetworkManager. So, use the information from
the state file and try to activate it. If that fails, it is not an
assume activation type. Note, that this means we now most of the time
don't do ASSUME anymore. Most of the time we do EXTERNAL activation
That is because the state information is only available after restart
of NetworkManager.
We need a distinction between external activations and assuming
connections. The former shall have the meaning of devices that are
*not* managed by NetworkManager, the latter are configurations that
are gracefully taken over after restart (but fully managed).
Express that in the activation-type of the active connection.
Also, no longer use the settings NM_SETTINGS_CONNECTION_FLAGS_VOLATILE
flag to determine whether an assumed connection is "external". These
concepts are entirely orthogonal (although in pratice, external
activations are in-memory and flagged as volatile, but the inverse
is not necessarily true).
Also change match_connection_filter() to consider all connections.
Later, we only call nm_utils_match_connection() for the connection
we want to assume -- which will be a regular settings connection,
not a generated one.
nm_device_uses_assumed_connection() basically called
nm_active_connection_get_assumed() on the device.
Rename those functions to be closer to the activation-type
flags.
The concepts of "assume", "external", and "assume_or_external"
will make sense with the following commits.
The concept of assumed-connection will change. Currently we mark
connections that are generated and assumed as "nm-generated-assumed".
That has several consequences, one of them being that such a settings
connection gets deleted when the device disconnects.
That is, such a settings connection lingers around as long as it's active,
but once it deactivates it gets automatically deleted. As such, it's
a more volatile concept of an in-memory connection.
The concept of such automatically cleaned up connections is useful beyond
generated-assumed. See the related bug rh#1401515.
The devices_inited_cb() callback is really supposed to only run
when there is nothing else left in the mainloop to dispatch.
But as we already schedule the idle action with G_PRIORITY_LOW+10
priority, it is very unlikely that there is anything else ready
to run (unless scheduled with an even lower priority, and then it
wouldn't help either because devices_inited_cb() would win again).
This doesn't really matter, because NMSetting's startup-complete never
switches back to FALSE once reached. Still, cleanup our signal handlers
when no longer needed.
And disconnect the signals before emitting "notify::startup".
Possibly pending messages from the netlink socket were not processed
since the platform instance was created earlier. As nm_manager_start()
may take a long time to run, make sure that there are no pending
messages before querying the devices.
The state-change of a device has a reason argument, which is mostly for information
only.
There are many places in code that are the source of a state-reason.
Mostly these are calls to:
- nm_device_state_changed()
- nm_device_queue_state()
- nm_device_queue_recheck_available()
- nm_device_set_unmanaged_by_*()
- nm_device_master_release_one_slave()
- nm_device_ip_method_failed()
- nm_modem_emit_prepare_result()
- nm_modem_emit_ppp_failed()
- nm_manager_deactivate_connection()
- NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_*);
However, there are a few places in code that look at the reason
to decide how to proceed. I think this is a bad pattern, because
cause and effect are decoupled and it gets hard to understand where
a certain reason is set and what consequences that has.
Add a nop-function nm_device_state_reason_check() to mark all uses
of the device state reason that derive decisions from it. That is,
highlight the "effect" part.
The NMDevice's autoconnect property is settable via D-Bus and is is
also modified by internal decision, like when no PIN is available.
Certain internal actions cause clearing the internal autoconnect flag,
but they should not override the user desicion.
For example, when NM awaks from sleep it would reenable autoconnect,
but it should not reenable it for devices where the user explicitly
said that autoconnect is to be disabled.
Similarly, activating a device alone is not yet an instruction to
re-enable autoconnect. If the user consciously disables autoconnect,
it should stay enabled. On the other hand, activating a device should
reenable autoconnect if it was blocked by internal decision.
We need to track these two flags separately, and set them accordingly.
Update the connectivity state if we go from CONNECTED_GLOBAL to
CONNECTED_LOCAL. It will likely fail immediately (unless there's a default
route we're not aware of or the check URL is routable locally), keeping the
Connectivity property up-to-date.
We want to have some guaranteed order when comparing different connections.
So, in case of equal timestamps, proceed with comparing more properties.
It makes sense to consider the autoconnect-priority next.
This is what get_existing_connection() needs, thus we no longer
need to pre-sort the list.
NMPolicy's auto_activate_device() wants to sort by autoconnect-priority,
nm_utils_cmp_connection_by_autoconnect_priority() but fallback to the default
nm_settings_connection_cmp_default(), which includes the timestamp.
Extend nm_settings_connection_cmp_default() to consider the
autoconnect-priority as well. Thus change behavior so that
nm_settings_connection_cmp_default() is the sort order that
auto_activate_device() wants. That makes sense, as
nm_settings_connection_cmp_default() already considered the
ability to autoconnect as first. Hence, it should also honor
the autoconnect priority.
When doing that, rename nm_settings_connection_cmp_default()
to nm_settings_connection_cmp_autoconnect_priority().
We call these functions a lot. A GSList is just the wrong tool for the
job. Refactor the code to use instead a sorted array everywhere.
This means, we malloc() one array for all connections instead
slice-allocate a GSList item for each. Also, sorting an array
is faster then sorting a GSList.
Technically, the GSList implementation had the same big-O runtime
complexity, but using an array is still faster. That is, sorting
an array and a GSList is both O(n*log(n)).
Actually, nm_settings_get_connections_sorted() used
g_slist_insert_sorted() instead of g_slist_sort(). That results
in O(n^2). That could have been fixed to have O(n*log(n)), but
instead refactor the code to use an array.
nm_settings_get_best_connections() has only one caller: to create
the hidden-SSID list.
Instead of having a highly specialised function (that accepts 3 ways for
filtering -- one of them broken, has one hard-coded way of sorting, and
a @max_requested argument), add a more generic nm_settings_get_connections_clone()
function.
Also invert nm_settings_sort_connections(). The two callers want
to sort descending, not ascending.
I think NM_CACHED_QUARK_FCN() is better because:
- the implementation is in our hand, meaning it is clear that
putting a "static" before NM_CACHED_QUARK_FCN() is guaranteed to
work -- without relying on G_DEFINE_QUARK() to be defined in a way
that this works (in fact, we currently never do that and instead
make all functions non-static).
- it does not construct function names by appending "_quark".
Thus you can grep for the entire function name and finding
the place where it is implemented.
- same with the stings, where the new macro doesn't stringify the
argument, which is less surpising. Again, now you can grep
for the string including the double quoting.
(yes, I really use grep to understand the source-code)