2019-09-10 11:19:01 +02:00
|
|
|
// SPDX-License-Identifier: LGPL-2.1+
|
2017-05-19 10:19:25 +02:00
|
|
|
/*
|
2019-10-01 09:20:35 +02:00
|
|
|
* Copyright (C) 2017, 2018 Red Hat, Inc.
|
2017-05-19 10:19:25 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef __NM_LIBNM_UTILS_H__
|
|
|
|
|
#define __NM_LIBNM_UTILS_H__
|
|
|
|
|
|
2019-10-18 08:20:59 +02:00
|
|
|
#include "nm-types.h"
|
2019-10-22 16:51:09 +02:00
|
|
|
#include "nm-glib-aux/nm-ref-string.h"
|
2019-10-18 08:20:59 +02:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
libnm: deprecate synchronous/blocking API in libnm
Note that D-Bus is fundamentally asynchronous. Doing blocking calls
on top of D-Bus is odd, especially for libnm's NMClient. That is because
NMClient essentially is a client-side cache of the objects from the D-Bus
interface. This cache should be filled exclusively by (asynchronous) D-Bus
events (PropertiesChanged). So, making a blocking D-Bus call means to wait
for a response and return it, while queuing all messages that are received
in the meantime.
Basically there are three ways how a synchronous API on NMClient could behave:
1) the call just calls g_dbus_connection_call_sync(). This means
that libnm sends a D-Bus request via GDBusConnection, and blockingly
waits for the response. All D-Bus messages that get received in the
meantime are queued in the GMainContext that belongs to NMClient.
That means, none of these D-Bus events are processed until we
iterate the GMainContext after the call returns. The effect is,
that NMClient (and all cached objects in there) are unaffected by
the D-Bus request.
Most of the synchronous API calls in libnm are of this kind.
The problem is that the strict ordering of D-Bus events gets
violated.
For some API this is not an immediate problem. Take for example
nm_device_wifi_request_scan(). The call merely blockingly tells
NetworkManager to start scanning, but since NetworkManager's D-Bus
API does not directly expose any state that tells whether we are
currently scanning, this out of order processing of the D-Bus
request is a small issue.
The problem is more obvious for nm_client_networking_set_enabled().
After calling it, NM_CLIENT_NETWORKING_ENABLED is still unaffected
and unchanged, because the PropertiesChanged signal from D-Bus
is not yet processed.
This means, while you make such a blocking call, NMClient's state
does not change. But usually you perform the synchronous call
to change some state. In this form, the blocking call is not useful,
because NMClient only changes the state after iterating the GMainContext,
and not after the blocking call returns.
2) like 1), but after making the blocking g_dbus_connection_call_sync(),
update the NMClient cache artificially. This is what
nm_manager_check_connectivity() does, to "fix" bgo#784629.
This also has the problem of out-of-order events, but it kinda
solves the problem of not changing the state during the blocking
call. But it does so by hacking the state of the cache. I think
this is really wrong because the state should only be updated from
the ordered stream of D-Bus messages (PropertiesChanged signal and
similar). When libnm decides to modify the state, there may be already
D-Bus messages queued that affect this very state.
3) instead of calling g_dbus_connection_call_sync(), use the
asynchronous g_dbus_connection_call(). If we would use a sepaate
GMainContext for all D-Bus related calls, we could ensure that
while we block for the response, we iterate that internal main context.
This might be nice, because all events are processed in order and
after the blocking call returns, the NMClient state is up to date.
The are problems however: current blocking API does not do this,
so it's a significant change in behavior. Also, it might be
unexpected to the user that during the blocking call the entire
content of NMClient's cache might change and all pointers to the
cache might be invalidated. Also, of course NMClient would invoke
signals for all the changes that happen.
Another problem is that this would be more effort to implement
and it involves a small performance overhead for all D-Bus related
calls (because we have to serialize all events in an internal
GMainContext first and then invoke them on the caller's context).
Also, if the users wants this behavior, they could implement it themself
by running libnm in their own GMainContext. Note that libnm might
have bugs to make that really working, but that should be fixed
instead of adding such synchrnous API behavior.
Read also [1], for why blocking calls are wrong.
[1] https://smcv.pseudorandom.co.uk/2008/11/nonblocking/
So, all possible behaviors for synchronous API have severe behavioural
issues. Mark all this API as deprecated. Also, this serves the purpose of
identifying blocking D-Bus calls in libnm.
Note that "deprecated" here does not really mean that the API is going
to be removed. We don't break API. The user may:
- continue to use this API. It's deprecated, awkward and discouraged,
but if it works, by all means use it.
- use asynchronous API. That's the only sensible way to use D-Bus.
If libnm lacks a certain asynchronous counterpart, it should be
added.
- use GDBusConnection directly. There really isn't anything wrong
with D-Bus or GDBusConnection. This deprecated API is just a wrapper
around g_dbus_connection_call_sync(). You may call it directly
without feeling dirty.
---
The only other remainging API is the synchronous GInitable call for
NMClient. That is an entirely separate beast and not particularly
wrong (from an API point of view).
Note that synchronous API in NMSecretAgentOld, NMVpnPluginOld and
NMVpnServicePlugin as not deprecated here. These types are not part
of the D-Bus cache and while they have similar issues, it's less severe
because they have less state.
2019-09-04 13:58:43 +02:00
|
|
|
/* Markers for deprecated sync code in internal API. */
|
|
|
|
|
#define _NM_DEPRECATED_SYNC_METHOD_INTERNAL NM_DEPRECATED_IN_1_22
|
|
|
|
|
#define _NM_DEPRECATED_SYNC_WRITABLE_PROPERTY_INTERNAL NM_DEPRECATED_IN_1_22
|
|
|
|
|
|
libnm: add logging NML_DBUS_LOG*() for debugging D-Bus for NMClient
Commonly, a library (like libnm) is not supposed to log anything.
Logging is not a suitable way to notify the calling application
about anything. When something of importance happens, then the
application must be notified via the library's API.
However, logging can be very useful for debugging to see what is going
on. Add a logging macro that by default does nothing, but can be turned
on via an environment variable "LIBNM_CLIENT_DEBUG=debug".
Another point is that libnm relies on the server side NetworkManager
D-Bus interface to be in an expected manner. For example, we require a
D-Bus object "org.freedesktop.NetworkManager" to be present and certain
D-Bus interfaces implemented.
However libnm should treat NetworkManager as external and untrusted component.
That means, we cannot assert against the expectations we have. There are two
reasons for this:
- a bug in NetworkManager, dbus-daemon or else may cause such errors.
This must not trigger an assertion failure in the client
application, at least not unless requested.
- libnm must be forward and backward compatible against a different
NetworkManager server version. That is only possibly by ignoring
anything that is unexpected. Asserting by default might prevent
to implement API changes, both on libnm and server side.
Note that we also don't notify the calling application via dedicated
API. On the one hand, these things *can* happen. On the other hand, what
would the calling appication do about it anyway? libnm by default must
just behave gracefully and pretend all is good.
For testing, development and debugging that is however not useful. We
want the user to opt in to strict API validation. The user will be able
to do that by setting "LIBNM_CLIENT_DEBUG=warning", which causes API
violations being logged with g_warning(). These are assertions when
running with G_DEBUG=fatal-warnings.
This is inspired by GDBus' G_DBUS_DEBUG variable.
Note that LIBNM_CLIENT_DEBUG environment variables is undocumented, unstable
API. It's used for debugging and testing of the current libnm version at hand.
There is no guaranteed stable behavior how a different libnm version
might behave.
2019-10-14 08:06:33 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
_NML_DBUS_LOG_LEVEL_INITIALIZED = 0x01,
|
|
|
|
|
|
|
|
|
|
_NML_DBUS_LOG_LEVEL_TRACE = 0x02,
|
|
|
|
|
|
|
|
|
|
_NML_DBUS_LOG_LEVEL_DEBUG = 0x04,
|
|
|
|
|
|
|
|
|
|
/* the difference between a warning and a critical is that it results in
|
|
|
|
|
* g_warning() vs. g_critical() messages. Note that we want to use "warnings"
|
|
|
|
|
* for unknown D-Bus API that could just result because we run against a
|
|
|
|
|
* newer NetworkManager version (such warnings are more graceful, because
|
|
|
|
|
* we want that libnm can be forward compatible against newer servers).
|
|
|
|
|
* Critial warnings should be emitted when NetworkManager exposes something
|
|
|
|
|
* on D-Bus that breaks the current expectations. Usually NetworkManager
|
|
|
|
|
* should not break API, hence such issues are more severe. */
|
|
|
|
|
_NML_DBUS_LOG_LEVEL_WARN = 0x08,
|
|
|
|
|
_NML_DBUS_LOG_LEVEL_ERROR = 0x10,
|
|
|
|
|
|
|
|
|
|
/* ANY is only relevant for nml_dbus_log_enabled() to check whether any of the
|
|
|
|
|
* options is on. */
|
|
|
|
|
NML_DBUS_LOG_LEVEL_ANY = _NML_DBUS_LOG_LEVEL_INITIALIZED,
|
|
|
|
|
|
|
|
|
|
NML_DBUS_LOG_LEVEL_TRACE = _NML_DBUS_LOG_LEVEL_TRACE,
|
|
|
|
|
NML_DBUS_LOG_LEVEL_DEBUG = _NML_DBUS_LOG_LEVEL_DEBUG
|
|
|
|
|
| NML_DBUS_LOG_LEVEL_TRACE,
|
|
|
|
|
NML_DBUS_LOG_LEVEL_WARN = _NML_DBUS_LOG_LEVEL_WARN
|
|
|
|
|
| NML_DBUS_LOG_LEVEL_DEBUG,
|
|
|
|
|
NML_DBUS_LOG_LEVEL_ERROR = _NML_DBUS_LOG_LEVEL_ERROR
|
|
|
|
|
| NML_DBUS_LOG_LEVEL_WARN,
|
|
|
|
|
} NMLDBusLogLevel;
|
|
|
|
|
|
|
|
|
|
extern volatile int _nml_dbus_log_level;
|
|
|
|
|
|
|
|
|
|
int _nml_dbus_log_level_init (void);
|
|
|
|
|
|
|
|
|
|
static inline gboolean
|
|
|
|
|
nml_dbus_log_enabled (NMLDBusLogLevel level)
|
|
|
|
|
{
|
|
|
|
|
int l;
|
|
|
|
|
|
|
|
|
|
nm_assert (NM_IN_SET (level, NML_DBUS_LOG_LEVEL_ANY,
|
|
|
|
|
NML_DBUS_LOG_LEVEL_TRACE,
|
|
|
|
|
NML_DBUS_LOG_LEVEL_DEBUG,
|
|
|
|
|
NML_DBUS_LOG_LEVEL_WARN,
|
|
|
|
|
NML_DBUS_LOG_LEVEL_ERROR));
|
|
|
|
|
|
|
|
|
|
l = g_atomic_int_get (&_nml_dbus_log_level);
|
|
|
|
|
if (G_UNLIKELY (l == 0))
|
|
|
|
|
l = _nml_dbus_log_level_init ();
|
|
|
|
|
|
|
|
|
|
nm_assert (l & _NML_DBUS_LOG_LEVEL_INITIALIZED);
|
|
|
|
|
if (level == NML_DBUS_LOG_LEVEL_ANY)
|
|
|
|
|
return l != _NML_DBUS_LOG_LEVEL_INITIALIZED;
|
|
|
|
|
return !!(((NMLDBusLogLevel) l) & level);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _nml_dbus_log (NMLDBusLogLevel level,
|
|
|
|
|
const char *fmt,
|
|
|
|
|
...) _nm_printf (2, 3);
|
|
|
|
|
|
|
|
|
|
#define NML_DBUS_LOG(level, ...) \
|
|
|
|
|
G_STMT_START { \
|
|
|
|
|
G_STATIC_ASSERT ( (level) == NML_DBUS_LOG_LEVEL_TRACE \
|
|
|
|
|
|| (level) == NML_DBUS_LOG_LEVEL_DEBUG \
|
|
|
|
|
|| (level) == NML_DBUS_LOG_LEVEL_WARN \
|
|
|
|
|
|| (level) == NML_DBUS_LOG_LEVEL_ERROR); \
|
|
|
|
|
\
|
|
|
|
|
if (nml_dbus_log_enabled (level)) { \
|
|
|
|
|
_nml_dbus_log ((level), __VA_ARGS__); \
|
|
|
|
|
} \
|
|
|
|
|
} G_STMT_END
|
|
|
|
|
|
|
|
|
|
#define NML_DBUS_LOG_T(...) NML_DBUS_LOG (NML_DBUS_LOG_LEVEL_TRACE, __VA_ARGS__)
|
|
|
|
|
#define NML_DBUS_LOG_D(...) NML_DBUS_LOG (NML_DBUS_LOG_LEVEL_DEBUG, __VA_ARGS__)
|
|
|
|
|
#define NML_DBUS_LOG_W(...) NML_DBUS_LOG (NML_DBUS_LOG_LEVEL_WARN, __VA_ARGS__)
|
|
|
|
|
#define NML_DBUS_LOG_E(...) NML_DBUS_LOG (NML_DBUS_LOG_LEVEL_ERROR, __VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
#define NML_NMCLIENT_LOG(level, self, ...) \
|
|
|
|
|
NML_DBUS_LOG ((level), \
|
|
|
|
|
"nmclient["NM_HASH_OBFUSCATE_PTR_FMT"]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
|
|
|
|
|
NM_HASH_OBFUSCATE_PTR (self) \
|
|
|
|
|
_NM_UTILS_MACRO_REST (__VA_ARGS__))
|
|
|
|
|
|
|
|
|
|
#define NML_NMCLIENT_LOG_T(self, ...) NML_NMCLIENT_LOG (NML_DBUS_LOG_LEVEL_TRACE, self, __VA_ARGS__)
|
|
|
|
|
#define NML_NMCLIENT_LOG_D(self, ...) NML_NMCLIENT_LOG (NML_DBUS_LOG_LEVEL_DEBUG, self, __VA_ARGS__)
|
|
|
|
|
#define NML_NMCLIENT_LOG_W(self, ...) NML_NMCLIENT_LOG (NML_DBUS_LOG_LEVEL_WARN, self, __VA_ARGS__)
|
|
|
|
|
#define NML_NMCLIENT_LOG_E(self, ...) NML_NMCLIENT_LOG (NML_DBUS_LOG_LEVEL_ERROR, self, __VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-10-22 16:51:09 +02:00
|
|
|
static inline const char *
|
|
|
|
|
_nml_coerce_property_str_not_null (const char *str)
|
|
|
|
|
{
|
|
|
|
|
return str ?: "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const char *
|
|
|
|
|
_nml_coerce_property_str_not_empty (const char *str)
|
|
|
|
|
{
|
|
|
|
|
return str && str[0] ? str : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const char *
|
|
|
|
|
_nml_coerce_property_object_path (NMRefString *path)
|
|
|
|
|
{
|
|
|
|
|
if (!path)
|
|
|
|
|
return NULL;
|
2019-10-24 12:29:21 +02:00
|
|
|
return nm_dbus_path_not_empty (path->str);
|
2019-10-22 16:51:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline const char *const*
|
|
|
|
|
_nml_coerce_property_strv_not_null (char **strv)
|
|
|
|
|
{
|
|
|
|
|
return ((const char *const*) strv) ?: NM_PTRARRAY_EMPTY (const char *);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2019-10-15 12:53:53 +02:00
|
|
|
char *nm_utils_wincaps_to_dash (const char *caps);
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-02-23 14:52:00 +01:00
|
|
|
char *nm_utils_fixup_vendor_string (const char *desc);
|
2018-02-23 14:52:05 +01:00
|
|
|
char *nm_utils_fixup_product_string (const char *desc);
|
2017-05-19 10:19:25 +02:00
|
|
|
|
2019-10-18 08:12:01 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
struct _NMObjectPrivate;
|
|
|
|
|
|
|
|
|
|
struct _NMObject {
|
|
|
|
|
GObject parent;
|
|
|
|
|
struct _NMObjectPrivate *_priv;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _NMObjectClass {
|
|
|
|
|
GObjectClass parent;
|
|
|
|
|
|
|
|
|
|
void (*init_dbus) (struct _NMObject *object);
|
|
|
|
|
|
|
|
|
|
/* The "object-creation-failed" method is PRIVATE for libnm and
|
|
|
|
|
* is not meant for any external usage. It indicates that an error
|
|
|
|
|
* occurred during creation of an object.
|
|
|
|
|
*/
|
|
|
|
|
void (*object_creation_failed) (struct _NMObject *master_object,
|
|
|
|
|
const char *failed_path);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
struct _NMDevicePrivate;
|
|
|
|
|
|
|
|
|
|
struct _NMDevice {
|
|
|
|
|
NMObject parent;
|
|
|
|
|
struct _NMDevicePrivate *_priv;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _NMDeviceClass {
|
|
|
|
|
struct _NMObjectClass parent;
|
|
|
|
|
|
|
|
|
|
/* Signals */
|
|
|
|
|
void (*state_changed) (NMDevice *device,
|
|
|
|
|
NMDeviceState new_state,
|
|
|
|
|
NMDeviceState old_state,
|
|
|
|
|
NMDeviceStateReason reason);
|
|
|
|
|
|
|
|
|
|
/* Methods */
|
|
|
|
|
gboolean (*connection_compatible) (NMDevice *device,
|
|
|
|
|
NMConnection *connection,
|
|
|
|
|
GError **error);
|
|
|
|
|
|
|
|
|
|
const char * (*get_type_description) (NMDevice *device);
|
|
|
|
|
const char * (*get_hw_address) (NMDevice *device);
|
|
|
|
|
|
|
|
|
|
GType (*get_setting_type) (NMDevice *device);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
struct _NMActiveConnectionPrivate;
|
|
|
|
|
|
|
|
|
|
struct _NMActiveConnection {
|
|
|
|
|
NMObject parent;
|
|
|
|
|
struct _NMActiveConnectionPrivate *_priv;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _NMActiveConnectionClass {
|
|
|
|
|
struct _NMObjectClass parent;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
struct _NMDhcpConfigPrivate;
|
|
|
|
|
|
|
|
|
|
struct _NMDhcpConfig {
|
|
|
|
|
NMObject parent;
|
|
|
|
|
struct _NMDhcpConfigPrivate *_priv;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _NMDhcpConfigClass {
|
|
|
|
|
struct _NMObjectClass parent;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
struct _NMIPConfigPrivate;
|
|
|
|
|
|
|
|
|
|
struct _NMIPConfig {
|
|
|
|
|
NMObject parent;
|
|
|
|
|
struct _NMIPConfigPrivate *_priv;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _NMIPConfigClass {
|
|
|
|
|
struct _NMObjectClass parent;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-05-19 10:19:25 +02:00
|
|
|
#endif /* __NM_LIBNM_UTILS_H__ */
|