I think NM_CACHED_QUARK_FCN() is better because:
- the implementation is in our hand, meaning it is clear that
putting a "static" before NM_CACHED_QUARK_FCN() is guaranteed to
work -- without relying on G_DEFINE_QUARK() to be defined in a way
that this works (in fact, we currently never do that and instead
make all functions non-static).
- it does not construct function names by appending "_quark".
Thus you can grep for the entire function name and finding
the place where it is implemented.
- same with the stings, where the new macro doesn't stringify the
argument, which is less surpising. Again, now you can grep
for the string including the double quoting.
(yes, I really use grep to understand the source-code)
Only use non-negative index values for the D-Bus path. This is purely
cosmetical, as the actual path value should be treated as opaque. Still,
avoid using 0 and start counting at 1.
An overflow of the 32 bit guint is possible and rather ugly
because the D-Bus path should be unique and not repeat.
Avoid that by extending the counter to 64 bit.
For new interfaces we don't define a PropertiesChanged signal as we
rely on GDBus to generate one on the standard
org.freedesktop.DBus.Properties interface.
This makes it easier to install the files with proper names.
Also, it makes the makefile rules slightly simpler.
Lastly, the documentation is now generated into docs/api, which makes it
possible to get rid of the awkward relative file names in docbook.
Keep the include paths clean and separate. We use directories to group source
files together. That makes sense (I guess), but then we should use this
grouping also when including files. Thus require to #include files with their
path relative to "src/".
Also, we build various artifacts from the "src/" tree. Instead of having
individual CFLAGS for each artifact in Makefile.am, the CFLAGS should be
unified. Previously, the CFLAGS for each artifact differ and are inconsistent
in which paths they add to the search path. Fix the inconsistency by just
don't add the paths at all.
- use _NM_GET_PRIVATE() and _NM_GET_PRIVATE_PTR() everywhere.
- reorder statements, to have GObject related functions (init, dispose,
constructed) at the bottom of each file and in a consistent order w.r.t.
each other.
- unify whitespaces in signal and properties declarations.
- use NM_GOBJECT_PROPERTIES_DEFINE() and _notify()
- drop unused signal slots in class structures
- drop unused header files for device factories
Before switching to gdbus (before 1.2.0), NetworkManager used dbus-glib.
Most objects in the D-Bus API with properties had a signal
NetworkManager-specific "PropertiesChanged" signal. Nowadays, this way of
handling of property changes is deprecated for the common "PropertiesChanged"
signal on the "org.freedesktop.DBus.Properties" interface.
There were a few pecularities in 1.0.0 and earlier:
(1) Due to the implementation with dbus-glib, a property-changed
signal was emitted on *all* interfaces. For example:
- a change on a NMDeviceVeth of "NMDeviceEthernet.HwAddress" would be
emitted both for the interfaces "fdo.NM.Device.Ethernet" and
"fdo.NM.Device.Veth". Note that NMDeviceVeth is derived from
NMDeviceEthernet and there is no "HwAddress" on veth device.
- a change of "NMVpnConnection.VpnState" was emitted on both
interfaces "fdo.NM.VPN.Connection" and "fdo.NM.Connecion.Active".
Note that NMActiveConnection is the parent type of NMVpnConnection and
only the latter has a property "VpnState".
(2) NMDevice's "fdo.NM.Device" interface doesn't have a "PropertiesChanged"
signal. From (1) follows that all property-changes for this type were instead
invoked with an interface like "fdo.NM.Device.Ethernet" (or multiple
interfaces in case of NMDeviceVeth).
1.2.0 introduced gdbus, which gives us the standard "fdo.DBus.Properties"
signal. However, it made the mistake of not realizing (1), thus instead
of emitting the signal once for each interface, it would pick the first
one in the inheritance tree.
With 1.4.0, a bug from merge commit 844345e caused signals for devices
to be only emitted for the interface "fdo.NM.Device.Statistics", instead
of "fdo.NM.Device.Ethernet" or "fdo.NM.Device.Veth" (or both).
The latter is what bgo#770629 is about and what commit 82e9439 tried to fix.
However, the fix was wrong because it tried to do the theoretically correct
thing of emitting the property-changed signal exactly once for the
interface that actually ontains the property. In addition, it missed that
NMDevice doesn't have a PropertiesChanged signal, which caused signals for
"fdo.NM.Device" to get lost *sigh*.
Now, restore the (broken) behavior of 1.0.0. These old-style property changed
signals are anyway considered deprecated and exist solely to satisfy old clients
and preserve the old API.
Fixes: 63fbfad3705db5901e6a2a6a2fc332da0f0ae4be
https://bugzilla.gnome.org/show_bug.cgi?id=770629https://bugzilla.redhat.com/show_bug.cgi?id=1371920
nm_exported_object_notify() hooks GObject's property-change signal
and searches for the D-Bus interface to which to send the
PropertiesChanged signal.
Then it would enqueue the value encoded as GVariant in pending_notifications.
However, thereby the association between the property that changed and the
interface was lost. So later in idle_emit_properties_changed() it would
just pick the first interface with a properties-changed-id.
That is wrong. pending_notifications must be associated with the D-Bus
interface that we are going to notify. That is, each InterfaceData must
have its own separate list.
This is broken since introducing NMExportedObject and moving to gdbus.
Only now it was discovered as NMDevice itself has two D-Bus interfaces:
"Device" and "Device.Statistics".
Note that the order of the PropertiesChanged in our D-Bus API is not defined
so that later signals can reach the receiver before earlier signals.
Also, multiple change signals for one property may be combined.
That is not changed by this patch and is not considered a bug, but something
that our D-Bus API wrt. PropertiesChanged does not guarantee.
https://bugzilla.gnome.org/show_bug.cgi?id=770629
Instead of looking up the signal-id every time, cache it.
g_signal_lookup() requires a g_quark_try_string() and a
lock a lock on a global mutex.
Downside is that the InterfaceData structure grows.
NMExportedObject is the center of every D-Bus exported object in
NetworkManager's core. It makes sense to optimize it.
Transform the GSList of interfaces to be a array. The array is still
allocated via the slice allocator (as we expect that there are only few
types in the list). This saves the overhead to allocate a GSList item
for each entry.
Another advantage is that the interfaces list is now strongly typed
instead of an opaque data pointer.
GVariantBuilder doesn't care if you give it multiple dict entries with
the same key. But that causes duplicate dict entries in the legacy
D-Bus PropertiesChanged signals that NM emits. So instead go back to
a hash table to ensure that any previous value is dropped in favor of
the new one.
- All internal source files (except "examples", which are not internal)
should include "config.h" first. As also all internal source
files should include "nm-default.h", let "config.h" be included
by "nm-default.h" and include "nm-default.h" as first in every
source file.
We already wanted to include "nm-default.h" before other headers
because it might contains some fixes (like "nm-glib.h" compatibility)
that is required first.
- After including "nm-default.h", we optinally allow for including the
corresponding header file for the source file at hand. The idea
is to ensure that each header file is self contained.
- Don't include "config.h" or "nm-default.h" in any header file
(except "nm-sd-adapt.h"). Public headers anyway must not include
these headers, and internal headers are never included after
"nm-default.h", as of the first previous point.
- Include all internal headers with quotes instead of angle brackets.
In practice it doesn't matter, because in our public headers we must
include other headers with angle brackets. As we use our public
headers also to compile our interal source files, effectively the
result must be the same. Still do it for consistency.
- Except for <config.h> itself. Include it with angle brackets as suggested by
https://www.gnu.org/software/autoconf/manual/autoconf.html#Configuration-Headers
When exporting an object, we first set the object path and then create
GDBus interface skeletons. While doing this, a signal can be generated
[1] and then nm_exported_object_signal_hook() can trigger the failed
assertion "interface != NULL" because the object is already exported
(priv->path != NULL) but the interface has not been registered yet.
To fix this, set the object path only after skeletons have been
created.
[1] This happens here every time I disable networking and restart NM:
#0 _g_log_abort (libglib-2.0.so.0)
#1 g_log (libglib-2.0.so.0)
#2 nm_exported_object_signal_hook (NetworkManager)
#3 signal_emit_unlocked_R (libgobject-2.0.so.0)
#4 g_signal_emit_valist (libgobject-2.0.so.0)
#5 g_signal_emit (libgobject-2.0.so.0)
#6 set_state (NetworkManager)
#7 nm_manager_update_state (NetworkManager)
#8 get_property (NetworkManager)
#9 object_get_property (libgobject-2.0.so.0)
#10 on_source_notify (libgobject-2.0.so.0)
#11 g_object_bind_property_full (libgobject-2.0.so.0)
#12 g_object_bind_property (libgobject-2.0.so.0)
#13 nm_exported_object_skeleton_create (NetworkManager)
#14 nm_exported_object_create_skeletons (NetworkManager)
#15 nm_exported_object_export (NetworkManager)
#16 nm_manager_setup (NetworkManager)
#17 main (NetworkManager)
#18 __libc_start_main (libc.so.6)
#19 _start (NetworkManager)
https://mail.gnome.org/archives/networkmanager-list/2016-February/msg00041.html
NMExportedObject now derives from GDBusObjectSkeleton, which is what
GDBusObjectManagerServer wants. The main GDBusConnection and each
private server connection now gets a new GDBusObjectManagerServer,
and exported objects are registered with that instead of individually
exporting each GDBusInterfaceSkeleton.
Previously exported objects were not referenced by the BusManager,
but instead removed from the exports hash via weak references. The
GDBusObjectManagerServer instead references exported objects, which
can make them live much longer than they did before.
Co-Authored-By: Thomas Haller <thaller@redhat.com>
Previously most objects were implicitly unexported when they were
destroyed, but since refcounts may make the object live longer than
intended, we should explicitly unexport them when they should no
longer be present on the bus.
This means we can assume that objects will always be un-exported
already when they are destroyed, *except* when quitting where most
objects will live until exit because NM leaves interfaces up and
running on quit.
For future use of ObjectManager, we must explicitly unexport
the AP and no longer depend on having it unexported during
deconstruction (because object manager keeps the instance alive).
Also refactor adding/removal of APs and move the export/unexport
calls to the place where we emit the signal.
Exporting the object already in the *_init() function will later
break because the object is not yet fully initialized at that point.
Add a convenient flag so that the NMExportedObject parent implementation
automatically can export itself. This saves the derived class from
overwriting the constructed() method.
Also add an assertion to catch such bugs.
While an NMExportedObject is exported (i.e. registered at NMBusManager),
it has a list of GDBusInterfaceSkeleton interfaces. The properties of
the nm-object are bound to the interfaces and the signals connected.
Previously, when unexporting the NMExportedObject, we would only unref
the interfaces, but not explicitly disconnect. As there is no guarantee
that the lifetime of the interfaces is shorter then the lifetime of the
nm-object, hence, explicitly disconnect.
- Also if the target object is the NMManager instance itself,
re-fetch the manager via nm_bus_manager_get_registered_object().
This way, we only set the property on the manager, if
it's also exported according to the bus-manager. Also,
we don't treat the manager instance special.
- Move fetching the object (nm_bus_manager_get_registered_object())
from do_set_property_check() to prop_set_auth_done_cb(). Otherwise,
we fetch the object first, but there is no guarantee that the object
is still exported after the asynchronous authentication succeeds.
Previously, nm_bus_manager_register_object() would take various D-Bus
skeleton objects that were associated with one NMExportedObject.
This was confusing, in that these skeleton objects are all for the
same NMObject but correspond to different D-Bus interfaces.
Also, setting the D-Bus property "Managed" of a Device is broken
because we might retrieve the wrong skeleton.
Now, the NMBusManager has a reference to the exported object directly.
The skeleton interface instances instead are now exposed by the NMExportedObject.
When unexporting an object, we might have a notification
pending. When the notification is finally processed, it
would find no "priv->interfaces" and raise an assertion
"idle_emit_properties_changed: assertion 'signal_id != 0' failed"
https://bugzilla.redhat.com/show_bug.cgi?id=1253326
Fixes: 073991f5a8
Port NMExportedObject to gdbus, and make
nm_exported_object_class_add_interface() deal with generating D-Bus
skeleton objects and attaching signal handlers and property bindings
as needed to properly handle methods, signals, and properties.
Our gdbus generated types use the same names as their corresponding
"real" types, but with "NM" changed to "NMDBus".
Unfortunately, that means that introspection/nmdbus-manager.c (the
generated type for src/nm-manager.c) uses the same type name as the
entirely unrelated src/nm-dbus-manager.c.
Fix this by removing the "d" from src/nm-dbus-manager.c. (We could
rename the generated type instead, but then it becomes inconsistent
with all the other generated types, and we're already using it as
"NMDBusManager" in libnm/nm-manager.c.)
Move D-Bus export/unexport handling into NMExportedObject and remove
type-specific export/get_path methods (export paths are now specified
at the class level, and NMExportedObject handles the counters for all
exported types automatically).
Since all exportable objects now use the same get_path() method, we
can also add some helper methods to simplify get_property()
implementations for object-path and object-path-array properties.
Add NMExportedObject, make it the base class of all D-Bus-exported
types, and move the nm-properties-changed-signal logic into it. (Also,
make NMSettings use the same properties-changed code as everything
else, which it was not previously doing, presumably for historical
reasons).
(This is mostly just shuffling code around at this point, but
NMExportedObject will be more important in the gdbus port, since
gdbus-codegen doesn't do a very good job of supporting objects that
export multiple interfaces [as each NMDevice subclass does, for
example], so we will need more glue/helper code in NMExportedObject
then.)