GArray.data is a char pointer. Most of the time we track other data in
a GArray. Casting that pointer can trigger "-Wcast-align=strict"
warnings.
Avoid them. Most of the time, instead use the nm_g_array*() helpers,
which also assert that the expected element size is correct.
These variants provide additional nm_assert() checks, and are thus
preferable.
Note that we cannot just blindly replace &g_array_index() with
&nm_g_array_index(), because the latter would not allow getting a
pointer at index [arr->len]. That might be a valid (though uncommon)
usecase. The correct replacement of &g_array_index() is thus
nm_g_array_index_p().
I checked the code manually and replaced uses of nm_g_array_index_p()
with &nm_g_array_index(), if that was a safe thing to do. The latter
seems preferable, because it is familar to &g_array_index().
We use clang-format for automatic formatting of our source files.
Since clang-format is actively maintained software, the actual
formatting depends on the used version of clang-format. That is
unfortunate and painful, but really unavoidable unless clang-format
would be strictly bug-compatible.
So the version that we must use is from the current Fedora release, which
is also tested by our gitlab-ci. Previously, we were using Fedora 34 with
clang-tools-extra-12.0.1-1.fc34.x86_64.
As Fedora 35 comes along, we need to update our formatting as Fedora 35
comes with version "13.0.0~rc1-1.fc35".
An alternative would be to freeze on version 12, but that has different
problems (like, it's cumbersome to rebuild clang 12 on Fedora 35 and it
would be cumbersome for our developers which are on Fedora 35 to use a
clang that they cannot easily install).
The (differently painful) solution is to reformat from time to time, as we
switch to a new Fedora (and thus clang) version.
Usually we would expect that such a reformatting brings minor changes.
But this time, the changes are huge. That is mentioned in the release
notes [1] as
Makes PointerAligment: Right working with AlignConsecutiveDeclarations. (Fixes https://llvm.org/PR27353)
[1] https://releases.llvm.org/13.0.0/tools/clang/docs/ReleaseNotes.html#clang-format
"direct" properties are the latest preferred way to implement GObject
base properties. That way, the property meta data tracks the
"direct_type" and the offset where to find the data in the struct.
That way, we can automatically
- initialize the default values
- free during finalize
- implement get_property()/set_property()
Also, the other settings operations (compare, to/from D-Bus) are
implemented more efficiently and don't need to go through
g_object_get_property()/GValue API.
Certain properties need to release memory when destroying the NMSetting.
For "direct" properties, we have all the information we need to do that
generically in the NMSetting base class. In practice, this only concerns
string properties.
See _finalize_direct() in "nm-setting.c".
However, if the NMSetting base class takes care of freeing the strings,
then the subclasses must not also unref the variable (to avoid double free).
Previously, subclasses had to opt-in for the base class to indicate that
they are fine with that.
Now, let the base class always handle it. We only need to make sure that
classes that implement direct string properties don't also try to free
the values during destruction.
Note that most implementations use g_object_set(), and it's not
easy to detect modification. In those cases, we assume that modification
happened -- just like also the GObject setter will emit a notification
(as none of our properties use G_PARAM_EXPLICIT_NOTIFY).
These functions tend to have many arguments. They are also quite som
boilerplate to implement the hundereds of properties we have, while
we want that properties have common behaviors and similarities.
Instead of repeatedly spelling out the function arguments, use a macro.
Advantages:
- the usage of a _NM_SETT_INFO_PROP_*_FCN_ARGS macro signals that this
is an implementation of a property. You can now grep for these macros
to find all implementation. That was previously rather imprecise, you
could only `git grep '\.to_dbus_fcn'` to find the uses, but not the
implementations.
As the goal is to keep properties "similar", there is a desire to
reduce the number of similar implementations and to find them.
- changing the arguments now no longer will require you to go through
all implementations. At least not, if you merely add an argument that
has a reasonable default behavior and does not require explicit
handling by most implementation.
- it's convenient to be able to patch the argument list to let the
compiler help to reason about something. For example, the
"connection_dict" argument to from_dbus_fcn() is usually unused.
If you'd like to find who uses it, rename the parameter, and
review the (few) compiler errors.
- it does save 573 LOC of boilerplate with no actual logic or useful
information. I argue, that this simplifies the code and review, by
increasing the relative amount of actually meaningful code.
Disadvantages:
- the user no longer directly sees the argument list. They would need
cscope/ctags or an IDE to jump to the macro definition and conveniently
see all arguments.
Also use _nm_nil, so that clang-format interprets this as a function
parameter list. Otherwise, it formats the function differently.
Naming is important, because the name of a thing should give you a good
idea what it does. Also, to find a thing, it needs a good name in the
first place. But naming is also hard.
Historically, some strv helper API was named as nm_utils_strv_*(),
and some API had a leading underscore (as it is internal API).
This was all inconsistent. Do some renaming and try to unify things.
We get rid of the leading underscore if this is just a regular
(internal) helper. But not for example from _nm_strv_find_first(),
because that is the implementation of nm_strv_find_first().
- _nm_utils_strv_cleanup() -> nm_strv_cleanup()
- _nm_utils_strv_cleanup_const() -> nm_strv_cleanup_const()
- _nm_utils_strv_cmp_n() -> _nm_strv_cmp_n()
- _nm_utils_strv_dup() -> _nm_strv_dup()
- _nm_utils_strv_dup_packed() -> _nm_strv_dup_packed()
- _nm_utils_strv_find_first() -> _nm_strv_find_first()
- _nm_utils_strv_sort() -> _nm_strv_sort()
- _nm_utils_strv_to_ptrarray() -> nm_strv_to_ptrarray()
- _nm_utils_strv_to_slist() -> nm_strv_to_gslist()
- nm_utils_strv_cmp_n() -> nm_strv_cmp_n()
- nm_utils_strv_dup() -> nm_strv_dup()
- nm_utils_strv_dup_packed() -> nm_strv_dup_packed()
- nm_utils_strv_dup_shallow_maybe_a() -> nm_strv_dup_shallow_maybe_a()
- nm_utils_strv_equal() -> nm_strv_equal()
- nm_utils_strv_find_binary_search() -> nm_strv_find_binary_search()
- nm_utils_strv_find_first() -> nm_strv_find_first()
- nm_utils_strv_make_deep_copied() -> nm_strv_make_deep_copied()
- nm_utils_strv_make_deep_copied_n() -> nm_strv_make_deep_copied_n()
- nm_utils_strv_make_deep_copied_nonnull() -> nm_strv_make_deep_copied_nonnull()
- nm_utils_strv_sort() -> nm_strv_sort()
Note that no names are swapped and none of the new names existed
previously. That means, all the new names are really new, which
simplifies to find errors due to this larger refactoring. E.g. if
you backport a patch from after this change to an old branch, you'll
get a compiler error and notice that something is missing.
A MAC address is a relatively common "type". The GObject property is of type string,
but the D-Bus type is a bytestring ("ay"). We will need a special NMSettInfoPropertType.
Note that like most implementations, the from-dbus implementation still is based
on GObject setters. This will change in the future.
Also note that the previous compare function was
_nm_setting_property_compare_fcn_default(). That is, it used to convert
the property to GVariant and compare those. The conversion to GVariant
in that case normalizes the string (e.g. it is case insensitive). Also,
only properties could be compared which were also convertible to D-Bus
(which is probably fine, because there is no guarantee the profiles that
don't verify can be compared).
The code now uses the direct comparison of the strings. That mostly
preserves the case-insensitivity of the previous comparison, because
the property setters for mac addresses all use
_nm_utils_hwaddr_canonical_or_invalid() to normalize the strings.
This is subtle, but still correct. Note that this will improve later,
by ensuring that the property setters for mac addresses automatically
perform the right normalization.
The aim is that properties have a "type", that is, that similar
properties share a common behavior and appearance.
Most properties of type "mac-address" normalize the string in the
GObject property setter. Three don't. Let them also do that.
This is also relevant, because the compare function for mac-addresses
(_nm_setting_property_compare_fcn_default()) converts the properties
first to a "ay" GVariant. Which means the comparison is case
insensitive. Normalizing the values in the setter avoids that
inconsistency.
So far, we only have NMSettingClass.compare_property() hook.
The ugliness is that this hook is per-setting, when basically
all implementations only compare one property.
It feels cleaner to have a per-property hook and call that consistently.
In step one, we give all properties (the same) compare_fcn() implementation,
which delegates to the existing NMSettingClass.compare_property().
In a second step, this will be untangled.
There is one problem with this approach: NMSettInfoPropertType grows by
one pointer size, and we have potentially many such types. That should
be addressed by unifying types in the future.
Various NMSetting API would accept a property_idx parameter. Together
with the NMSettInfoSetting instance, this was useful to find the actual
NMSettInfoProperty instance.
The idea was, to provide the most of the functionality. That is, if you
might need the property_idx too, you had it -- after all, the
property_info you could lookup yourself.
However,
- literally zero users care about the property_idx. The care about
the property_info.
- if the user really, really required the property_idx, then it
is a given that it can be easily computed by
(property_info - sett_info->property_infos)
NMSetting instances either have no private data, they use
g_type_add_class_private(), or they embed the private data in the
NMSetting struct.
In all cases, we can find the private data at a fixed offset. Track that
offset in the NMSettInfoSetting meta data.
This will be useful, because properties really are stored in simple
fields, like a boolean property can be stored in a "bool" field. We will
extend the property meta data to track the offset of this property
field, but we also need to know where the offset starts.
"wifi.seen-bssid" is an unusual property, therefore very ugly due to the
inconsistency.
It is not a regular user configuration that makes sense to store to
disk or modify by the user. It gets populated by the daemon, and
stored in "/var/lib/NetworkManager/seen-bssids" file.
As such, how to convert this to/from D-Bus needs special handling.
This means, that the to/from D-Bus functions will only serialize the
property when the seen-bssids are specified via
NMConnectionSerializationOptions, which is what the daemon does.
Also, the daemon ignores seen-bssids when parsing the variant.
This has the odd effect that when the client converts a setting to
GVariant, the seen-bssids gets lost. That means, a conversion to GVariant
and back looses information. I think that is OK in this case, because the
main point of to/from D-Bus is not to have a lossless GVariant representation
of a setting, but to transfer the setting via D-Bus between client and
daemon. And transferring seen-bssids via D-Bus makes only sense from the daemon
to the client.
When subclassing a GObject type, the class and object structs
must be available and defined in the header.
For libnm, and in particular for NMSetting classes, we don't want
users to subclass NMSetting. It also doesn't work, because libnm
has internal code that is necessary to hook up the NMSetting class.
You cannot define your own type and make it work together with
libnm.
Having the structs in public headers limits what we can do with them.
For example, we could embed the private data directly in the structures
and avoid the additional indirection.
This is an API break, but for something that most likely nobody cares
about. Or better, nobody should care about. API is not what is
accidentally defined in a header, API was the library provides to
meaningfully use. Subclassing these types is not meaningful and was
only accidentally possible so far.
Only hide the structs for now. More cleanup is possible later. We shall
however aim to keep the padding and struct layout to not also break ABI.
(cherry picked from commit e46d484fae)
nm-settings-connection.c has code similar to this in two places:
/* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
* in particular if there are no secrets to begin with. */
connection_cloned = nm_simple_connection_new_clone(new);
/* Clear out unwanted secrets */
_nm_connection_clear_secrets_by_secret_flags(connection_cloned,
NM_SETTING_SECRET_FLAG_NOT_SAVED
| NM_SETTING_SECRET_FLAG_AGENT_OWNED);
secrets = nm_g_variant_ref_sink(
nm_connection_to_dbus(connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
It seems the secrets filtering can be done by nm_connection_to_dbus() if
the NM_CONNECTION_SERIALIZE_* flags are extended. The current set of
flags contains flags that start with NO, ONLY and WITH prefixes, which
makes it useless for combining the flags because most combinations of
more than one flag don't have a clear interpretation. So they're mostly
useful when used alone, i.e. you'd need to add a new enum value for
each new subset of settings to be serialized.
To get the most flexibility from a small set of flags they should
either all be of the WITH_* type or NO_* type. In the former case they
could be combined to extend the subset of properties serialized, in the
latter case each flag would reduce the subset. After trying both
options I found it's easier to adapt the current set of flags to the
WITH_* schema while keeping binary and source compatibility. This
commit changes the set of flags in the following way:
NM_CONNECTION_SERIALIZE_ALL is kept for compatibility but is equivalent
to a combination of other flags.
NM_CONNECTION_SERIALIZE_WITH_NON_SECRET is added with the same value as
NM_CONNECTION_SERIALIZE_NO_SECRETS, it implies that non-secret
properties are included but doesn't prevent including other properties.
Since it couldn't be meaningfully combined with any other flag this
change shouldn't break compatibility.
Similarly NM_CONNECTION_SERIALIZE_WITH_SECRETS is added with the same
value as existing NM_CONNECTION_SERIALIZE_ONLY_SECRETS with the same
consideration about compatibility.
NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED and the new
NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED and
NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED add only subsets of
secrets and can be combined. For backwards compatibility
NM_CONNECTION_SERIALIZE_ONLY_SECRETS is basically ignored when either of
these three is present, so that the value:
..ONLY_SECRETS | ..AGENT_OWNED works as previously.
"libnm-core/" is rather complicated. It provides a static library that
is linked into libnm.so and NetworkManager. It also contains public
headers (like "nm-setting.h") which are part of public libnm API.
Then we have helper libraries ("libnm-core/nm-libnm-core-*/") which
only rely on public API of libnm-core, but are themself static
libraries that can be used by anybody who uses libnm-core. And
"libnm-core/nm-libnm-core-intern" is used by libnm-core itself.
Move "libnm-core/" to "src/". But also split it in different
directories so that they have a clearer purpose.
The goal is to have a flat directory hierarchy. The "src/libnm-core*/"
directories correspond to the different modules (static libraries and set
of headers that we have). We have different kinds of such modules because
of how we combine various code together. The directory layout now reflects
this.
2021-02-18 19:46:51 +01:00
Renamed from libnm-core/nm-setting-wireless.c (Browse further)