NetworkManager/src/nm-connectivity.c

1017 lines
30 KiB
C
Raw Normal View History

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
* Copyright (C) 2011 Dan Williams <dcbw@redhat.com>
* Copyright (C) 2016 - 2018 Red Hat, Inc.
*/
#include "nm-default.h"
#include "nm-connectivity.h"
#include "nm-dbus-manager.h"
#include <string.h>
#if WITH_CONCHECK
#include <curl/curl.h>
#endif
#include "c-list/src/c-list.h"
#include "nm-config.h"
#include "NetworkManagerUtils.h"
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n"
/*****************************************************************************/
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_state_to_string, int /*NMConnectivityState*/,
NM_UTILS_LOOKUP_DEFAULT_WARN ("???"),
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_UNKNOWN, "UNKNOWN"),
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_NONE, "NONE"),
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_LIMITED, "LIMITED"),
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_PORTAL, "PORTAL"),
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL, "FULL"),
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_ERROR, "ERROR"),
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FAKE, "FAKE"),
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_CANCELLED, "CANCELLED"),
NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_DISPOSING, "DISPOSING"),
);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
const char *
nm_connectivity_state_to_string (NMConnectivityState state)
{
return _state_to_string (state);
}
/*****************************************************************************/
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
struct _NMConnectivityCheckHandle {
CList handles_lst;
NMConnectivity *self;
NMConnectivityCheckCallback callback;
gpointer user_data;
char *ifspec;
const char *completed_log_message;
char *completed_log_message_free;
#if WITH_CONCHECK
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
struct {
char *response;
int ifindex;
GCancellable *resolve_cancellable;
CURLM *curl_mhandle;
guint curl_timer;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
CURL *curl_ehandle;
struct curl_slist *request_headers;
struct curl_slist *hosts;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
GString *recv_msg;
} concheck;
#endif
guint64 request_counter;
int addr_family;
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
2018-01-05 15:52:39 +01:00
guint timeout_id;
NMConnectivityState completed_state;
bool fail_reason_no_dbus_connection:1;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
};
2018-01-05 15:52:39 +01:00
enum {
connectivity: schedule connectivity timers per-device and probe for short outages It might happen, that connectivitiy is lost only for a moment and returns soon after. Based on that assumption, when we loose connectivity we want to have a probe interval where we check for returning connectivity more frequently. For that, we handle tracking of the timeouts per-device. The intervall shall start with 1 seconds, and double the interval time until the full interval is reached. Actually, due to the implementation, it's unlikely that we already perform the second check 1 second later. That is because commonly the first check returns before the one second timeout is reached and bumps the interval to 2 seconds right away. Also, we go through extra lengths so that manual connectivity check delay the periodic checks. By being more smart about that, we can reduce the number of connectivity checks, but still keeping the promise to check at least within the requested interval. The complexity of book keeping the timeouts is remarkable. But I think it is worth the effort and we should try hard to - have a connectivity state as accurate as possible. Clearly, connectivity checking means that we probing, so being more intelligent about timeout and backoff timers can result in a better connectivity state. The connectivity state is important because we use it for the default-route penaly and the GUI indicates bad connectivity. - be intelligent about avoiding redundant connectivity checks. While we want to check often to get an accurate connectivity state, we also want to minimize the number of HTTP requests, in case the connectivity is established and suppossedly stable. Also, perform connectivity checks in every state of the device. Even if a device is disconnected, it still might have connectivity, for example if the user externally adds an IP address on an unmanaged device. https://bugzilla.gnome.org/show_bug.cgi?id=792240
2018-02-20 21:41:14 +01:00
CONFIG_CHANGED,
2018-01-05 15:52:39 +01:00
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
typedef struct {
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
CList handles_lst_head;
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
CList completed_handles_lst_head;
char *uri;
char *host;
char *port;
char *response;
gboolean enabled;
guint interval;
NMConfig *config;
} NMConnectivityPrivate;
struct _NMConnectivity {
GObject parent;
NMConnectivityPrivate _priv;
};
struct _NMConnectivityClass {
GObjectClass parent;
};
G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT)
#define NM_CONNECTIVITY_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMConnectivity, NM_IS_CONNECTIVITY)
NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECTIVITY);
/*****************************************************************************/
2016-10-14 15:32:56 +02:00
#define _NMLOG_DOMAIN LOGD_CONCHECK
#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "connectivity", __VA_ARGS__)
#define _NMLOG2_DOMAIN LOGD_CONCHECK
#define _NMLOG2(level, ...) \
G_STMT_START { \
const NMLogLevel __level = (level); \
\
if (nm_logging_enabled (__level, _NMLOG2_DOMAIN)) { \
_nm_log (__level, _NMLOG2_DOMAIN, 0, \
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
(cb_data->ifspec ? &cb_data->ifspec[3] : NULL), \
NULL, \
"connectivity: (%s,IPv%c,%"G_GUINT64_FORMAT") " \
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
_NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
(cb_data->ifspec ? &cb_data->ifspec[3] : ""), \
nm_utils_addr_family_to_char (cb_data->addr_family), \
cb_data->request_counter \
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
_NM_UTILS_MACRO_REST (__VA_ARGS__)); \
} \
} G_STMT_END
/*****************************************************************************/
static void
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_complete (NMConnectivityCheckHandle *cb_data,
NMConnectivityState state,
const char *log_message)
{
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
NMConnectivity *self;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
nm_assert (cb_data);
nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
nm_assert (cb_data->callback);
nm_assert (state != NM_CONNECTIVITY_UNKNOWN);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
nm_assert (log_message);
self = cb_data->self;
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
/* mark the handle as completing. After this point, nm_connectivity_check_cancel()
* is no longer possible. */
cb_data->self = NULL;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
c_list_unlink_stale (&cb_data->handles_lst);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#if WITH_CONCHECK
if (cb_data->concheck.curl_ehandle) {
/* Contrary to what cURL manual claim it is *not* safe to remove
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
* the easy handle "at any moment"; specifically it's not safe to
* remove *any* handle from within a libcurl callback. That is
* why we queue completed handles in this case.
*
* cb_data_complete() is however only called *not* from within a
* libcurl callback. So, this is fine. */
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HEADERFUNCTION, NULL);
curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HEADERDATA, NULL);
curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_PRIVATE, NULL);
curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HTTPHEADER, NULL);
curl_multi_remove_handle (cb_data->concheck.curl_mhandle,
cb_data->concheck.curl_ehandle);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
curl_easy_cleanup (cb_data->concheck.curl_ehandle);
curl_multi_cleanup (cb_data->concheck.curl_mhandle);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
curl_slist_free_all (cb_data->concheck.request_headers);
curl_slist_free_all (cb_data->concheck.hosts);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
}
nm_clear_g_source (&cb_data->concheck.curl_timer);
nm_clear_g_cancellable (&cb_data->concheck.resolve_cancellable);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#endif
nm_clear_g_source (&cb_data->timeout_id);
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
_LOG2D ("check completed: %s; %s",
nm_connectivity_state_to_string (state),
log_message);
cb_data->callback (self,
cb_data,
state,
cb_data->user_data);
/* Note: self might be a danling pointer at this point. It must not be used
* after this point, and all callers must either take a reference first, or
* not use the self pointer too. */
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#if WITH_CONCHECK
g_free (cb_data->concheck.response);
if (cb_data->concheck.recv_msg)
g_string_free (cb_data->concheck.recv_msg, TRUE);
#endif
g_free (cb_data->ifspec);
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
if (cb_data->completed_log_message_free)
g_free (cb_data->completed_log_message_free);
g_slice_free (NMConnectivityCheckHandle, cb_data);
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
/*****************************************************************************/
#if WITH_CONCHECK
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
static void
cb_data_queue_completed (NMConnectivityCheckHandle *cb_data,
NMConnectivityState state,
const char *log_message_static,
char *log_message_take /* take */)
{
nm_assert (cb_data);
nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
nm_assert (state != NM_CONNECTIVITY_UNKNOWN);
nm_assert (log_message_static || log_message_take);
nm_assert (cb_data->completed_state == NM_CONNECTIVITY_UNKNOWN);
nm_assert (!cb_data->completed_log_message);
nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst));
cb_data->completed_state = state;
cb_data->completed_log_message = log_message_static ?: log_message_take;
cb_data->completed_log_message_free = log_message_take;
c_list_unlink_stale (&cb_data->handles_lst);
c_list_link_tail (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->completed_handles_lst_head, &cb_data->handles_lst);
}
static void
_complete_queued (NMConnectivity *self)
{
NMConnectivity *self_keep_alive = NULL;
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
NMConnectivityCheckHandle *cb_data;
while ((cb_data = c_list_first_entry (&priv->completed_handles_lst_head, NMConnectivityCheckHandle, handles_lst))) {
if (!self_keep_alive)
self_keep_alive = g_object_ref (self);
cb_data_complete (cb_data,
cb_data->completed_state,
cb_data->completed_log_message);
}
nm_g_object_unref (self_keep_alive);
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
static const char *
_check_handle_get_response (NMConnectivityCheckHandle *cb_data)
{
return cb_data->concheck.response ?: NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE;
}
static gboolean
_con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask)
{
NMConnectivityCheckHandle *cb_data;
CURLMsg *msg;
CURLcode eret;
int m_left;
long response_code;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
CURLMcode ret;
int running_handles;
gboolean success = TRUE;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
ret = curl_multi_socket_action (mhandle, sockfd, ev_bitmask, &running_handles);
if (ret != CURLM_OK) {
_LOGD ("connectivity check failed: (%d) %s", ret, curl_easy_strerror (ret));
success = FALSE;
}
while ((msg = curl_multi_info_read (mhandle, &m_left))) {
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
if (msg->msg != CURLMSG_DONE)
continue;
/* Here we have completed a session. Check easy session result. */
eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, (char **) &cb_data);
if (eret != CURLE_OK) {
_LOGD ("curl cannot extract cb_data for easy handle, skipping msg: (%d) %s",
eret, curl_easy_strerror (eret));
success = FALSE;
continue;
}
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
nm_assert (cb_data);
nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
/* callback was already invoked earlier. Nothing to do. */
continue;
}
if (msg->data.result != CURLE_OK) {
cb_data_queue_completed (cb_data,
NM_CONNECTIVITY_LIMITED,
NULL,
g_strdup_printf ("check failed: (%d) %s",
msg->data.result,
curl_easy_strerror (msg->data.result)));
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
} else if ( !((_check_handle_get_response (cb_data))[0])
&& (curl_easy_getinfo (msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK)
&& response_code == 204) {
/* If we got a 204 response code (no content) and we actually
* requested no content, report full connectivity. */
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_queue_completed (cb_data,
NM_CONNECTIVITY_FULL,
"no content, as expected",
NULL);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
} else {
/* If we get here, it means that easy_write_cb() didn't read enough
* bytes to be able to do a match, or that we were asking for no content
* (204 response code) and we actually got some. Either way, that is
* an indication of a captive portal */
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_queue_completed (cb_data,
NM_CONNECTIVITY_PORTAL,
"unexpected short response",
NULL);
}
}
/* if we return a failure, we don't know what went wrong. It's likely serious, because
* a failure here is not expected. Return FALSE, so that we stop polling the file descriptor.
* Worst case, this leaves the pending connectivity check unhandled, until our regular
* time-out kicks in. */
return success;
}
static gboolean
_con_curl_timeout_cb (gpointer user_data)
{
NMConnectivityCheckHandle *cb_data = user_data;
cb_data->concheck.curl_timer = 0;
_con_curl_check_connectivity (cb_data->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0);
_complete_queued (cb_data->self);
2017-03-22 18:48:58 +00:00
return G_SOURCE_REMOVE;
}
static int
multi_timer_cb (CURLM *multi, long timeout_ms, void *userdata)
{
NMConnectivityCheckHandle *cb_data = userdata;
nm_clear_g_source (&cb_data->concheck.curl_timer);
if (timeout_ms != -1)
cb_data->concheck.curl_timer = g_timeout_add (timeout_ms, _con_curl_timeout_cb, cb_data);
return 0;
}
typedef struct {
NMConnectivityCheckHandle *cb_data;
GIOChannel *ch;
/* this is a very simplistic weak-pointer. If ConCurlSockData gets
* destroyed, it will set *destroy_notify to TRUE.
*
* _con_curl_socketevent_cb() uses this to detect whether it can
* safely access @fdp after _con_curl_check_connectivity(). */
gboolean *destroy_notify;
guint ev;
} ConCurlSockData;
static gboolean
_con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_data)
{
ConCurlSockData *fdp = user_data;
NMConnectivityCheckHandle *cb_data = fdp->cb_data;
int fd = g_io_channel_unix_get_fd (ch);
int action = 0;
gboolean fdp_destroyed = FALSE;
gboolean success;
if (condition & G_IO_IN)
action |= CURL_CSELECT_IN;
if (condition & G_IO_OUT)
action |= CURL_CSELECT_OUT;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
if (condition & G_IO_ERR)
action |= CURL_CSELECT_ERR;
nm_assert (!fdp->destroy_notify);
fdp->destroy_notify = &fdp_destroyed;
success = _con_curl_check_connectivity (cb_data->concheck.curl_mhandle, fd, action);
if (fdp_destroyed) {
/* hups. fdp got invalidated during _con_curl_check_connectivity(). That's fine,
* just don't touch it. */
} else {
nm_assert (fdp->destroy_notify == &fdp_destroyed);
fdp->destroy_notify = NULL;
if (!success)
fdp->ev = 0;
}
_complete_queued (cb_data->self);
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
return success ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
}
static int
multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, void *socketp)
{
NMConnectivityCheckHandle *cb_data = userdata;
ConCurlSockData *fdp = socketp;
GIOCondition condition = 0;
(void) _NM_ENSURE_TYPE (int, fd);
if (what == CURL_POLL_REMOVE) {
if (fdp) {
if (fdp->destroy_notify)
*fdp->destroy_notify = TRUE;
curl_multi_assign (cb_data->concheck.curl_mhandle, fd, NULL);
nm_clear_g_source (&fdp->ev);
g_io_channel_unref (fdp->ch);
g_slice_free (ConCurlSockData, fdp);
}
} else {
if (!fdp) {
fdp = g_slice_new0 (ConCurlSockData);
fdp->cb_data = cb_data;
fdp->ch = g_io_channel_unix_new (fd);
curl_multi_assign (cb_data->concheck.curl_mhandle, fd, fdp);
} else
nm_clear_g_source (&fdp->ev);
if (what == CURL_POLL_IN)
condition = G_IO_IN;
else if (what == CURL_POLL_OUT)
condition = G_IO_OUT;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
else if (what == CURL_POLL_INOUT)
condition = G_IO_IN | G_IO_OUT;
if (condition)
fdp->ev = g_io_add_watch (fdp->ch, condition, _con_curl_socketevent_cb, fdp);
}
2017-03-22 18:48:58 +00:00
return CURLM_OK;
}
static size_t
easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata)
{
NMConnectivityCheckHandle *cb_data = userdata;
size_t len = size * nitems;
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
/* already completed. */
return 0;
}
if ( len >= sizeof (HEADER_STATUS_ONLINE) - 1
&& !g_ascii_strncasecmp (buffer, HEADER_STATUS_ONLINE, sizeof (HEADER_STATUS_ONLINE) - 1)) {
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_queue_completed (cb_data,
NM_CONNECTIVITY_FULL,
"status header found",
NULL);
return 0;
}
return len;
}
static size_t
easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata)
{
NMConnectivityCheckHandle *cb_data = userdata;
size_t len = size * nmemb;
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
const char *response;
if (cb_data->completed_state != NM_CONNECTIVITY_UNKNOWN) {
/* already completed. */
return 0;
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
if (!cb_data->concheck.recv_msg)
cb_data->concheck.recv_msg = g_string_sized_new (len + 10);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
g_string_append_len (cb_data->concheck.recv_msg, buffer, len);
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
response = _check_handle_get_response (cb_data);;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
if ( response
&& cb_data->concheck.recv_msg->len >= strlen (response)) {
/* We already have enough data -- check response */
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
if (g_str_has_prefix (cb_data->concheck.recv_msg->str, response)) {
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_queue_completed (cb_data,
NM_CONNECTIVITY_FULL,
"expected response",
NULL);
} else {
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_queue_completed (cb_data,
NM_CONNECTIVITY_PORTAL,
"unexpected response",
NULL);
}
return 0;
}
return len;
}
static gboolean
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
_timeout_cb (gpointer user_data)
{
NMConnectivityCheckHandle *cb_data = user_data;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst));
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_complete (cb_data, NM_CONNECTIVITY_LIMITED, "timeout");
return G_SOURCE_REMOVE;
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#endif
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
static gboolean
_idle_cb (gpointer user_data)
{
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
NMConnectivityCheckHandle *cb_data = user_data;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
nm_assert (NM_IS_CONNECTIVITY (cb_data->self));
nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst));
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
cb_data->timeout_id = 0;
if (!cb_data->ifspec) {
gs_free_error GError *error = NULL;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
/* the invocation was with an invalid ifname. It is a fail. */
g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
"no interface specified for connectivity check");
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "missing interface");
} else if (cb_data->fail_reason_no_dbus_connection) {
gs_free_error GError *error = NULL;
g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
"no D-Bus connection");
cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "no D-Bus connection");
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
} else
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_complete (cb_data, NM_CONNECTIVITY_FAKE, "fake result");
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
return G_SOURCE_REMOVE;
}
static void
do_curl_request (NMConnectivityCheckHandle *cb_data)
{
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (cb_data->self);
CURLM *mhandle;
CURL *ehandle;
long resolve;
mhandle = curl_multi_init ();
if (!mhandle) {
cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "curl error");
return;
}
ehandle = curl_easy_init ();
if (!ehandle) {
curl_multi_cleanup (mhandle);
cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "curl error");
return;
}
cb_data->concheck.response = g_strdup (priv->response);
cb_data->concheck.curl_mhandle = mhandle;
cb_data->concheck.curl_ehandle = ehandle;
cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close");
cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data);
curl_multi_setopt (mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
curl_multi_setopt (mhandle, CURLMOPT_SOCKETDATA, cb_data);
curl_multi_setopt (mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
curl_multi_setopt (mhandle, CURLMOPT_TIMERDATA, cb_data);
curl_multi_setopt (mhandle, CURLOPT_VERBOSE, 1);
switch (cb_data->addr_family) {
case AF_INET:
resolve = CURL_IPRESOLVE_V4;
break;
case AF_INET6:
resolve = CURL_IPRESOLVE_V6;
break;
case AF_UNSPEC:
resolve = CURL_IPRESOLVE_WHATEVER;
break;
default:
resolve = CURL_IPRESOLVE_WHATEVER;
g_warn_if_reached ();
}
curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri);
curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb);
curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data);
curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb);
curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data);
curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data);
curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers);
curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec);
curl_easy_setopt (ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts);
curl_easy_setopt (ehandle, CURLOPT_IPRESOLVE, resolve);
curl_multi_add_handle (mhandle, ehandle);
}
static void
resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data)
{
NMConnectivityCheckHandle *cb_data;
NMConnectivity *self;
NMConnectivityPrivate *priv;
GVariant *result;
GVariant *addresses;
gsize no_addresses;
int ifindex;
int addr_family;
GVariant *address = NULL;
const guchar *address_buf;
gsize len = 0;
char str[INET6_ADDRSTRLEN + 1] = { 0, };
char *host;
gsize i;
gs_free_error GError *error = NULL;
result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), res, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
cb_data = user_data;
self = cb_data->self;
priv = NM_CONNECTIVITY_GET_PRIVATE (self);
g_clear_object (&cb_data->concheck.resolve_cancellable);
if (!result) {
/* Never mind. Just let do curl do its own resolving. */
_LOG2D ("can't resolve a name via systemd-resolved: %s", error->message);
do_curl_request (cb_data);
return;
}
addresses = g_variant_get_child_value (result, 0);
no_addresses = g_variant_n_children (addresses);
g_variant_unref (result);
for (i = 0; i < no_addresses; i++) {
g_variant_get_child (addresses, i, "(ii@ay)", &ifindex, &addr_family, &address);
address_buf = g_variant_get_fixed_array (address, &len, 1);
if ( (addr_family == AF_INET && len == sizeof (struct in_addr))
|| (addr_family == AF_INET6 && len == sizeof (struct in6_addr))) {
inet_ntop (addr_family, address_buf, str, sizeof (str));
host = g_strdup_printf ("%s:%s:%s", priv->host, priv->port ?: "80", str);
cb_data->concheck.hosts = curl_slist_append (cb_data->concheck.hosts, host);
_LOG2T ("adding '%s' to curl resolve list", host);
g_free (host);
}
g_variant_unref (address);
}
g_variant_unref (addresses);
do_curl_request (cb_data);
}
#define SD_RESOLVED_DNS ((guint64) (1LL << 0))
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
NMConnectivityCheckHandle *
nm_connectivity_check_start (NMConnectivity *self,
int addr_family,
int ifindex,
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
const char *iface,
NMConnectivityCheckCallback callback,
gpointer user_data)
{
NMConnectivityPrivate *priv;
NMConnectivityCheckHandle *cb_data;
static guint64 request_counter = 0;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
g_return_val_if_fail (NM_IS_CONNECTIVITY (self), NULL);
g_return_val_if_fail (callback, NULL);
priv = NM_CONNECTIVITY_GET_PRIVATE (self);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
cb_data = g_slice_new0 (NMConnectivityCheckHandle);
cb_data->self = self;
cb_data->request_counter = ++request_counter;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
c_list_link_tail (&priv->handles_lst_head, &cb_data->handles_lst);
cb_data->callback = callback;
cb_data->user_data = user_data;
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data->completed_state = NM_CONNECTIVITY_UNKNOWN;
cb_data->addr_family = addr_family;
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
if (iface)
cb_data->ifspec = g_strdup_printf ("if!%s", iface);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#if WITH_CONCHECK
if (iface && ifindex > 0 && priv->enabled && priv->host) {
GDBusConnection *dbus_connection;
cb_data->concheck.ifindex = ifindex;
dbus_connection = nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ());
if (!dbus_connection) {
/* we have no D-Bus connection? That might happen in configure and quit mode.
*
* Anyway, something is very odd, just fail connectivity check. */
_LOG2D ("start fake request (fail due to no D-Bus connection)");
cb_data->fail_reason_no_dbus_connection = TRUE;
cb_data->timeout_id = g_idle_add (_idle_cb, cb_data);
return cb_data;
}
cb_data->concheck.resolve_cancellable = g_cancellable_new ();
g_dbus_connection_call (nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ()),
"org.freedesktop.resolve1",
"/org/freedesktop/resolve1",
"org.freedesktop.resolve1.Manager",
"ResolveHostname",
g_variant_new ("(isit)",
(gint32) cb_data->concheck.ifindex,
priv->host,
(gint32) cb_data->addr_family,
SD_RESOLVED_DNS),
G_VARIANT_TYPE ("(a(iiay)st)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
cb_data->concheck.resolve_cancellable,
resolve_cb,
cb_data);
_LOG2D ("start request to '%s' (try resolving '%s' using systemd-resolved)",
priv->uri,
priv->host);
return cb_data;
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#endif
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
_LOG2D ("start fake request");
cb_data->timeout_id = g_idle_add (_idle_cb, cb_data);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
return cb_data;
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
void
nm_connectivity_check_cancel (NMConnectivityCheckHandle *cb_data)
{
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
g_return_if_fail (cb_data);
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
g_return_if_fail (NM_IS_CONNECTIVITY (cb_data->self));
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
nm_assert ( c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst)
|| c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->completed_handles_lst_head, &cb_data->handles_lst));
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
cb_data_complete (cb_data, NM_CONNECTIVITY_CANCELLED, "cancelled");
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
/*****************************************************************************/
gboolean
nm_connectivity_check_enabled (NMConnectivity *self)
{
connectivity: schedule connectivity timers per-device and probe for short outages It might happen, that connectivitiy is lost only for a moment and returns soon after. Based on that assumption, when we loose connectivity we want to have a probe interval where we check for returning connectivity more frequently. For that, we handle tracking of the timeouts per-device. The intervall shall start with 1 seconds, and double the interval time until the full interval is reached. Actually, due to the implementation, it's unlikely that we already perform the second check 1 second later. That is because commonly the first check returns before the one second timeout is reached and bumps the interval to 2 seconds right away. Also, we go through extra lengths so that manual connectivity check delay the periodic checks. By being more smart about that, we can reduce the number of connectivity checks, but still keeping the promise to check at least within the requested interval. The complexity of book keeping the timeouts is remarkable. But I think it is worth the effort and we should try hard to - have a connectivity state as accurate as possible. Clearly, connectivity checking means that we probing, so being more intelligent about timeout and backoff timers can result in a better connectivity state. The connectivity state is important because we use it for the default-route penaly and the GUI indicates bad connectivity. - be intelligent about avoiding redundant connectivity checks. While we want to check often to get an accurate connectivity state, we also want to minimize the number of HTTP requests, in case the connectivity is established and suppossedly stable. Also, perform connectivity checks in every state of the device. Even if a device is disconnected, it still might have connectivity, for example if the user externally adds an IP address on an unmanaged device. https://bugzilla.gnome.org/show_bug.cgi?id=792240
2018-02-20 21:41:14 +01:00
g_return_val_if_fail (NM_IS_CONNECTIVITY (self), FALSE);
connectivity: schedule connectivity timers per-device and probe for short outages It might happen, that connectivitiy is lost only for a moment and returns soon after. Based on that assumption, when we loose connectivity we want to have a probe interval where we check for returning connectivity more frequently. For that, we handle tracking of the timeouts per-device. The intervall shall start with 1 seconds, and double the interval time until the full interval is reached. Actually, due to the implementation, it's unlikely that we already perform the second check 1 second later. That is because commonly the first check returns before the one second timeout is reached and bumps the interval to 2 seconds right away. Also, we go through extra lengths so that manual connectivity check delay the periodic checks. By being more smart about that, we can reduce the number of connectivity checks, but still keeping the promise to check at least within the requested interval. The complexity of book keeping the timeouts is remarkable. But I think it is worth the effort and we should try hard to - have a connectivity state as accurate as possible. Clearly, connectivity checking means that we probing, so being more intelligent about timeout and backoff timers can result in a better connectivity state. The connectivity state is important because we use it for the default-route penaly and the GUI indicates bad connectivity. - be intelligent about avoiding redundant connectivity checks. While we want to check often to get an accurate connectivity state, we also want to minimize the number of HTTP requests, in case the connectivity is established and suppossedly stable. Also, perform connectivity checks in every state of the device. Even if a device is disconnected, it still might have connectivity, for example if the user externally adds an IP address on an unmanaged device. https://bugzilla.gnome.org/show_bug.cgi?id=792240
2018-02-20 21:41:14 +01:00
return NM_CONNECTIVITY_GET_PRIVATE (self)->enabled;
}
/*****************************************************************************/
connectivity: schedule connectivity timers per-device and probe for short outages It might happen, that connectivitiy is lost only for a moment and returns soon after. Based on that assumption, when we loose connectivity we want to have a probe interval where we check for returning connectivity more frequently. For that, we handle tracking of the timeouts per-device. The intervall shall start with 1 seconds, and double the interval time until the full interval is reached. Actually, due to the implementation, it's unlikely that we already perform the second check 1 second later. That is because commonly the first check returns before the one second timeout is reached and bumps the interval to 2 seconds right away. Also, we go through extra lengths so that manual connectivity check delay the periodic checks. By being more smart about that, we can reduce the number of connectivity checks, but still keeping the promise to check at least within the requested interval. The complexity of book keeping the timeouts is remarkable. But I think it is worth the effort and we should try hard to - have a connectivity state as accurate as possible. Clearly, connectivity checking means that we probing, so being more intelligent about timeout and backoff timers can result in a better connectivity state. The connectivity state is important because we use it for the default-route penaly and the GUI indicates bad connectivity. - be intelligent about avoiding redundant connectivity checks. While we want to check often to get an accurate connectivity state, we also want to minimize the number of HTTP requests, in case the connectivity is established and suppossedly stable. Also, perform connectivity checks in every state of the device. Even if a device is disconnected, it still might have connectivity, for example if the user externally adds an IP address on an unmanaged device. https://bugzilla.gnome.org/show_bug.cgi?id=792240
2018-02-20 21:41:14 +01:00
guint
nm_connectivity_get_interval (NMConnectivity *self)
{
connectivity: schedule connectivity timers per-device and probe for short outages It might happen, that connectivitiy is lost only for a moment and returns soon after. Based on that assumption, when we loose connectivity we want to have a probe interval where we check for returning connectivity more frequently. For that, we handle tracking of the timeouts per-device. The intervall shall start with 1 seconds, and double the interval time until the full interval is reached. Actually, due to the implementation, it's unlikely that we already perform the second check 1 second later. That is because commonly the first check returns before the one second timeout is reached and bumps the interval to 2 seconds right away. Also, we go through extra lengths so that manual connectivity check delay the periodic checks. By being more smart about that, we can reduce the number of connectivity checks, but still keeping the promise to check at least within the requested interval. The complexity of book keeping the timeouts is remarkable. But I think it is worth the effort and we should try hard to - have a connectivity state as accurate as possible. Clearly, connectivity checking means that we probing, so being more intelligent about timeout and backoff timers can result in a better connectivity state. The connectivity state is important because we use it for the default-route penaly and the GUI indicates bad connectivity. - be intelligent about avoiding redundant connectivity checks. While we want to check often to get an accurate connectivity state, we also want to minimize the number of HTTP requests, in case the connectivity is established and suppossedly stable. Also, perform connectivity checks in every state of the device. Even if a device is disconnected, it still might have connectivity, for example if the user externally adds an IP address on an unmanaged device. https://bugzilla.gnome.org/show_bug.cgi?id=792240
2018-02-20 21:41:14 +01:00
return nm_connectivity_check_enabled (self)
? NM_CONNECTIVITY_GET_PRIVATE (self)->interval
: 0;
}
static gboolean
host_and_port_from_uri (const char *uri, char **host, char **port)
{
const char *p = uri;
const char *host_begin = NULL;
size_t host_len = 0;
const char *port_begin = NULL;
size_t port_len = 0;
/* scheme */
while (*p != ':' && *p != '/') {
if (!*p++)
return FALSE;
}
/* :// */
if (*p++ != ':')
return FALSE;
if (*p++ != '/')
return FALSE;
if (*p++ != '/')
return FALSE;
/* host */
if (*p == '[')
return FALSE;
host_begin = p;
while (*p && *p != ':' && *p != '/') {
host_len++;
p++;
}
if (host_len == 0)
return FALSE;
*host = strndup (host_begin, host_len);
/* port */
if (*p++ == ':') {
port_begin = p;
while (*p && *p != '/') {
port_len++;
p++;
}
if (port_len)
*port = strndup (port_begin, port_len);
}
return TRUE;
}
static void
update_config (NMConnectivity *self, NMConfigData *config_data)
{
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
const char *uri, *response;
guint interval;
gboolean enabled;
gboolean changed = FALSE;
/* Set the URI. */
uri = nm_config_data_get_connectivity_uri (config_data);
if (uri && !*uri)
uri = NULL;
changed = g_strcmp0 (uri, priv->uri) != 0;
if (uri) {
char *scheme = g_uri_parse_scheme (uri);
if (!scheme) {
_LOGE ("invalid URI '%s' for connectivity check.", uri);
uri = NULL;
} else if (strcasecmp (scheme, "https") == 0) {
_LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri);
} else if (strcasecmp (scheme, "http") != 0) {
_LOGE ("scheme of '%s' uri doesn't use a scheme that is allowed for connectivity check.", uri);
uri = NULL;
}
if (scheme)
g_free (scheme);
}
if (changed) {
g_free (priv->uri);
priv->uri = g_strdup (uri);
g_clear_pointer (&priv->host, g_free);
g_clear_pointer (&priv->port, g_free);
if (uri)
host_and_port_from_uri (uri, &priv->host, &priv->port);
}
/* Set the interval. */
interval = nm_config_data_get_connectivity_interval (config_data);
connectivity: schedule connectivity timers per-device and probe for short outages It might happen, that connectivitiy is lost only for a moment and returns soon after. Based on that assumption, when we loose connectivity we want to have a probe interval where we check for returning connectivity more frequently. For that, we handle tracking of the timeouts per-device. The intervall shall start with 1 seconds, and double the interval time until the full interval is reached. Actually, due to the implementation, it's unlikely that we already perform the second check 1 second later. That is because commonly the first check returns before the one second timeout is reached and bumps the interval to 2 seconds right away. Also, we go through extra lengths so that manual connectivity check delay the periodic checks. By being more smart about that, we can reduce the number of connectivity checks, but still keeping the promise to check at least within the requested interval. The complexity of book keeping the timeouts is remarkable. But I think it is worth the effort and we should try hard to - have a connectivity state as accurate as possible. Clearly, connectivity checking means that we probing, so being more intelligent about timeout and backoff timers can result in a better connectivity state. The connectivity state is important because we use it for the default-route penaly and the GUI indicates bad connectivity. - be intelligent about avoiding redundant connectivity checks. While we want to check often to get an accurate connectivity state, we also want to minimize the number of HTTP requests, in case the connectivity is established and suppossedly stable. Also, perform connectivity checks in every state of the device. Even if a device is disconnected, it still might have connectivity, for example if the user externally adds an IP address on an unmanaged device. https://bugzilla.gnome.org/show_bug.cgi?id=792240
2018-02-20 21:41:14 +01:00
interval = MIN (interval, (7 * 24 * 3600));
if (priv->interval != interval) {
priv->interval = interval;
changed = TRUE;
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
enabled = FALSE;
#if WITH_CONCHECK
if ( priv->uri
&& priv->interval)
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
enabled = nm_config_data_get_connectivity_enabled (config_data);
#endif
if (priv->enabled != enabled) {
priv->enabled = enabled;
changed = TRUE;
}
/* Set the response. */
response = nm_config_data_get_connectivity_response (config_data);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
if (!nm_streq0 (response, priv->response)) {
/* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response
* (including "") is accepted. */
g_free (priv->response);
priv->response = g_strdup (response);
changed = TRUE;
}
connectivity: schedule connectivity timers per-device and probe for short outages It might happen, that connectivitiy is lost only for a moment and returns soon after. Based on that assumption, when we loose connectivity we want to have a probe interval where we check for returning connectivity more frequently. For that, we handle tracking of the timeouts per-device. The intervall shall start with 1 seconds, and double the interval time until the full interval is reached. Actually, due to the implementation, it's unlikely that we already perform the second check 1 second later. That is because commonly the first check returns before the one second timeout is reached and bumps the interval to 2 seconds right away. Also, we go through extra lengths so that manual connectivity check delay the periodic checks. By being more smart about that, we can reduce the number of connectivity checks, but still keeping the promise to check at least within the requested interval. The complexity of book keeping the timeouts is remarkable. But I think it is worth the effort and we should try hard to - have a connectivity state as accurate as possible. Clearly, connectivity checking means that we probing, so being more intelligent about timeout and backoff timers can result in a better connectivity state. The connectivity state is important because we use it for the default-route penaly and the GUI indicates bad connectivity. - be intelligent about avoiding redundant connectivity checks. While we want to check often to get an accurate connectivity state, we also want to minimize the number of HTTP requests, in case the connectivity is established and suppossedly stable. Also, perform connectivity checks in every state of the device. Even if a device is disconnected, it still might have connectivity, for example if the user externally adds an IP address on an unmanaged device. https://bugzilla.gnome.org/show_bug.cgi?id=792240
2018-02-20 21:41:14 +01:00
if (changed)
g_signal_emit (self, signals[CONFIG_CHANGED], 0);
}
static void
config_changed_cb (NMConfig *config,
NMConfigData *config_data,
NMConfigChangeFlags changes,
NMConfigData *old_data,
NMConnectivity *self)
{
update_config (self, config_data);
}
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
/*****************************************************************************/
static void
nm_connectivity_init (NMConnectivity *self)
{
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
#if WITH_CONCHECK
CURLcode ret;
#endif
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
c_list_init (&priv->handles_lst_head);
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
c_list_init (&priv->completed_handles_lst_head);
priv->config = g_object_ref (nm_config_get ());
g_signal_connect (G_OBJECT (priv->config),
NM_CONFIG_SIGNAL_CONFIG_CHANGED,
G_CALLBACK (config_changed_cb),
self);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#if WITH_CONCHECK
ret = curl_global_init (CURL_GLOBAL_ALL);
if (ret != CURLE_OK) {
_LOGE ("unable to init cURL, connectivity check will not work: (%d) %s",
ret, curl_easy_strerror (ret));
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
}
#endif
update_config (self, nm_config_get_data (priv->config));
}
static void
dispose (GObject *object)
{
NMConnectivity *self = NM_CONNECTIVITY (object);
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
NMConnectivityCheckHandle *cb_data;
connectivity: fix crash when removing easy-handle from curl callback libcurl does not allow removing easy-handles from within a curl callback. That was already partly avoided for one handle alone. That is, when a handle completed inside a libcurl callback, it would only invoke the callback, but not yet delete it. However, that is not enough, because from within a callback another handle can be cancelled, leading to the removal of (the other) handle and a crash: ==24572== at 0x40319AB: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==24572== by 0x52DDAE5: Curl_close (url.c:392) ==24572== by 0x52EC02C: curl_easy_cleanup (easy.c:825) ==24572== by 0x5FDCD2: cb_data_free (nm-connectivity.c:215) ==24572== by 0x5FF6DE: nm_connectivity_check_cancel (nm-connectivity.c:585) ==24572== by 0x55F7F9: concheck_handle_complete (nm-device.c:2601) ==24572== by 0x574C12: concheck_cb (nm-device.c:2725) ==24572== by 0x5FD887: cb_data_invoke_callback (nm-connectivity.c:167) ==24572== by 0x5FD959: easy_header_cb (nm-connectivity.c:435) ==24572== by 0x52D73CB: chop_write (sendf.c:612) ==24572== by 0x52D73CB: Curl_client_write (sendf.c:668) ==24572== by 0x52D54ED: Curl_http_readwrite_headers (http.c:3904) ==24572== by 0x52E9EA7: readwrite_data (transfer.c:548) ==24572== by 0x52E9EA7: Curl_readwrite (transfer.c:1161) ==24572== by 0x52F4193: multi_runsingle (multi.c:1915) ==24572== by 0x52F5531: multi_socket (multi.c:2607) ==24572== by 0x52F5804: curl_multi_socket_action (multi.c:2771) Fix that, by never invoking any callbacks when we are inside a libcurl callback. Instead, the handle is marked for completion and queued. Later, we complete all queue handles separately. While at it, drop the @error argument from NMConnectivityCheckCallback. It was only used to signal cancellation. Let's instead signal that via status NM_CONNECTIVITY_CANCELLED. https://bugzilla.gnome.org/show_bug.cgi?id=797136 https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1792745 https://bugzilla.opensuse.org/show_bug.cgi?id=1107197 https://github.com/NetworkManager/NetworkManager/pull/207 Fixes: d8a31794c8b9db243076ba0c24dfe6e496b78697
2018-09-17 11:08:15 +02:00
nm_assert (c_list_is_empty (&priv->completed_handles_lst_head));
while ((cb_data = c_list_first_entry (&priv->handles_lst_head,
NMConnectivityCheckHandle,
handles_lst)))
cb_data_complete (cb_data, NM_CONNECTIVITY_DISPOSING, "shutting down");
g_clear_pointer (&priv->uri, g_free);
g_clear_pointer (&priv->host, g_free);
g_clear_pointer (&priv->port, g_free);
g_clear_pointer (&priv->response, g_free);
connectivity: rework async connectivity check requests An asynchronous request should either be cancellable or not keep the target object alive. Preferably both. Otherwise, it is impossible to do a controlled shutdown when terminating NetworkManager. Currently, when NetworkManager is about to terminate, it just quits the mainloop and essentially leaks everything. That is a bug. If we ever want to fix that, every asynchronous request must be cancellable in a controlled way (or it must not prevent objects from getting disposed, where disposing the object automatically cancels the callback). Rework the asynchronous request for connectivity check to - return a handle that can be used to cancel the operation. Cancelling is optional. The caller may choose to ignore the handle because the asynchronous operation does not keep the target object alive. That means, it is still possible to shutdown, by everybody giving up their reference to the target object. In which case the callback will be invoked during dispose() of the target object. - also, the callback will always be invoked exactly once, and never synchronously from within the asynchronous start call. But during cancel(), the callback is invoked synchronously from within cancel(). Note that it's only allowed to cancel an action at most once, and never after the callback is invoked (also not from within the callback itself). - also, NMConnectivity already supports a fake handler, in case connectivity check is disabled via configuration. Hence, reuse the same code paths also when compiling without --enable-concheck. That means, instead of having #if WITH_CONCHECK at various callers, move them into NMConnectivity. The downside is, that if you build without concheck, there is a small overhead compared to before. The upside is, we reuse the same code paths when compiling with or without concheck. - also, the patch synchronizes the connecitivty states. For example, previously `nmcli networking connectivity check` would schedule requests in parallel, and return the accumulated result of the individual requests. However, the global connectivity state of the manager might have have been the same as the answer to the explicit connecitivity check, because while the answer for the manual check is waiting for all pending checks to complete, the global connectivity state could already change. That is just wrong. There are not multiple global connectivity states at the same time, there is just one. A manual connectivity check should have the meaning of ensure that the global state is up to date, but it still should return the global connectivity state -- not the answers for several connectivity checks issued in parallel. This is related to commit b799de281bc01073c31dd2c86171b29c8132441c (libnm: update property in the manager after connectivity check), which tries to address a similar problem client side. Similarly, each device has a connectivity state. While there might be several connectivity checks per device pending, whenever a check completes, it can update the per-device state (and return that device state as result), but the immediate answer of the individual check might not matter. This is especially the case, when a later request returns earlier and obsoletes all earlier requests. In that case, earlier requests return with the result of the currend devices connectivity state. This patch cleans up the internal API and gives a better defined behavior to the user (thus, the simple API which simplifies implementation for the caller). However, the implementation of getting this API right and properly handle cancel and destruction of the target object is more complicated and complex. But this but is not just for the sake of a nicer API. This fixes actual issues explained above. Also, get rid of GAsyncResult to track information about the pending request. Instead, allocate our own handle structure, which ends up to be nicer because it's strongly typed and has exactly the properties that are useful to track the request. Also, it gets rid of the awkward _finish() API by passing the relevant arguments to the callback directly.
2018-01-05 17:46:49 +01:00
#if WITH_CONCHECK
curl_global_cleanup ();
#endif
if (priv->config) {
g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
g_clear_object (&priv->config);
}
G_OBJECT_CLASS (nm_connectivity_parent_class)->dispose (object);
}
static void
nm_connectivity_class_init (NMConnectivityClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
connectivity: schedule connectivity timers per-device and probe for short outages It might happen, that connectivitiy is lost only for a moment and returns soon after. Based on that assumption, when we loose connectivity we want to have a probe interval where we check for returning connectivity more frequently. For that, we handle tracking of the timeouts per-device. The intervall shall start with 1 seconds, and double the interval time until the full interval is reached. Actually, due to the implementation, it's unlikely that we already perform the second check 1 second later. That is because commonly the first check returns before the one second timeout is reached and bumps the interval to 2 seconds right away. Also, we go through extra lengths so that manual connectivity check delay the periodic checks. By being more smart about that, we can reduce the number of connectivity checks, but still keeping the promise to check at least within the requested interval. The complexity of book keeping the timeouts is remarkable. But I think it is worth the effort and we should try hard to - have a connectivity state as accurate as possible. Clearly, connectivity checking means that we probing, so being more intelligent about timeout and backoff timers can result in a better connectivity state. The connectivity state is important because we use it for the default-route penaly and the GUI indicates bad connectivity. - be intelligent about avoiding redundant connectivity checks. While we want to check often to get an accurate connectivity state, we also want to minimize the number of HTTP requests, in case the connectivity is established and suppossedly stable. Also, perform connectivity checks in every state of the device. Even if a device is disconnected, it still might have connectivity, for example if the user externally adds an IP address on an unmanaged device. https://bugzilla.gnome.org/show_bug.cgi?id=792240
2018-02-20 21:41:14 +01:00
signals[CONFIG_CHANGED] =
g_signal_new (NM_CONNECTIVITY_CONFIG_CHANGED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->dispose = dispose;
}